Browse Source

Merge pull request #7236 from cakephp/email-viewbuilder

3.1 - Email ViewBuilder
Mark Story 10 years ago
parent
commit
7a270f2f64

+ 72 - 110
src/Mailer/Email.php

@@ -23,6 +23,7 @@ use Cake\Log\Log;
 use Cake\Network\Http\FormData\Part;
 use Cake\Utility\Hash;
 use Cake\Utility\Text;
+use Cake\View\ViewVarsTrait;
 use Closure;
 use Exception;
 use InvalidArgumentException;
@@ -51,6 +52,7 @@ class Email implements JsonSerializable, Serializable
 {
 
     use StaticConfigTrait;
+    use ViewVarsTrait;
 
     /**
      * Line length - no should more - RFC 2822 - 2.1.1
@@ -183,48 +185,6 @@ class Email implements JsonSerializable, Serializable
     protected $_headers = [];
 
     /**
-     * Layout for the View
-     *
-     * @var string
-     */
-    protected $_layout = 'default';
-
-    /**
-     * Template for the view
-     *
-     * @var string
-     */
-    protected $_template = '';
-
-    /**
-     * View for render
-     *
-     * @var string
-     */
-    protected $_viewRender = 'Cake\View\View';
-
-    /**
-     * Vars to sent to render
-     *
-     * @var array
-     */
-    protected $_viewVars = [];
-
-    /**
-     * Theme for the View
-     *
-     * @var string
-     */
-    protected $_theme = null;
-
-    /**
-     * Helpers to be used in the render
-     *
-     * @var array
-     */
-    protected $_helpers = ['Html'];
-
-    /**
      * Text message
      *
      * @var string
@@ -379,6 +339,12 @@ class Email implements JsonSerializable, Serializable
             $this->_domain = php_uname('n');
         }
 
+        $this->viewBuilder()
+            ->className('Cake\View\View')
+            ->template('')
+            ->layout('default')
+            ->helpers(['Html']);
+
         if ($config) {
             $this->profile($config);
         }
@@ -879,13 +845,13 @@ class Email implements JsonSerializable, Serializable
     {
         if ($template === false) {
             return [
-                'template' => $this->_template,
-                'layout' => $this->_layout
+                'template' => $this->viewBuilder()->template(),
+                'layout' => $this->viewBuilder()->layout()
             ];
         }
-        $this->_template = $template;
+        $this->viewBuilder()->template($template ?: '');
         if ($layout !== false) {
-            $this->_layout = $layout;
+            $this->viewBuilder()->layout($layout ?: false);
         }
         return $this;
     }
@@ -899,9 +865,9 @@ class Email implements JsonSerializable, Serializable
     public function viewRender($viewClass = null)
     {
         if ($viewClass === null) {
-            return $this->_viewRender;
+            return $this->viewBuilder()->className();
         }
-        $this->_viewRender = $viewClass;
+        $this->viewBuilder()->className($viewClass);
         return $this;
     }
 
@@ -914,9 +880,9 @@ class Email implements JsonSerializable, Serializable
     public function viewVars($viewVars = null)
     {
         if ($viewVars === null) {
-            return $this->_viewVars;
+            return $this->viewVars;
         }
-        $this->_viewVars = array_merge($this->_viewVars, (array)$viewVars);
+        $this->set((array)$viewVars);
         return $this;
     }
 
@@ -929,9 +895,9 @@ class Email implements JsonSerializable, Serializable
     public function theme($theme = null)
     {
         if ($theme === null) {
-            return $this->_theme;
+            return $this->viewBuilder()->theme();
         }
-        $this->_theme = $theme;
+        $this->viewBuilder()->theme($theme);
         return $this;
     }
 
@@ -944,9 +910,9 @@ class Email implements JsonSerializable, Serializable
     public function helpers($helpers = null)
     {
         if ($helpers === null) {
-            return $this->_helpers;
+            return $this->viewBuilder()->helpers();
         }
-        $this->_helpers = (array)$helpers;
+        $this->viewBuilder()->helpers((array)$helpers, false);
         return $this;
     }
 
@@ -1407,37 +1373,44 @@ class Email implements JsonSerializable, Serializable
             }
             unset($name);
         }
+
         $this->_profile = array_merge($this->_profile, $config);
-        if (!empty($config['charset'])) {
-            $this->charset = $config['charset'];
-        }
-        if (!empty($config['headerCharset'])) {
-            $this->headerCharset = $config['headerCharset'];
-        }
-        if (empty($this->headerCharset)) {
-            $this->headerCharset = $this->charset;
-        }
+
         $simpleMethods = [
-            'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
-            'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments',
-            'transport', 'emailFormat', 'theme', 'helpers', 'emailPattern'
+            'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath',
+            'cc', 'bcc', 'messageId', 'domain', 'subject', 'attachments',
+            'transport', 'emailFormat', 'emailPattern', 'charset', 'headerCharset'
         ];
         foreach ($simpleMethods as $method) {
             if (isset($config[$method])) {
                 $this->$method($config[$method]);
-                unset($config[$method]);
             }
         }
+
+        if (empty($this->headerCharset)) {
+            $this->headerCharset = $this->charset;
+        }
         if (isset($config['headers'])) {
             $this->setHeaders($config['headers']);
-            unset($config['headers']);
         }
 
-        if (array_key_exists('template', $config)) {
-            $this->_template = $config['template'];
+        $viewBuilderMethods = [
+            'template', 'layout', 'theme'
+        ];
+        foreach ($viewBuilderMethods as $method) {
+            if (array_key_exists($method, $config)) {
+                $this->viewBuilder()->$method($config[$method]);
+            }
         }
-        if (array_key_exists('layout', $config)) {
-            $this->_layout = $config['layout'];
+
+        if (array_key_exists('helpers', $config)) {
+            $this->viewBuilder()->helpers($config['helpers'], false);
+        }
+        if (array_key_exists('viewRender', $config)) {
+            $this->viewBuilder()->className($config['viewRender']);
+        }
+        if (array_key_exists('viewVars', $config)) {
+            $this->set($config['viewVars']);
         }
     }
 
@@ -1459,12 +1432,6 @@ class Email implements JsonSerializable, Serializable
         $this->_messageId = true;
         $this->_subject = '';
         $this->_headers = [];
-        $this->_layout = 'default';
-        $this->_template = '';
-        $this->_viewRender = 'Cake\View\View';
-        $this->_viewVars = [];
-        $this->_theme = null;
-        $this->_helpers = ['Html'];
         $this->_textMessage = '';
         $this->_htmlMessage = '';
         $this->_message = '';
@@ -1475,6 +1442,14 @@ class Email implements JsonSerializable, Serializable
         $this->_attachments = [];
         $this->_profile = [];
         $this->_emailPattern = self::EMAIL_PATTERN;
+
+        $this->viewBuilder()->layout('default');
+        $this->viewBuilder()->template('');
+        $this->viewBuilder()->classname('Cake\View\View');
+        $this->viewVars = [];
+        $this->viewBuilder()->theme(false);
+        $this->viewBuilder()->helpers(['Html'], false);
+
         return $this;
     }
 
@@ -1828,31 +1803,18 @@ class Email implements JsonSerializable, Serializable
     {
         $types = $this->_getTypes();
         $rendered = [];
-        if (empty($this->_template)) {
+        $template = $this->viewBuilder()->template();
+        if (empty($template)) {
             foreach ($types as $type) {
                 $rendered[$type] = $this->_encodeString($content, $this->charset);
             }
             return $rendered;
         }
-        $viewClass = $this->_viewRender;
-        if ($viewClass === 'View') {
-            $viewClass = App::className('View', 'View');
-        } else {
-            $viewClass = App::className($viewClass, 'View', 'View');
-        }
-
-        $View = new $viewClass(null);
-        $View->viewVars = $this->_viewVars;
-        $View->helpers = $this->_helpers;
-
-        if ($this->_theme) {
-            $View->theme = $this->_theme;
-        }
 
-        $View->loadHelpers();
+        $View = $this->createView();
 
-        list($templatePlugin) = pluginSplit($this->_template);
-        list($layoutPlugin) = pluginSplit($this->_layout);
+        list($templatePlugin) = pluginSplit($View->template());
+        list($layoutPlugin) = pluginSplit($View->layout());
         if ($templatePlugin) {
             $View->plugin = $templatePlugin;
         } elseif ($layoutPlugin) {
@@ -1863,17 +1825,12 @@ class Email implements JsonSerializable, Serializable
             $View->set('content', $content);
         }
 
-        // Convert null to false, as View needs false to disable
-        // the layout.
-        if ($this->_layout === null) {
-            $this->_layout = false;
-        }
-
         foreach ($types as $type) {
             $View->hasRendered = false;
-            $View->viewPath = $View->layoutPath = 'Email/' . $type;
+            $View->templatePath('Email/' . $type);
+            $View->layoutPath('Email/' . $type);
 
-            $render = $View->render($this->_template, $this->_layout);
+            $render = $View->render();
             $render = str_replace(["\r\n", "\r"], "\n", $render);
             $rendered[$type] = $this->_encodeString($render, $this->charset);
         }
@@ -1934,12 +1891,12 @@ class Email implements JsonSerializable, Serializable
     public function jsonSerialize()
     {
         $properties = [
-            '_to', '_from', '_sender', '_replyTo', '_cc', '_bcc', '_subject', '_returnPath', '_readReceipt',
-            '_template', '_layout', '_viewRender', '_viewVars', '_theme', '_helpers', '_emailFormat',
-            '_emailPattern', '_attachments', '_domain', '_messageId', '_headers', 'charset', 'headerCharset',
+            '_to', '_from', '_sender', '_replyTo', '_cc', '_bcc', '_subject',
+            '_returnPath', '_readReceipt', '_emailFormat', '_emailPattern', '_domain',
+            '_attachments', '_messageId', '_headers', 'viewVars', 'charset', 'headerCharset'
         ];
 
-        $array = [];
+        $array = ['viewConfig' => $this->viewBuilder()->jsonSerialize()];
 
         foreach ($properties as $property) {
             $array[$property] = $this->{$property};
@@ -1952,7 +1909,7 @@ class Email implements JsonSerializable, Serializable
             }
         });
 
-        array_walk_recursive($array['_viewVars'], [$this, '_checkViewVars']);
+        array_walk_recursive($array['viewVars'], [$this, '_checkViewVars']);
 
         return array_filter($array, function ($i) {
             return !is_array($i) && strlen($i) || !empty($i);
@@ -1993,6 +1950,11 @@ class Email implements JsonSerializable, Serializable
      */
     public function createFromArray($config)
     {
+        if (isset($config['viewConfig'])) {
+            $this->viewBuilder()->createFromArray($config['viewConfig']);
+            unset($config['viewConfig']);
+        }
+
         foreach ($config as $property => $value) {
             $this->{$property} = $value;
         }
@@ -2003,7 +1965,7 @@ class Email implements JsonSerializable, Serializable
     /**
      * Serializes the Email object.
      *
-     * @return void.
+     * @return string
      */
     public function serialize()
     {

+ 64 - 1
src/View/ViewBuilder.php

@@ -20,6 +20,8 @@ use Cake\Network\Request;
 use Cake\Network\Response;
 use Cake\View\Exception\MissingViewException;
 use Cake\View\View;
+use JsonSerializable;
+use Serializable;
 
 /**
  * Provides an API for iteratively building a view up.
@@ -27,7 +29,7 @@ use Cake\View\View;
  * Once you have configured the view and established all the context
  * you can create a view instance with `build()`.
  */
-class ViewBuilder
+class ViewBuilder implements JsonSerializable, Serializable
 {
     /**
      * The subdirectory to the template.
@@ -338,4 +340,65 @@ class ViewBuilder
         $data += $this->_options;
         return new $className($request, $response, $events, $data);
     }
+
+    /**
+     * Serializes the view builder object to a value that can be natively
+     * serialized and re-used to clone this builder instance.
+     *
+     * @return array Serializable array of configuration properties.
+     */
+    public function jsonSerialize()
+    {
+        $properties = [
+            '_templatePath', '_template', '_plugin', '_theme', '_layout', '_autoLayout',
+            '_layoutPath', '_name', '_className', '_options', '_helpers'
+        ];
+
+        $array = [];
+
+        foreach ($properties as $property) {
+            $array[$property] = $this->{$property};
+        }
+
+        return array_filter($array, function ($i) {
+            return !is_array($i) && strlen($i) || !empty($i);
+        });
+    }
+
+    /**
+     * Configures a view builder instance from serialized config.
+     *
+     * @param array $config View builder configuration array.
+     * @return $this Configured view builder instance.
+     */
+    public function createFromArray($config)
+    {
+        foreach ($config as $property => $value) {
+            $this->{$property} = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Serializes the view builder object.
+     *
+     * @return string
+     */
+    public function serialize()
+    {
+        $array = $this->jsonSerialize();
+        return serialize($array);
+    }
+
+    /**
+     * Unserializes the view builder object.
+     *
+     * @param string $data Serialized string.
+     * @return $this Configured view builder instance.
+     */
+    public function unserialize($data)
+    {
+        return $this->createFromArray(unserialize($data));
+    }
 }

+ 4 - 3
src/View/ViewVarsTrait.php

@@ -14,6 +14,7 @@
 namespace Cake\View;
 
 use Cake\Core\App;
+use Cake\Event\EventDispatcherInterface;
 use Cake\View\ViewBuilder;
 
 /**
@@ -106,9 +107,9 @@ trait ViewVarsTrait
 
         return $builder->build(
             $this->viewVars,
-            $this->request,
-            $this->response,
-            $this->eventManager(),
+            isset($this->request) ? $this->request : null,
+            isset($this->response) ? $this->response : null,
+            $this instanceof EventDispatcherInterface ? $this->eventManager() : null,
             $viewOptions
         );
     }

+ 20 - 15
tests/TestCase/Mailer/EmailTest.php

@@ -682,11 +682,11 @@ class EmailTest extends TestCase
         $this->assertSame($expected, $this->CakeEmail->template());
 
         $this->CakeEmail->template('template', null);
-        $expected = ['template' => 'template', 'layout' => null];
+        $expected = ['template' => 'template', 'layout' => false];
         $this->assertSame($expected, $this->CakeEmail->template());
 
         $this->CakeEmail->template(null, null);
-        $expected = ['template' => null, 'layout' => null];
+        $expected = ['template' => '', 'layout' => false];
         $this->assertSame($expected, $this->CakeEmail->template());
     }
 
@@ -717,7 +717,7 @@ class EmailTest extends TestCase
         $this->assertSame(['value' => 12345], $this->CakeEmail->viewVars());
 
         $this->CakeEmail->viewVars(['name' => 'CakePHP']);
-        $this->assertSame(['value' => 12345, 'name' => 'CakePHP'], $this->CakeEmail->viewVars());
+        $this->assertEquals(['value' => 12345, 'name' => 'CakePHP'], $this->CakeEmail->viewVars());
 
         $this->CakeEmail->viewVars(['value' => 4567]);
         $this->assertSame(['value' => 4567, 'name' => 'CakePHP'], $this->CakeEmail->viewVars());
@@ -1875,7 +1875,7 @@ class EmailTest extends TestCase
         $this->assertSame($instance->to(), ['debug@cakephp.org' => 'debug@cakephp.org']);
         $this->assertSame($instance->subject(), 'Update ok');
         $this->assertSame($instance->template(), ['template' => 'custom', 'layout' => 'custom_layout']);
-        $this->assertSame($instance->viewVars(), ['value' => 123, 'name' => 'CakePHP']);
+        $this->assertEquals($instance->viewVars(), ['value' => 123, 'name' => 'CakePHP']);
         $this->assertSame($instance->cc(), ['cake@cakephp.org' => 'Myself']);
 
         $configs = ['from' => 'root@cakephp.org', 'message' => 'Message from configs', 'transport' => 'debug'];
@@ -1938,7 +1938,7 @@ class EmailTest extends TestCase
 
         $this->CakeEmail->reset();
         $this->assertSame([], $this->CakeEmail->to());
-        $this->assertNull($this->CakeEmail->theme());
+        $this->assertFalse($this->CakeEmail->theme());
         $this->assertSame(Email::EMAIL_PATTERN, $this->CakeEmail->emailPattern());
     }
 
@@ -2648,7 +2648,6 @@ XML;
             ->cc(['mark@cakephp.org', 'juan@cakephp.org' => 'Juan Basso'])
             ->bcc('phpnut@cakephp.org')
             ->subject('Test Serialize')
-            ->template('default', 'test')
             ->messageId('<uuid@server.com>')
             ->domain('foo.bar')
             ->viewVars([
@@ -2664,9 +2663,13 @@ XML;
                 ]
             ]);
 
+        $this->CakeEmail->viewBuilder()
+            ->template('default')
+            ->layout('test');
+
         $result = json_decode(json_encode($this->CakeEmail), true);
-        $this->assertContains('test', $result['_viewVars']['exception']);
-        unset($result['_viewVars']['exception']);
+        $this->assertContains('test', $result['viewVars']['exception']);
+        unset($result['viewVars']['exception']);
 
         $encode = function ($path) {
             return chunk_split(base64_encode(file_get_contents($path)), 76, "\r\n");
@@ -2679,16 +2682,18 @@ XML;
             '_cc' => ['mark@cakephp.org' => 'mark@cakephp.org', 'juan@cakephp.org' => 'Juan Basso'],
             '_bcc' => ['phpnut@cakephp.org' => 'phpnut@cakephp.org'],
             '_subject' => 'Test Serialize',
-            '_template' => 'default',
-            '_layout' => 'test',
-            '_viewRender' => 'Cake\View\View',
-            '_helpers' => ['Html'],
             '_emailFormat' => 'text',
             '_messageId' => '<uuid@server.com>',
             '_domain' => 'foo.bar',
             'charset' => 'utf-8',
             'headerCharset' => 'utf-8',
-            '_viewVars' => [
+            'viewConfig' => [
+                '_template' => 'default',
+                '_layout' => 'test',
+                '_helpers' => ['Html'],
+                '_className' => 'Cake\View\View',
+            ],
+            'viewVars' => [
                 'users' => [
                     'id' => 1,
                     'username' => 'mariano'
@@ -2713,8 +2718,8 @@ XML;
         $this->assertEquals($expected, $result);
 
         $result = json_decode(json_encode(unserialize(serialize($this->CakeEmail))), true);
-        $this->assertContains('test', $result['_viewVars']['exception']);
-        unset($result['_viewVars']['exception']);
+        $this->assertContains('test', $result['viewVars']['exception']);
+        unset($result['viewVars']['exception']);
         $this->assertEquals($expected, $result);
     }
 

+ 55 - 0
tests/TestCase/View/ViewBuilderTest.php

@@ -183,4 +183,59 @@ class ViewBuilderTest extends TestCase
         $builder->className('Foo');
         $builder->build();
     }
+
+    /**
+     * testJsonSerialize()
+     *
+     * @return void
+     */
+    public function testJsonSerialize()
+    {
+        $builder = new ViewBuilder();
+
+        $builder
+            ->template('default')
+            ->layout('test')
+            ->helpers(['Html'])
+            ->className('JsonView');
+
+        $result = json_decode(json_encode($builder), true);
+
+        $expected = [
+            '_template' => 'default',
+            '_layout' => 'test',
+            '_helpers' => ['Html'],
+            '_className' => 'JsonView',
+        ];
+        $this->assertEquals($expected, $result);
+
+        $result = json_decode(json_encode(unserialize(serialize($builder))), true);
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * testCreateFromArray()
+     *
+     * @return void
+     */
+    public function testCreateFromArray()
+    {
+        $builder = new ViewBuilder();
+
+        $builder
+            ->template('default')
+            ->layout('test')
+            ->helpers(['Html'])
+            ->className('JsonView');
+
+        $result = json_encode($builder);
+
+        $builder = new ViewBuilder();
+        $builder->createFromArray(json_decode($result, true));
+
+        $this->assertEquals('default', $builder->template());
+        $this->assertEquals('test', $builder->layout());
+        $this->assertEquals(['Html'], $builder->helpers());
+        $this->assertEquals('JsonView', $builder->className());
+    }
 }