Pull to refresh

Zend Framework: XSL и самостоятельная сериализация Views

Reading time 6 min
Views 3.5K
Перевод статьи Zend Framework: XSL and self-serializing Views
Автор: Pascal Opitz

Я давно утверждал, что фраемворки MVC должны использовать стили XSL вместо встроенного PHP кода и прочего. Вот почему я постучал вместе немного доказательство концепции Zend Framework, где представления файлов в виде XSL шаблона, а представление сериализует себя в XML для рендеринга.


Базовая структура MVC


Я только что создал демо-макет, используя стандартную структуру MVC из Zend_Controller:

|-application
|---default
|-----controllers
|-----models
|-----views
|-------filters
|-------helpers
|-------scripts
|---------index
|---------test
|-library
|---demo
|---zendframework_1.6.2
|-webroot


Конечно, теперь нам нужен загрузочный файл:

set_include_path('.'
 . PATH_SEPARATOR . '../library/zendframework_1.6.2/'
 . PATH_SEPARATOR . '../library/demo/'
 . PATH_SEPARATOR . '../application/default/controllers'
 . PATH_SEPARATOR . get_include_path());

require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass('Zend_Controller_Action_Helper_ViewRenderer');

$frontController = Zend_Controller_Front::getInstance();
$frontController->setControllerDirectory(array(
 'default' => '../application/default/controllers',
));

require_once 'View_Xslt.php';
$view = new View_Xslt;
$options = array();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view, $options);
$viewRenderer->setViewSuffix('xsl');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

$frontController->dispatch();


Обратите внимание, что я представил новый viewRenderer и вид объекта, который вызывается из View_Xslt.php и находится в папке library/demo. Также я установил суффикс представления для XSL.

ZIP файл, содержащий все демо (без учета Zend Framework файлов) можно скачать здесь.

Представления (VIEW)



View объект должен быть получен из класса, который расширяет Zend_View_Abstract. Рендинг View происходит в методе _run, и файл представления будет передан в качестве первого аргумента. Однако, этот аргумент должен быть доступен с func_get_arg, в противном случае мы сталкиваемся с аккуратным сообщением об ошибке, что наше заявление несовместимо с Zend_View_Abstract.

Для того, чтобы мой View объект само-сериализации позже, я также добавил Serializer в магическом методе конструктора, плюс я добавил приватной функции, которая сериализует представления в XML с использованием Serializer, который мы только что создали.

require_once('Serializer.php');

class View_Xslt extends Zend_View_Abstract
{
 private $serializer;
 private $rootName;
 
 public function __construct($data = array()) {
 $this->serializer = new Serializer();
 parent::__construct($data);
 }

 public function setRootName($name) {
 $this->rootName = $name;
 }
 
 protected function _run() {
 $template = func_get_arg(0); 
 $xslDoc = new DOMDocument();
 $xslDoc->load($template);
 $xmlDoc = $this->toXml();
 $proc = new XSLTProcessor();
 $proc->importStylesheet($xslDoc);
 echo $proc->transformToXML($xmlDoc);
 }
 
 private function toXml() {
 $xml_str = $this->serializer->Serialize($this, $this->rootName);
 return $xml_str;
 }
}


Сериализатор (Serializer)


Так что же это сериализатор делает? Он использует Отражение(рефлексию) функциональности для сериализации объектов в XML строку. Это дает нам возможность использовать нормальные переменные для просмотра с помощью наших контроллеров действий(controller actions), просто говоря $this->foo = 'bар'.

Я делал быстрый пост XML-Сериализации и раньше, и Сериализатор я представил вдохновлен, что я нашел там. Предостережение: Имейте в виду, что это всего лишь доказательство концепции, и чтобы получить лучшие результаты, вероятно, требуется немного больше работы.

class Serializer
{
 private $xmlDoc;
 
 public function __construct() {
 $this->xmlDoc = new DOMDocument();
 }
 
 public function Serialize($inst, $nodeName=null) {
 if(is_object($inst))
 {
  $nodeName = ($nodeName == null) ? get_class($inst) : $nodeName;
  $root = $this->xmlDoc->createElement($nodeName);
  $this->xmlDoc->appendChild($root);
  $this->SerializeObject($inst, $nodeName, $root);
 } else if(is_array($inst)) {
  $nodeName = ($nodeName == null) ? get_class($inst) : $nodeName;
  $root = $this->xmlDoc->createElement($nodeName);
  $this->xmlDoc->appendChild($root);
  $this->SerializeArray($inst, $nodeName, $root);
 }

 return $this->xmlDoc;
 }
 
 private function SerializeObject($inst, $nodeName, $parent) {
 $obj = new ReflectionObject($inst);
 $properties = $obj->getProperties();
 
 foreach($properties as $prop) {
  if(!$prop->isPrivate()) {
  $elem = $this->SerializeData($prop->getName(), $prop->getValue($inst), $parent);
  }
 }
 }
 
 private function SerializeArray($array, $nodeName, $parent) {
 foreach($array as $key => $val) {
  $keyStr = (is_numeric($key)) ? 'ArrayValue' : $key;
  $elem = $this->SerializeData($keyStr, $val, $parent);
  
  if(is_numeric($key)) {
  $elem->setAttribute('index', $key);
  }
 }
 }
 
 private function SerializeData($key, $val, $parent) {
 if(is_object($val)) {
  $propNodeName = get_class($val);
  $elem = $this->xmlDoc->createElement($propNodeName);
  $parent->appendChild($elem);   
  $this->SerializeObject($val, $propNodeName, $parent);
  $elem->setAttribute('type', 'object');
 } else if(is_array($val)) {
  $elem = $this->xmlDoc->createElement($key);
  $parent->appendChild($elem);
  $this->SerializeArray($val, $key, $elem);
  $elem->setAttribute('type', 'array');
 } else {
  $elem = $this->xmlDoc->createElement($key, $val);
  $parent->appendChild($elem);
  $elem->setAttribute('type', 'property');
 }
 
 return $elem;
 }
}


Файлы Контролера и Представления



Почти все. Нам просто нужно несколько файлов XSL и контроллер с действиями, чтобы получить работающее демо. Первый контроллер и действие. Я включил маленький демо класс, чтоб мы смогли увидеть сериализатор в действии:

class IndexController extends Zend_Controller_Action {
 public function indexAction() {
  $this->view->setRootName('DataObject');

  $this->view->foo = 'bar';
  $this->view->super = array(
  'here' => 'there', 'foo' => array(1,2,'test'),
  );
  $this->view->testObject = new DemoObject();
  $this->view->testObject->var = 'testObjectVar';
 }
}

class DemoObject {}


Файл(ы) View. Мы могли бы создать только один, но потому, что я хотел подерживать Zend_Layout, я не использовал xsl: import для того, чтобы сделать нечто подобное.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:import href="../layout.xsl"/>

 <xsl:template match="DataObject">
 <xsl:apply-templates select="*" />
 </xsl:template>
 
 <xsl:template match="*">
 <div>
  <h2><xsl:value-of select="name()" /></h2>
  <xsl:apply-templates select="text()" />
  <xsl:apply-templates select="*" />
 </div> 
 </xsl:template>
</xsl:stylesheet>


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" encoding="ISO-8859-1" omit-xml-declaration="no" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" indent="yes" />
 <xsl:template match="/">
 <html>
  <head>
  <title>Test</title>
  </head>
  
  <body>
  <xsl:apply-templates select="/*" />
  </body>
 </html>
 </xsl:template>
</xsl:stylesheet>


Результат


И вот оно что! Полученная страница индекса должна дать вам на выходе что-то вроде этого:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
 <title>Test</title>
 </head>
 <body>
 <div><h2>foo</h2>bar</div>

 <div>
 <h2>super</h2>
 <div><h2>here</h2>there</div>
 <div>
 <h2>foo</h2>
 <div><h2>ArrayValue</h2>1</div>

 <div><h2>ArrayValue</h2>2</div>
 <div><h2>ArrayValue</h2>test</div>
 </div>
 </div>
 <div>
 <h2>DemoObject</h2>

 </div>
 <div><h2>var</h2>testObjectVar</div>
 </body>
</html>





Так получилось, что я долго работал с XML и мне для работы захотелось использовать шаблонизатор из XSLT в ZendFramework, и это единственная статья, которую мне удалось найти, которая позволяет реализовать данное желание.

В результате у меня получилась простая система, где движок и набор стандартных шаблонов работали для небольших сайтов визиток, а контент от них хранился в файлах xml папке data. И вся миграция с хостинга на хостинг происходила простым копированием, без головной боли с БД. А опубликованная папка содержала только CSS, JavaScript и картинки.
Tags:
Hubs:
0
Comments 2
Comments Comments 2

Articles