How to render a custom tag for XML Text using your Symfony services and templates

One of my favorite features of eZ Publish is the ability to create nice-looking content with various nicely formatted blocks. That makes reading more interesting for the user. Due to storing content as XML it is possible to present information however you want. Along with a big number of standard tags like paragraph, image or table you are free to create your own custom tags: an embedded YouTube video, Google Maps, a sllideshare presentation, a source code block with syntax highlighting, QR-code etc. This article will show you how to use Symfony services to render custom tags.

eZ Publish 5 uses XSL stylesheets to render XML tags. On the one hand it is a very logical approach because XSLT is the a best ever known tool ever to transform XML data into HTML or something else. But on the from other hand XSL is not a sophisticated programming language. While it might be easy to implement YouTube embedded HTML code using XSL style sheet, generating, but a QR-code with XSL would probably be much less fun.

Moreover, sometimes we already have twig templates in a project and wish to reuse them. Luckily we have the possibilityan to render custom tags without XSL. There is the concept of pre-converter services instead.
Assume we’d like to display information about a webshop product in a custom tag.

First of all configure a XML tag name and its attributes. Unfortunately it still requires editing  of a legacy configuration file:
content.ini.append.php

[codesyntax lang="php"]
[product]
CustomAttributes[]=product_id
[/codesyntax]

Here “product” is a custom tag name and “product_id” is an product identifier which is used to fetch product data from the product catalog.

Then register a pre-converter service:

services.yml
[codesyntax lang=“php“]

demo.ezxml.pre_converter:
       class: Acme\DemoBundle\XmlText\ProductPreConverterService
       arguments: [@demo.product.provider, @templating]
       tags:
           -  { name: ezpublish.ezxml.converter }

[/codesyntax]

Here we have two injected services: product.provider to provide data about a webshop product and the templating engine to render it in HTML. The tag ezpublish.ezxml.converter defines a service as pre-converter.

Main method of a pre-converter that executes the job:

ProductPreConverterService.php

 

[codesyntax lang=“php“]

public function convert(DOMDocument $xmlDoc)
{
       $renderedHtml = '';
       $xpath = new DOMXpath($xmlDoc);
       /** @var \DOMNodeList $customTags */
       $customTags = $xpath->query(".//custom[@name='product']");
       /** @var \DOMElement $customTag */
       foreach ($customTags as $customTag) {
           $productId = $customTag->getAttribute('custom:product_id');
           $product = $this->productProvider->getProductData($productId);
           $html = $this->templatingEngine->render(
               'AcmeDemoBundle:CustomTag:product.html.twig',
               $product
           );
           $this->replaceCustomTagWithHtmlText($customTag, $html);
       }
}

[/codesyntax]

 

If the product provider is implemented as a controller, it is possible to do the same job even without the templating engine:

ProductPreConverterService.php

 

[codesyntax lang=“php“]

  public function convert(DOMDocument $xmlDoc)
   {
       $renderedHtml = '';
       $xpath = new DOMXpath($xmlDoc);
       /** @var \DOMNodeList $customTags */
       $customTags = $xpath->query(".//custom[@name='product']");
       /** @var \DOMElement $customTag */
       foreach ($customTags as $customTag) {
           $productId = $customTag->getAttribute('custom:product_id');
           $actionResponse = $this->productProvider->showProductAction($productId);
           $html = $actionResponse->getContent();
           $this->replaceCustomTagWithHtmlText($customTag, $html);
       }
   }

[/codesyntax]

 

The only trick here is the method replaceCustomTagWithHtmlText.
It may happen that a twig template renders non valid XHTML code. In this case our pre-converter will fail with generating the XML document. To solve this problem there is the following method that works equally good with HTML and XHTML:

ProductPreConverterService.php

[codesyntax lang=“php“]

  private function replaceCustomTagWithHtmlText(DOMElement $customTag, $htmlText)
   {
       $xmlDocument = $customTag->ownerDocument;
       $htmlDocument = new DOMDocument();
       // It is important to add xml declaration to import data in UTF-8
       $htmlDocument->loadHTML('' . $htmlText);
       $xpath = new DOMXpath($htmlDocument);
       $htmlBody = $xpath->query('/html/body')->item(0);
       $importedNode = $xmlDocument->importNode($htmlBody, true);
       $fragment = $xmlDocument->createDocumentFragment();
       while ($importedNode->childNodes->length > 0) {
           $fragment->appendChild($importedNode->childNodes->item(0));
       }
       $customTag->parentNode->replaceChild($fragment, $customTag);
   }

[/codesyntax]

 

In most cases using XSL style sheets for rendering custom tags is a good solution. But if your templates are complex and it is not possible or desirable to use XSL, you can use the approach described above.

https://blog.silversolutions.de/wp-content/uploads/2018/12/ez_dummy.pnghttps://blog.silversolutions.de/wp-content/uploads/2018/12/ez_dummy-150x150.pngFrank DegeB2B.technologieCMS,Symfony,Tipps & TricksHow to render a custom tag for XML Text using your Symfony services and templates One of my favorite features of eZ Publish is the ability to create nice-looking content with various nicely formatted blocks. That makes reading more interesting for the user. Due to storing content as XML it...Die e-Commerce B2B Experten bloggen über Shops, ERP Connectivity und das integrierte CMS eZ Publish