📜 ⬆️ ⬇️

Zend Framework: XSL and Self Serialization Views

Zend Framework article translation : XSL and self-serializing Views
Posted by: Pascal Opitz

I have long argued that MVC framelets should use XSL styles instead of embedded PHP code and so on. That's why I knocked together a bit of proof of the Zend Framework concept, where file views are in the form of an XSL template, and the view serializes itself to XML for rendering.


Basic MVC structure


I have just created a demo layout using the standard MVC structure from Zend_Controller:
')
| -application
| --- default
| ----- controllers
| ----- models
| ----- views
| ------- filters
| ------- helpers
| ------- scripts
| --------- index
| --------- test
| -library
| --- demo
| --- zendframework_1.6.2
| -webroot


Of course, now we need a boot file:

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(); 


Please note that I have introduced a new viewRenderer and the type of object that is called from View_Xslt.php and is located in the library / demo folder. I also set the view suffix for XSL.

A ZIP file containing all demos (excluding Zend Framework files) can be downloaded here .

Views (VIEW)



The view object must be obtained from a class that extends Zend_View_Abstract. The view is rendered in the _run method, and the view file will be passed as the first argument. However, this argument should be available with func_get_arg, otherwise we are faced with a neat error message that our statement is incompatible with Zend_View_Abstract.

In order for my View object to be self-serialized later, I also added a Serializer in the magic constructor method, plus I added a private function that serializes the views to XML using the Serializer we just created.

 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


So what does the serializer do? It uses Reflection ( reflection ) functionality to serialize objects into an XML string. This gives us the ability to use normal variables to view using our controller actions (controller actions), just saying $ this-> foo = 'bar'.

I was doing a quick post on XML Serialization before, and the Serializer I submitted was inspired by what I found there. Caution: Keep in mind that this is just proof of concept, and it probably takes a little more work to get better results.

 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; } } 


Controller and View Files



Almost all. We just need some XSL files and a controller with actions to get a working demo. The first controller and action. I turned on a small demo class so that we could see the serializer in action:

 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 {} 


File (s) View. We could create only one, but because I wanted to support Zend_Layout, I did not use xsl: import to do something similar.

 <?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> 


Result


And that's it! The resulting index page should give you something like this on output:

 <?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> 





It so happened that I worked with XML for a long time and I wanted to use the templating engine from XSLT to ZendFramework for work, and this is the only article I managed to find that allows you to realize this desire.

As a result, I got a simple system, where the engine and a set of standard templates worked for small business card sites, and the content from them was stored in the xml files in the data folder. And the whole migration from hosting to hosting was done by simple copying, without a headache from the database. And the published folder contained only CSS, JavaScript and images.

Source: https://habr.com/ru/post/166631/


All Articles