Browse Source

Make View use InstanceConfigTrait.

Custom options of ViewBuilder are now set as View config.
This allows removing use of special view vars to control rendering options.
ADmad 6 years ago
parent
commit
671df73f36

+ 4 - 3
src/Error/ExceptionRenderer.php

@@ -214,8 +214,8 @@ class ExceptionRenderer implements ExceptionRendererInterface
             'url' => h($url),
             'error' => $exception,
             'code' => $code,
-            '_serialize' => ['message', 'url', 'code'],
         ];
+        $serialize = ['message', 'url', 'code'];
 
         $isDebug = Configure::read('debug');
         if ($isDebug) {
@@ -225,10 +225,11 @@ class ExceptionRenderer implements ExceptionRendererInterface
             ]);
             $viewVars['file'] = $exception->getFile() ?: 'null';
             $viewVars['line'] = $exception->getLine() ?: 'null';
-            $viewVars['_serialize'][] = 'file';
-            $viewVars['_serialize'][] = 'line';
+            $serialize[] = 'file';
+            $serialize[] = 'line';
         }
         $this->controller->set($viewVars);
+        $this->controller->viewBuilder()->setOption('serialize', $serialize);
 
         if ($exception instanceof CakeException && $isDebug) {
             $this->controller->set($exception->getAttributes());

+ 38 - 56
src/View/JsonView.php

@@ -27,7 +27,7 @@ use Cake\Core\Configure;
  *
  * ```
  * $this->set(['posts' => $posts]);
- * $this->set('_serialize', true);
+ * $this->viewBuilder()->setOption('serialize', true);
  * ```
  *
  * When the view is rendered, the `$posts` view variable will be serialized
@@ -38,20 +38,20 @@ use Cake\Core\Configure;
  *
  * ```
  * $this->set(compact('posts', 'users', 'stuff'));
- * $this->set('_serialize', true);
+ * $this->viewBuilder()->setOption('serialize', true);
  * ```
  *
  * The above would generate a JSON object that looks like:
  *
  * `{"posts": [...], "users": [...]}`
  *
- * You can also set `'_serialize'` to a string or array to serialize only the
+ * You can also set `'serialize'` to a string or array to serialize only the
  * specified view variables.
  *
- * If you don't use the `_serialize`, you will need a view template. You can use
- * extended views to provide layout-like functionality.
+ * If you don't set the `serialize` opton, you will need a view template.
+ * You can use extended views to provide layout-like functionality.
  *
- * You can also enable JSONP support by setting parameter `_jsonp` to true or a
+ * You can also enable JSONP support by setting `jsonp` option to true or a
  * string to specify custom query string parameter name which will contain the
  * callback function name.
  */
@@ -79,26 +79,31 @@ class JsonView extends SerializedView
     protected $_responseType = 'json';
 
     /**
-     * List of special view vars.
+     * Default config options.
      *
-     * @var array
-     */
-    protected $_specialVars = ['_serialize', '_jsonOptions', '_jsonp'];
-
-    /**
-     * Render a JSON view.
+     * Use ViewBuilder::setOption()/setOptions() in your controller to set these options.
      *
-     * ### Special parameters
-     * `_serialize` To convert a set of view variables into a JSON response.
+     * - `serialize`: Option to convert a set of view variables into a serialized response.
      *   Its value can be a string for single variable name or array for multiple
-     *   names. If true all view variables will be serialized. If unset normal
-     *   view template will be rendered.
-     * `_jsonp` Enables JSONP support and wraps response in callback function
-     *   provided in query string.
+     *   names. If true all view variables will be serialized. If null or false
+     *   normal view template will be rendered.
+     * - `jsonOptions`: Options for json_encode(). For e.g. `JSON_HEX_TAG | JSON_HEX_APOS`.
+     * - `jsonp`: Enables JSONP support and wraps response in callback function provided in query string.
      *   - Setting it to true enables the default query string parameter "callback".
      *   - Setting it to a string value, uses the provided query string parameter
      *     for finding the JSONP callback name.
      *
+     * @var array{serialize:string|bool|null, jsonOptions: int|null, jsonp: bool|string|null}
+     */
+    protected $_defaultConfig = [
+        'serialize' => null,
+        'jsonOptions' => null,
+        'jsonp' => null,
+    ];
+
+    /**
+     * Render a JSON view.
+     *
      * @param string|null $template The template being rendered.
      * @param string|null|false $layout The layout being rendered.
      * @return string The rendered view.
@@ -107,13 +112,13 @@ class JsonView extends SerializedView
     {
         $return = parent::render($template, $layout);
 
-        if (!empty($this->viewVars['_jsonp'])) {
-            $jsonpParam = $this->viewVars['_jsonp'];
-            if ($this->viewVars['_jsonp'] === true) {
-                $jsonpParam = 'callback';
+        $jsonp = $this->getConfig('jsonp');
+        if ($jsonp) {
+            if ($jsonp === true) {
+                $jsonp = 'callback';
             }
-            if ($this->request->getQuery($jsonpParam)) {
-                $return = sprintf('%s(%s)', h($this->request->getQuery($jsonpParam)), $return);
+            if ($this->request->getQuery($jsonp)) {
+                $return = sprintf('%s(%s)', h($this->request->getQuery($jsonp)), $return);
                 $this->response = $this->response->withType('js');
             }
         }
@@ -124,27 +129,18 @@ class JsonView extends SerializedView
     /**
      * Serialize view vars
      *
-     * ### Special parameters
-     * `_jsonOptions` You can set custom options for json_encode() this way,
-     *   e.g. `JSON_HEX_TAG | JSON_HEX_APOS`.
-     *
-     * @param array|string|bool $serialize The name(s) of the view variable(s)
-     *   that need(s) to be serialized. If true all available view variables.
+     * @param array|string $serialize The name(s) of the view variable(s) that need(s) to be serialized.
      * @return string|false The serialized data, or boolean false if not serializable.
      */
     protected function _serialize($serialize)
     {
         $data = $this->_dataToSerialize($serialize);
 
-        $jsonOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT |
-            JSON_PARTIAL_OUTPUT_ON_ERROR;
-
-        if (isset($this->viewVars['_jsonOptions'])) {
-            if ($this->viewVars['_jsonOptions'] === false) {
-                $jsonOptions = 0;
-            } else {
-                $jsonOptions = $this->viewVars['_jsonOptions'];
-            }
+        $jsonOptions = $this->getConfig('jsonOptions');
+        if ($jsonOptions === null) {
+            $jsonOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_PARTIAL_OUTPUT_ON_ERROR;
+        } elseif ($jsonOptions === false) {
+            $jsonOptions = 0;
         }
 
         if (Configure::read('debug')) {
@@ -157,25 +153,11 @@ class JsonView extends SerializedView
     /**
      * Returns data to be serialized.
      *
-     * @param array|string|bool $serialize The name(s) of the view variable(s) that
-     *   need(s) to be serialized. If true all available view variables will be used.
+     * @param array|string $serialize The name(s) of the view variable(s) that need(s) to be serialized.
      * @return mixed The data to serialize.
      */
-    protected function _dataToSerialize($serialize = true)
+    protected function _dataToSerialize($serialize)
     {
-        if ($serialize === true) {
-            $data = array_diff_key(
-                $this->viewVars,
-                array_flip($this->_specialVars)
-            );
-
-            if (empty($data)) {
-                return null;
-            }
-
-            return $data;
-        }
-
         if (is_array($serialize)) {
             $data = [];
             foreach ($serialize as $alias => $key) {

+ 32 - 11
src/View/SerializedView.php

@@ -34,6 +34,22 @@ abstract class SerializedView extends View
     protected $_responseType;
 
     /**
+     * Default config options.
+     *
+     * Use ViewBuilder::setOption()/setOptions() in your controlle to set these options.
+     *
+     * - `serialize`: Option to convert a set of view variables into a serialized response.
+     *   Its value can be a string for single variable name or array for multiple
+     *   names. If true all view variables will be serialized. If null or false
+     *   normal view template will be rendered.
+     *
+     * @var array{serialize:string|bool|null}
+     */
+    protected $_defaultConfig = [
+        'serialize' => null,
+    ];
+
+    /**
      * Constructor
      *
      * @param \Cake\Http\ServerRequest|null $request Request instance.
@@ -50,6 +66,7 @@ abstract class SerializedView extends View
         if ($response) {
             $response = $response->withType($this->_responseType);
         }
+
         parent::__construct($request, $response, $eventManager, $viewOptions);
     }
 
@@ -60,7 +77,7 @@ abstract class SerializedView extends View
      */
     public function loadHelpers()
     {
-        if (empty($this->viewVars['_serialize'])) {
+        if (!$this->getConfig('serialize')) {
             parent::loadHelpers();
         }
 
@@ -79,23 +96,27 @@ abstract class SerializedView extends View
     /**
      * Render view template or return serialized data.
      *
-     * ### Special parameters
-     * `_serialize` To convert a set of view variables into a serialized form.
-     *   Its value can be a string for single variable name or array for multiple
-     *   names. If true all view variables will be serialized. If unset normal
-     *   view template will be rendered.
-     *
      * @param string|null $template The template being rendered.
      * @param string|null|false $layout The layout being rendered.
      * @return string The rendered view.
      */
     public function render(?string $template = null, $layout = null): string
     {
-        $serialize = false;
-        if (isset($this->viewVars['_serialize'])) {
-            $serialize = $this->viewVars['_serialize'];
-        }
+        $serialize = $this->getConfig('serialize', false);
+
+        if ($serialize === true) {
+            $options = array_map(
+                function ($v) {
+                    return '_' . $v;
+                },
+                array_keys($this->_defaultConfig)
+            );
 
+            $serialize = array_diff(
+                array_keys($this->viewVars),
+                $options
+            );
+        }
         if ($serialize !== false) {
             $result = $this->_serialize($serialize);
             if ($result === false) {

+ 51 - 1
src/View/View.php

@@ -18,6 +18,7 @@ namespace Cake\View;
 
 use Cake\Cache\Cache;
 use Cake\Core\App;
+use Cake\Core\InstanceConfigTrait;
 use Cake\Core\Plugin;
 use Cake\Event\EventDispatcherInterface;
 use Cake\Event\EventDispatcherTrait;
@@ -74,6 +75,9 @@ class View implements EventDispatcherInterface
         cell as public;
     }
     use EventDispatcherTrait;
+    use InstanceConfigTrait {
+        getConfig as private _getConfig;
+    }
     use LogTrait;
     use ViewVarsTrait;
 
@@ -217,6 +221,13 @@ class View implements EventDispatcherInterface
     ];
 
     /**
+     * Default custom config options.
+     *
+     * @var array
+     */
+    protected $_defaultConfig = [];
+
+    /**
      * Holds an array of paths.
      *
      * @var string[]
@@ -321,6 +332,11 @@ class View implements EventDispatcherInterface
                 $this->{$var} = $viewOptions[$var];
             }
         }
+        $this->setConfig(array_diff_key(
+            $viewOptions,
+            array_flip($this->_passedVars)
+        ));
+
         if ($eventManager !== null) {
             $this->setEventManager($eventManager);
         }
@@ -565,6 +581,38 @@ class View implements EventDispatcherInterface
     }
 
     /**
+     * Get config value.
+     *
+     * Currently if config is not set it fallbacks to checking corresponding
+     * view var with underscore prefix. Using underscore prefixed special view
+     * vars is deprecated and this fallback will be removed in CakePHP 4.1.0.
+     *
+     * @param string|null $key The key to get or null for the whole config.
+     * @param mixed $default The return value when the key does not exist.
+     * @return mixed Config value being read.
+     */
+    public function getConfig(?string $key = null, $default = null)
+    {
+        $value = $this->_getConfig($key);
+
+        if ($value !== null) {
+            return $value;
+        }
+
+        if (isset($this->viewVars["_{$key}"])) {
+            deprecationWarning(sprintf(
+                'Setting special view var "_%s" is deprecated. Use ViewBuilder::setOption(\'%s\', $value) instead.',
+                $key,
+                $key
+            ));
+
+            return $this->viewVars["_{$key}"];
+        }
+
+        return $default;
+    }
+
+    /**
      * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string.
      *
      * This realizes the concept of Elements, (or "partial layouts") and the $params array is used to send
@@ -799,7 +847,9 @@ class View implements EventDispatcherInterface
             if (is_array($value)) {
                 $data = array_combine($name, $value);
                 if ($data === false) {
-                    throw new RuntimeException('Invalid data provided for array_combine() to work: Both $name and $value require same count.');
+                    throw new RuntimeException(
+                        'Invalid data provided for array_combine() to work: Both $name and $value require same count.'
+                    );
                 }
             } else {
                 $data = $name;

+ 38 - 26
src/View/XmlView.php

@@ -23,15 +23,16 @@ use Cake\Utility\Xml;
 /**
  * A view class that is used for creating XML responses.
  *
- * By setting the '_serialize' key in your controller, you can specify a view variable
- * that should be serialized to XML and used as the response for the request.
+ * By setting the 'serialize' option in view builder of your controller, you can specify
+ * a view variable that should be serialized to XML and used as the response for the request.
  * This allows you to omit views + layouts, if your just need to emit a single view
  * variable as the XML response.
  *
  * In your controller, you could do the following:
  *
  * ```
- * $this->set(['posts' => $posts, '_serialize' => true]);
+ * $this->set(['posts' => $posts]);
+ * $this->viewBuilder()->setOption('serialize', true);
  * ```
  *
  * When the view is rendered, the `$posts` view variable will be serialized
@@ -39,22 +40,22 @@ use Cake\Utility\Xml;
  *
  * **Note** The view variable you specify must be compatible with Xml::fromArray().
  *
- * You can also define `'_serialize'` as an array. This will create an additional
+ * You can also set `'serialize'` as an array. This will create an additional
  * top level element named `<response>` containing all the named view variables:
  *
  * ```
  * $this->set(compact('posts', 'users', 'stuff'));
- * $this->set('_serialize', true);
+ * $this->viewBuilder()->setOption('serialize', true);
  * ```
  *
  * The above would generate a XML object that looks like:
  *
  * `<response><posts>...</posts><users>...</users></response>`
  *
- * You can also set `'_serialize'` to a string or array to serialize only the
+ * You can also set `'serialize'` to a string or array to serialize only the
  * specified view variables.
  *
- * If you don't use the `_serialize` key, you will need a view. You can use extended
+ * If you don't set the `serialize` option, you will need a view. You can use extended
  * views to provide layout like functionality.
  */
 class XmlView extends SerializedView
@@ -81,32 +82,46 @@ class XmlView extends SerializedView
     protected $_responseType = 'xml';
 
     /**
-     * List of special view vars.
+     * Option to allow setting an array of custom options for Xml::fromArray()
      *
-     * @var array
+     * For e.g. 'format' as 'attributes' instead of 'tags'.
+     *
+     * @var array|null
      */
-    protected $_specialVars = ['_serialize', '_rootNode', '_xmlOptions'];
+    protected $xmlOptions;
 
     /**
-     * Serialize view vars.
+     * Default config options.
+     *
+     * Use ViewBuilder::setOption()/setOptions() in your controller to set these options.
      *
-     * ### Special parameters
-     * `_xmlOptions` You can set an array of custom options for Xml::fromArray() this way, e.g.
-     *   'format' as 'attributes' instead of 'tags'.
+     * - `serialize`: Option to convert a set of view variables into a serialized response.
+     *   Its value can be a string for single variable name or array for multiple
+     *   names. If true all view variables will be serialized. If null or false
+     *   normal view template will be rendered.
+     * - `xmlOptions`: Option to allow setting an array of custom options for Xml::fromArray().
+     *   For e.g. 'format' as 'attributes' instead of 'tags'.
+     * - `rootNode`: Root node name. Defaults to "response".
      *
-     * @param array|string|true $serialize The name(s) of the view variable(s) that need(s) to be serialized
+     * @var array{serialize:string|bool|null, xmlOptions: int|null, rootNode: string|null}
+     */
+    protected $_defaultConfig = [
+        'serialize' => null,
+        'xmlOptions' => null,
+        'rootNode' => null,
+    ];
+
+    /**
+     * Serialize view vars.
+     *
+     * @param array|string $serialize The name(s) of the view variable(s) that need(s) to be serialized
      * @return string|false The serialized data
      */
     protected function _serialize($serialize)
     {
-        $rootNode = $this->viewVars['_rootNode'] ?? 'response';
-
-        if ($serialize === true) {
-            $serialize = array_diff(
-                array_keys($this->viewVars),
-                $this->_specialVars
-            );
+        $rootNode = $this->getConfig('rootNode', 'response');
 
+        if (is_array($serialize)) {
             if (empty($serialize)) {
                 $serialize = null;
             } elseif (count($serialize) === 1) {
@@ -131,10 +146,7 @@ class XmlView extends SerializedView
             }
         }
 
-        $options = [];
-        if (isset($this->viewVars['_xmlOptions'])) {
-            $options = $this->viewVars['_xmlOptions'];
-        }
+        $options = $this->getConfig('xmlOptions', []);
         if (Configure::read('debug')) {
             $options['pretty'] = true;
         }

+ 1 - 1
tests/TestCase/Controller/ControllerTest.php

@@ -275,8 +275,8 @@ class ControllerTest extends TestCase
 
         $Controller->set([
             'test' => 'value',
-            '_serialize' => ['test'],
         ]);
+        $Controller->viewBuilder()->setOption('serialize', ['test']);
         $debug = Configure::read('debug');
         Configure::write('debug', false);
         $result = $Controller->render('index');

+ 3 - 0
tests/TestCase/Error/DebuggerTest.php

@@ -348,6 +348,7 @@ object(Cake\View\View) {
 		(int) 8 => 'templatePath',
 		(int) 9 => 'plugin'
 	]
+	[protected] _defaultConfig => []
 	[protected] _paths => []
 	[protected] _pathsForPlugin => []
 	[protected] _parents => []
@@ -357,6 +358,8 @@ object(Cake\View\View) {
 	[protected] _viewBlockClass => 'Cake\View\ViewBlock'
 	[protected] _eventManager => object(Cake\Event\EventManager) {}
 	[protected] _eventClass => 'Cake\Event\Event'
+	[protected] _config => []
+	[protected] _configInitialized => true
 	[protected] _viewBuilder => null
 }
 TEXT;

+ 25 - 10
tests/TestCase/View/JsonViewTest.php

@@ -223,10 +223,23 @@ class JsonViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
 
+        $this->deprecated(function () use ($Controller, $data, $serialize, $jsonOptions, $expected) {
+            $Controller->set($data);
+            $Controller->set('_serialize', $serialize);
+            $Controller->set('_jsonOptions', $jsonOptions);
+            $Controller->viewBuilder()->setClassName('Json');
+            $View = $Controller->createView();
+            $output = $View->render();
+
+            $this->assertSame($expected, $output);
+        });
+
+        $Controller = new Controller($Request, $Response);
+
         $Controller->set($data);
-        $Controller->set('_serialize', $serialize);
-        $Controller->set('_jsonOptions', $jsonOptions);
-        $Controller->viewBuilder()->setClassName('Json');
+        $Controller->viewBuilder()
+            ->setOptions(compact('serialize', 'jsonOptions'))
+            ->setClassName('Json');
         $View = $Controller->createView();
         $output = $View->render();
 
@@ -246,9 +259,10 @@ class JsonViewTest extends TestCase
 
         $Controller->set([
             'tags' => ['cakephp', 'framework'],
-            '_serialize' => 'tags',
         ]);
-        $Controller->viewBuilder()->setClassName('Json');
+        $Controller->viewBuilder()
+            ->setClassName('Json')
+            ->setOption('serialize', 'tags');
         $View = $Controller->createView();
         $View->render();
 
@@ -269,10 +283,10 @@ class JsonViewTest extends TestCase
         $data = ['user' => 'fake', 'list' => ['item1', 'item2']];
         $Controller->set([
             'data' => $data,
-            '_serialize' => 'data',
-            '_jsonp' => true,
         ]);
-        $Controller->viewBuilder()->setClassName('Json');
+        $Controller->viewBuilder()
+            ->setClassName('Json')
+            ->setOptions(['serialize' => 'data', 'jsonp' => true]);
         $View = $Controller->createView();
         $output = $View->render();
 
@@ -285,8 +299,9 @@ class JsonViewTest extends TestCase
         $this->assertSame($expected, $output);
         $this->assertSame('application/javascript', $View->getResponse()->getType());
 
-        $View->setRequest($View->getRequest()->withQueryParams(['jsonCallback' => 'jfunc']));
-        $View->set('_jsonp', 'jsonCallback');
+        $Controller->viewBuilder()->setOption('jsonp', 'jsonCallback');
+        $Controller->setRequest($Controller->getRequest()->withQueryParams(['jsonCallback' => 'jfunc']));
+        $View = $Controller->createView();
         $output = $View->render();
         $expected = 'jfunc(' . json_encode($data) . ')';
         $this->assertSame($expected, $output);

+ 47 - 28
tests/TestCase/View/XmlViewTest.php

@@ -47,8 +47,10 @@ class XmlViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
         $data = ['users' => ['user' => ['user1', 'user2']]];
-        $Controller->set(['users' => $data, '_serialize' => 'users']);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->set(['users' => $data]);
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', 'users');
         $View = $Controller->createView();
         $output = $View->render();
 
@@ -67,16 +69,19 @@ class XmlViewTest extends TestCase
                 ],
             ],
         ];
-        $Controller->set(['users' => $data, '_serialize' => 'users']);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->set(['users' => $data]);
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', 'users');
         $View = $Controller->createView();
         $output = $View->render();
 
         $expected = Xml::build(['response' => ['users' => $data]])->asXML();
         $this->assertSame($expected, $output);
 
-        $Controller->set('_rootNode', 'custom_name');
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('rootNode', 'custom_name');
         $View = $Controller->createView();
         $output = $View->render();
 
@@ -95,10 +100,11 @@ class XmlViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
         $Controller->set([
-            '_serialize' => 'tags',
             'tags' => ['cakephp', 'framework'],
         ]);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', 'tags');
         $View = $Controller->createView();
         $View->render();
         $this->assertFalse(isset($View->Html), 'No helper loaded.');
@@ -115,8 +121,6 @@ class XmlViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
         $data = [
-            '_serialize' => ['tags', 'nope'],
-            '_xmlOptions' => ['format' => 'attributes', 'return' => 'domdocument'],
             'tags' => [
                     'tag' => [
                         [
@@ -130,12 +134,17 @@ class XmlViewTest extends TestCase
                     ],
             ],
         ];
+        $xmlOptions = ['format' => ['format' => 'attributes', 'return' => 'domdocument']];
+
         $Controller->set($data);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', ['tags', 'nope'])
+            ->setOption('xmlOptions', $xmlOptions);
         $View = $Controller->createView();
         $result = $View->render();
 
-        $expected = Xml::build(['response' => ['tags' => $data['tags']]], $data['_xmlOptions'])->saveXML();
+        $expected = Xml::build(['response' => ['tags' => $data['tags']]], $xmlOptions)->saveXML();
         $this->assertSame($expected, $result);
     }
 
@@ -150,8 +159,6 @@ class XmlViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
         $data = [
-            '_serialize' => 'tags',
-            '_xmlOptions' => ['format' => 'attributes'],
             'tags' => [
                 'tags' => [
                     'tag' => [
@@ -167,12 +174,17 @@ class XmlViewTest extends TestCase
                 ],
             ],
         ];
+        $xmlOptions = ['format' => 'attributes'];
+
         $Controller->set($data);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', 'tags')
+            ->setOption('xmlOptions', $xmlOptions);
         $View = $Controller->createView();
         $result = $View->render();
 
-        $expected = Xml::build($data['tags'], $data['_xmlOptions'])->asXML();
+        $expected = Xml::build($data['tags'], $xmlOptions)->asXML();
         $this->assertSame($expected, $result);
     }
 
@@ -188,8 +200,9 @@ class XmlViewTest extends TestCase
         $Controller = new Controller($Request, $Response);
         $data = ['no' => 'nope', 'user' => 'fake', 'list' => ['item1', 'item2']];
         $Controller->set($data);
-        $Controller->set('_serialize', ['no', 'user']);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', ['no', 'user']);
         $View = $Controller->createView();
         $this->assertSame('application/xml', $View->getResponse()->getType());
         $output = $View->render();
@@ -198,8 +211,9 @@ class XmlViewTest extends TestCase
         ];
         $this->assertSame(Xml::build($expected)->asXML(), $output);
 
-        $Controller->set('_rootNode', 'custom_name');
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('rootNode', 'custom_name');
         $View = $Controller->createView();
         $output = $View->render();
         $expected = [
@@ -220,8 +234,9 @@ class XmlViewTest extends TestCase
         $Controller = new Controller($Request, $Response);
         $data = ['original_name' => 'my epic name', 'user' => 'fake', 'list' => ['item1', 'item2']];
         $Controller->set($data);
-        $Controller->set('_serialize', ['new_name' => 'original_name', 'user']);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', ['new_name' => 'original_name', 'user']);
         $View = $Controller->createView();
         $this->assertSame('application/xml', $View->getResponse()->getType());
         $output = $View->render();
@@ -230,8 +245,9 @@ class XmlViewTest extends TestCase
         ];
         $this->assertSame(Xml::build($expected)->asXML(), $output);
 
-        $Controller->set('_rootNode', 'custom_name');
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('rootNode', 'custom_name');
         $View = $Controller->createView();
         $output = $View->render();
         $expected = [
@@ -251,8 +267,10 @@ class XmlViewTest extends TestCase
         $Response = new Response();
         $Controller = new Controller($Request, $Response);
         $data = ['users' => ['user' => ['user1', 'user2']]];
-        $Controller->set(['users' => $data, '_serialize' => true]);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->set(['users' => $data]);
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', true);
         $View = $Controller->createView();
         $output = $View->render();
 
@@ -261,9 +279,10 @@ class XmlViewTest extends TestCase
 
         $data = ['no' => 'nope', 'user' => 'fake', 'list' => ['item1', 'item2']];
         $Controller = new Controller($Request, $Response);
-        $Controller->viewBuilder()->setClassName('Xml');
+        $Controller->viewBuilder()
+            ->setClassName('Xml')
+            ->setOption('serialize', true);
         $Controller->set($data);
-        $Controller->set('_serialize', true);
         $View = $Controller->createView();
         $output = $View->render();
         $expected = [

+ 1 - 1
tests/test_app/TestApp/Controller/PostsController.php

@@ -89,7 +89,7 @@ class PostsController extends AppController
         $data = [];
 
         $this->set(compact('data'));
-        $this->set('_serialize', ['data']);
+        $this->viewBuilder()->setOption('serialize', ['data']);
     }
 
     /**