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

от автора

Перевод статьи 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 и картинки.

ссылка на оригинал статьи http://habrahabr.ru/post/166631/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *