Rendering XML to a variable using CakePHP's XmlView class
Learnings
- Instantiating and using ViewBuilder
- You don't need
->set()
if you pass the data to->build()
- You can move XML creation into its own class to clean your controller action up and use it elsewhere in your code
- Identify what you want converted to XML using
->setOption('serialize'
// controller action
public function xml()
{
// this can be a $this->Items->find('all') call
// setting attributes
$items = [
['hi' => 'yes', '@attribute' => "attribute",],
['hi' => 'no']
];
// this example shows instantiating ViewBuilder
// you could use this to generate XML output elsewhere in your code
// not just inside a controller action
$vb = new ViewBuilder();
$vb->setClassName('Xml')->setOption('serialize', 'items');
$vb->setTemplate(false);
$view = $vb->build();
$view->set(compact('items'));
$xml = $view->render();
$data = ['data' => $xml, 'filename' => uniqid('hijames') . '.xml'];
$options = ['config' => 'default'];
QueueManager::push(SFTPSendJob::class, $data, $options);
// stops the action looking for a view template file
$this->autoRender = false;
// still output the XML to the browser but without a view template
return $this->response->withType('xml')->withStringBody($xml);
}
You don't have to create ViewBuilder from scratch you can just use $this->viewBuilder just capture the XmlView to a variable and then set data to it.
// in controller just use $this->viewBuilder()
$this->viewBuilder()->setClassName('Xml')
->setOption('serialize', 'items');
$view = $this->viewBuilder()->build();
$view->set(compact('items'));
$xml = $view->render();
Contents of $xml
<?xml version="1.0" encoding="UTF-8"?>
<response>
<items attribute="attribute">
<hi>yes</hi>
</items>
<items>
<hi>no</hi>
</items>
</response>
Another Example with Custom RootNode and attributes on RootNode
<?xml version="1.0" encoding="UTF-8"?>
<myItems myattribute="This is my attribute" filename="hijames62647c4e016d4.xml">
<items>
<hi>yes</hi>
<sub>
<hi>there</hi>
</sub>
</items>
<items attribute="attributeOnTheChildHereButAppearsOneLevelUp">
<hi>no</hi>
</items>
</myItems>
public function xml()
{
// Custom finder
// $items = $this->Items->find('validItems', ['productTypeId' => 1]);
// if passing to a beanstalk queue you have to limit the amount of data or reconfigure
// beanstalk to accept larger JSON payloads
// $items = $this->Items->find('all')->where(['active' => 1])->limit(1);
$filename = uniqid('hijames') . '.xml';
// build your own XML
$items = [
['hi' => 'yes', 'sub' => ['hi' => 'there']],
['hi' => 'no', '@attribute' => "attributeOnTheChildHereButAppearsOneLevelUp"]
];
$this->viewBuilder()
->setClassName('Xml')
->setOption('rootNode', 'myItems') // custom root node <hiJames></hiJames>
->setOption('serialize', ['@myattribute', 'items', '@filename']);
// Can i just build without set if I pass vars to build?
// Yes!!!
$view = $this->viewBuilder()->build([
'items' => $items,
'@myattribute' => "This is my attribute",
'@filename' => $filename
]);
$data = ['data' => $xml, 'filename' => $filename];
$options = ['config' => 'default'];
// this is the CakePHP/Queue plugin Pushing to beanstalk or redis queue
QueueManager::push(SFTPSendJob::class, $data, $options);
// don't try to pick up template in Items/xml
$this->autoRender = false;
// also render the xml to browser
return $this->response->withType('xml')->withStringBody($xml);
}
Moving XML Creation Out of Controller
<?php
namespace App\Lib\Utility;
use Cake\View\ViewBuilder;
class XMLCreator
{
public function create($data, $options = null)
{
// array_merge
// the same key appearing later will
// overwrite
$options = array_merge([
'rootNode' => 'response'
], $options);
// + syntax if it already exists
// + will NOT overwrite but will add if it
// doesn't exist
$options = $options + [
'rootNode' => 'response'
];
$vb = new ViewBuilder();
$vb->setClassName('Xml')
->setOption('rootNode', $options['rootNode'])
->setOption('serialize', $options['serialize']);
$view = $vb->build($data);
$xml = $view->render();
return $xml;
}
}
<?php
class ItemsController
{
public function xml()
{
// $items = $this->Items->find('validItems', ['productTypeId' => 1]);
// $items = $this->Items->find('all')->where(['active' => 1]);
$filename = uniqid('hijames') . '.xml';
$items = [
['hi' => 'yes', 'sub' => ['hi' => 'there']],
['hi' => 'no', '@attribute' => "attributeOnTheChildHereButAppearsOneLevelUp"]
];
$data = [
'items' => $items,
'@myattribute' => "This is my attribute",
'@filename' => $filename
];
$serialize = array_keys($data);
$xml = (new XMLCreator())->create($data, [
'rootNode' => 'rupert',
'serialize' => $serialize
]);
$this->autoRender = false;
return $this->response->withType('xml')->withStringBody($xml);
}
}
0 Comments