Browse Source

Add initial `Mailer` class + tests

Jad Bitar 11 years ago
parent
commit
97ef0750fe

+ 36 - 0
src/Mailer/Exception/MissingMailException.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Mailer\Exception;
+
+use Cake\Core\Exception\Exception;
+
+/**
+ * Missing Action exception - used when a controller action
+ * cannot be found, or when the controller's isAction() method returns false.
+ */
+class MissingMailException extends Exception
+{
+
+    /**
+     * {@inheritDoc}
+     */
+    protected $_messageTemplate = 'Mail %s::%s() could not be found, or is not accessible.';
+
+    /**
+     * {@inheritDoc}
+     */
+    public function __construct($message, $code = 404)
+    {
+        parent::__construct($message, $code);
+    }
+}

+ 69 - 44
src/Mailer/Mailer.php

@@ -1,27 +1,40 @@
 <?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
 namespace Cake\Mailer;
 
 use ArrayAccess;
 use Cake\Datasource\ModelAwareTrait;
 use Cake\Event\EventListenerInterface;
+use Cake\Mailer\Exception\MissingMailException;
+use Cake\Utility\Inflector;
 
-class Mailer implements ArrayAccess, EventListenerInterface
+abstract class Mailer implements ArrayAccess, EventListenerInterface
 {
     use ModelAwareTrait;
 
     /**
-     * Email instance.
+     * Layout.
      *
-     * @var \Cake\Mailer\Email
+     * @var string
      */
-    protected $_email;
+    public $layout;
 
     /**
-     * Serialized email instance's initial state configuration defined by mailer.
+     * Email instance.
      *
-     * @var string
+     * @var \Cake\Mailer\Email
      */
-    protected $_emailInitialState;
+    protected $_email;
 
     /**
      * Constructor.
@@ -34,54 +47,33 @@ class Mailer implements ArrayAccess, EventListenerInterface
             $email = new Email();
         }
 
-        $this->_email = $email->profile($this);
-        $this->_emailInitialState = $email->jsonSerialize();
-    }
-
-    /**
-     * Dispatches mailer actions.
-     *
-     * @param string $action Name of the action to trigger (i.e. 'welcome' will trigger '_welcome').
-     * @param array $args Arguments passed to triggered action.
-     * @return mixed When using `send` prefix, result of `\Cake\Mailer\Email::send()`. Otherwise, $this.
-     */
-    public function __call($method, $args)
-    {
-        $action = str_replace('send', '', $method);
-        $send = $action !== $method;
-
-        $actionMethod = Inflector::camelize($action);
-        if (!method_exists($this, $actionMethod)) {
-            throw new \Exception('Missing mailer action: ' . $actionMethod);
+        if ($this->layout === null) {
+            $this->layout = Inflector::underscore($this->getName());
         }
 
-        $this += [
-            'template' => Inflector::underscore($action),
-        ];
-
-        call_user_func_array([$this, $actionMethod], $args);
-
-        return $send ? $this->send() : $this;
+        $this->_email = $email->profile((array)$this);
     }
 
     /**
-     * Implemented events.
+     * Returns the mailer's name.
      *
-     * @return array
+     * @return string
      */
-    public function implementedEvents()
+    public function getName()
     {
-        return [];
+        return str_replace('Mailer', '', join('', array_slice(explode('\\', get_class($this)), -1)));
     }
 
     /**
-     * Resets email instance to initial state used by mailer.
+     * Sets layout to use. Defaults to configured layout template if a custom layout
+     * could not be found.
      *
+     * @param string $layout Name of the layout to use.
      * @return $this object.
      */
-    public function reset()
+    public function layout($layout)
     {
-        $this->_email->profile(json_decode($this->_emailInitialState));
+        $this->layout = $layout;
         return $this;
     }
 
@@ -89,7 +81,7 @@ class Mailer implements ArrayAccess, EventListenerInterface
      * Sets headers.
      *
      * @param array $headers Headers to set.
-     * @return  $this object.
+     * @return $this object.
      */
     public function setHeaders(array $headers)
     {
@@ -101,7 +93,7 @@ class Mailer implements ArrayAccess, EventListenerInterface
      * Adds headers.
      *
      * @param array $headers Headers to set.
-     * @return  $this object.
+     * @return $this object.
      */
     public function addHeaders(array $headers)
     {
@@ -138,16 +130,28 @@ class Mailer implements ArrayAccess, EventListenerInterface
     /**
      * Sends email.
      *
+     * @param string $mail The name of the mail action to trigger.
+     * @param array $args Arguments to pass to the triggered mail action.
      * @param array $headers Headers to set.
      * @return array
+     * @throws \Cake\Mailer\Exception\MissingMailException
      * @throws \BadMethodCallException
      */
-    public function send($headers = [])
+    public function send($mail, $args = [], $headers = [])
     {
+        if (!method_exists($this, $mail)) {
+            throw new MissingMailException([
+                'mailer' => $this->getName() . 'Mailer',
+                'mail' => $mail,
+            ]);
+        }
+
         $this->setHeaders($headers);
 
+        call_user_func_array([$this, $mail], $args);
+
         $result = $this->_email
-            ->profile($this)
+            ->profile((array)$this)
             ->send();
 
         $this->reset();
@@ -155,6 +159,17 @@ class Mailer implements ArrayAccess, EventListenerInterface
     }
 
     /**
+     * Resets email instance to original config.
+     *
+     * @return $this object.
+     */
+    public function reset()
+    {
+        $this->_email->reset();
+        return $this;
+    }
+
+    /**
      * {@inheritdoc}
      */
     public function offsetExists($offset)
@@ -194,4 +209,14 @@ class Mailer implements ArrayAccess, EventListenerInterface
     {
         unset($this->{$offset});
     }
+
+    /**
+     * Implemented events.
+     *
+     * @return array
+     */
+    public function implementedEvents()
+    {
+        return [];
+    }
 }

+ 118 - 0
tests/TestCase/Mailer/MailerTest.php

@@ -0,0 +1,118 @@
+<?php
+namespace Cake\Test\TestCase\Mailer;
+
+use Cake\Mailer\Email;
+use Cake\Mailer\Mailer;
+use Cake\TestSuite\TestCase;
+
+class TestMailer extends Mailer
+{
+    public function getEmailForAssertion()
+    {
+        return $this->_email;
+    }
+
+}
+
+class MailerTest extends TestCase
+{
+    public function getMockForEmail($methods = [], $args = [])
+    {
+        return $this->getMock('Cake\Mailer\Email', (array)$methods, (array)$args);
+    }
+
+    public function testConstructor()
+    {
+        $mailer = new TestMailer();
+        $this->assertInstanceOf('Cake\Mailer\Email', $mailer->getEmailForAssertion());
+        $this->assertEquals('test', $mailer->layout);
+    }
+
+    public function testReset()
+    {
+        $mailer = new TestMailer();
+        $email = $mailer->getEmailForAssertion();
+
+        $mailer->set(['foo' => 'bar']);
+        $this->assertNotEquals($email->viewVars(), $mailer->reset()->getEmailForAssertion()->viewVars());
+    }
+
+    public function testGetName()
+    {
+        $result = (new TestMailer())->getName();
+        $expected = 'Test';
+        $this->assertEquals($expected, $result);
+    }
+
+    public function testLayout()
+    {
+        $result = (new TestMailer())->layout('foo');
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+        $this->assertEquals('foo', $result->layout);
+    }
+
+    public function testProxies()
+    {
+        $email = $this->getMockForEmail('setHeaders');
+        $email->expects($this->once())
+            ->method('setHeaders')
+            ->with([]);
+        $result = (new TestMailer($email))->setHeaders([]);
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+
+        $email = $this->getMockForEmail('addHeaders');
+        $email->expects($this->once())
+            ->method('addHeaders')
+            ->with([]);
+        $result = (new TestMailer($email))->addHeaders([]);
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+
+        $email = $this->getMockForEmail('attachments');
+        $email->expects($this->once())
+            ->method('attachments')
+            ->with([]);
+        $result = (new TestMailer($email))->attachments([]);
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+    }
+
+    public function testSet()
+    {
+        $email = $this->getMockForEmail('viewVars');
+        $email->expects($this->once())
+            ->method('viewVars')
+            ->with(['key' => 'value']);
+        $result = (new TestMailer($email))->set('key', 'value');
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+
+        $email = $this->getMockForEmail('viewVars');
+        $email->expects($this->once())
+            ->method('viewVars')
+            ->with(['key' => 'value']);
+        $result = (new TestMailer($email))->set(['key' => 'value']);
+        $this->assertInstanceOf('Cake\Test\TestCase\Mailer\TestMailer', $result);
+    }
+
+    public function testSend()
+    {
+        $email = $this->getMockForEmail('send');
+        $email->expects($this->any())
+            ->method('send')
+            ->will($this->returnValue([]));
+
+        $mailer = $this->getMock('Cake\Test\TestCase\Mailer\TestMailer', ['test'], [$email]);
+        $mailer->expects($this->once())
+            ->method('test')
+            ->with('foo', 'bar');
+
+        $mailer->send('test', ['foo', 'bar']);
+    }
+
+    /**
+     * @expectedException Cake\Mailer\Exception\MissingMailException
+     * @expectedExceptionMessage Mail TestMailer::test() could not be found, or is not accessible.
+     */
+    public function testMissingMailThrowsException()
+    {
+        (new TestMailer())->send('test');
+    }
+}