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'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // 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.
1 2 3 4 5 6 7 8 9 | // 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
1 2 3 4 5 6 7 8 9 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 | <?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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?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 ; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?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