diff --git a/framework/yii/email/BaseMailer.php b/framework/yii/email/BaseMailer.php index 92fa3df..b3b40b3 100644 --- a/framework/yii/email/BaseMailer.php +++ b/framework/yii/email/BaseMailer.php @@ -18,7 +18,8 @@ use Yii; * * @see BaseMessage * - * @property array $view view instance or its array configuration. + * @property \yii\base\View|array $view view instance or its array configuration. + * @property \yii\email\ViewResolver|array $viewResolver view resolver instance or its array configuration. * @property array $defaultMessageConfig configuration, which should be applied by default to any * new created email message instance. * @@ -32,6 +33,10 @@ abstract class BaseMailer extends Component */ private $_view = []; /** + * @var \yii\email\ViewResolver|array view resolver instance or its array configuration. + */ + private $_viewResolver = []; + /** * @var array configuration, which should be applied by default to any new created * email message instance. * For example: @@ -69,6 +74,29 @@ abstract class BaseMailer extends Component } /** + * @param array|\yii\email\ViewResolver $viewResolver view resolver instance or its array configuration. + * @throws \yii\base\InvalidConfigException on invalid argument. + */ + public function setViewResolver($viewResolver) + { + if (!is_array($viewResolver) && !is_object($viewResolver)) { + throw new InvalidConfigException('"' . get_class($this) . '::viewResolver" should be either object or array, "' . gettype($viewResolver) . '" given.'); + } + $this->_viewResolver = $viewResolver; + } + + /** + * @return \yii\email\ViewResolver view resolver. + */ + public function getViewResolver() + { + if (!is_object($this->_viewResolver)) { + $this->_viewResolver = $this->createViewResolver($this->_viewResolver); + } + return $this->_viewResolver; + } + + /** * @param array $defaultMessageConfig default message config */ public function setDefaultMessageConfig(array $defaultMessageConfig) @@ -94,7 +122,19 @@ abstract class BaseMailer extends Component if (!array_key_exists('class', $config)) { $config['class'] = '\yii\base\View'; } - $config['context'] = $this; + return Yii::createObject($config); + } + + /** + * Creates view resolver instance from given configuration. + * @param array $config view resolver configuration. + * @return \yii\email\ViewResolver view resolver instance. + */ + protected function createViewResolver(array $config) + { + if (!array_key_exists('class', $config)) { + $config['class'] = '\yii\email\ViewResolver'; + } return Yii::createObject($config); } @@ -122,4 +162,15 @@ abstract class BaseMailer extends Component } return $successCount; } + + /** + * Renders a view. + * @param string $view the view name or the path alias of the view file. + * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. + * @return string string the rendering result + */ + public function render($view, $params = []) + { + return $this->getView()->renderFile($this->getViewResolver()->findViewFile($view), $params, $this); + } } \ No newline at end of file diff --git a/framework/yii/email/BaseMessage.php b/framework/yii/email/BaseMessage.php index 506563b..00d27e0 100644 --- a/framework/yii/email/BaseMessage.php +++ b/framework/yii/email/BaseMessage.php @@ -134,4 +134,18 @@ abstract class BaseMessage extends Object $content = file_get_contents($fileName); $this->createAttachment($content, $attachFileName, $contentType); } + + /** + * Renders a view. + * The view to be rendered can be specified in one of the following formats: + * - path alias (e.g. "@app/emails/contact/body"); + * - relative path (e.g. "contact"): the actual view file will be resolved by [[resolveView]]. + * @param string $view the view name or the path alias of the view file. + * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. + * @return string string the rendering result + */ + public function render($view, $params = []) + { + return $this->getMailer()->render($view, $params); + } } \ No newline at end of file diff --git a/framework/yii/email/ViewResolver.php b/framework/yii/email/ViewResolver.php new file mode 100644 index 0000000..4f522be --- /dev/null +++ b/framework/yii/email/ViewResolver.php @@ -0,0 +1,57 @@ + + * @since 2.0 + */ +class ViewResolver extends Component +{ + /** + * @var string directory containing view files for this email messages. + */ + public $viewPath = '@app/emails'; + + /** + * Finds the view file based on the given view name. + * The view to be rendered can be specified in one of the following formats: + * - path alias (e.g. "@app/emails/contact/body"); + * - relative path (e.g. "contact"): the actual view file will be resolved by [[resolveView]]. + * @param string $view the view name or the path alias of the view file. + * @return string the view file path. Note that the file may not exist. + */ + public function findViewFile($view) + { + if (strncmp($view, '@', 1) === 0) { + // e.g. "@app/views/main" + $file = Yii::getAlias($view); + } else { + $file = $this->resolveView($view); + } + return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file; + } + + /** + * Composes file name for the view name, appending view name to [[viewPath]]. + * Child classes may override this method to provide more sophisticated + * search of the view files or even composition of the view files "on the fly". + * @param string $view the view name. + * @return string the view file path. + */ + protected function resolveView($view) + { + return Yii::getAlias($this->viewPath) . DIRECTORY_SEPARATOR . $view; + } +} \ No newline at end of file diff --git a/tests/unit/framework/email/BaseMailerTest.php b/tests/unit/framework/email/BaseMailerTest.php index 178028b..bd97aeb 100644 --- a/tests/unit/framework/email/BaseMailerTest.php +++ b/tests/unit/framework/email/BaseMailerTest.php @@ -6,6 +6,8 @@ use Yii; use yii\base\View; use yii\email\BaseMailer; use yii\email\BaseMessage; +use yii\email\ViewResolver; +use yii\helpers\FileHelper; use yiiunit\TestCase; /** @@ -17,6 +19,26 @@ class BaseMailerTest extends TestCase { $this->mockApplication(); Yii::$app->setComponent('email', $this->createTestEmailComponent()); + $filePath = $this->getTestFilePath(); + if (!file_exists($filePath)) { + FileHelper::createDirectory($filePath); + } + } + + public function tearDown() + { + $filePath = $this->getTestFilePath(); + if (file_exists($filePath)) { + FileHelper::removeDirectory($filePath); + } + } + + /** + * @return string test file path. + */ + protected function getTestFilePath() + { + return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid(); } /** @@ -37,8 +59,22 @@ class BaseMailerTest extends TestCase $view = new View(); $mailer->setView($view); $this->assertEquals($view, $mailer->getView(), 'Unable to setup view!'); + + $viewConfig = [ + 'params' => [ + 'param1' => 'value1', + 'param2' => 'value2', + ] + ]; + $mailer->setView($viewConfig); + $view = $mailer->getView(); + $this->assertTrue(is_object($view), 'Unable to setup view via config!'); + $this->assertEquals($viewConfig['params'], $view->params, 'Unable to configure view via config array!'); } + /** + * @depends testSetupView + */ public function testGetDefaultView() { $mailer = new Mailer(); @@ -46,6 +82,33 @@ class BaseMailerTest extends TestCase $this->assertTrue(is_object($view), 'Unable to get default view!'); } + public function testSetupViewResolver() + { + $mailer = new Mailer(); + + $viewResolver = new ViewResolver(); + $mailer->setViewResolver($viewResolver); + $this->assertEquals($viewResolver, $mailer->getViewResolver(), 'Unable to setup view resolver!'); + + $viewResolverConfig = [ + 'viewPath' => '/test/view/path', + ]; + $mailer->setViewResolver($viewResolverConfig); + $viewResolver = $mailer->getViewResolver(); + $this->assertTrue(is_object($viewResolver), 'Unable to setup view resolver via config!'); + $this->assertEquals($viewResolverConfig['viewPath'], $viewResolver->viewPath, 'Unable to configure view resolver via config array!'); + } + + /** + * @depends testSetupViewResolver + */ + public function testGetDefaultViewResolver() + { + $mailer = new Mailer(); + $viewResolver = $mailer->getViewResolver(); + $this->assertTrue(is_object($viewResolver), 'Unable to get default view resolver!'); + } + public function testDefaultMessageConfig() { $defaultMessageConfig = array( @@ -60,6 +123,29 @@ class BaseMailerTest extends TestCase $this->assertEquals($value, $message->$name); } } + + /** + * @depends testGetDefaultView + * @depends testGetDefaultViewResolver + */ + public function testRender() + { + $mailer = new Mailer(); + + $filePath = $this->getTestFilePath(); + $mailer->getViewResolver()->viewPath = $filePath; + + $viewName = 'test_view'; + $fileName = $filePath . DIRECTORY_SEPARATOR . $viewName . '.php'; + $fileContent = ''; + file_put_contents($fileName, $fileContent); + + $params = [ + 'testParam' => 'test output' + ]; + $renderResult = $mailer->render($viewName, $params); + $this->assertEquals($params['testParam'], $renderResult); + } } /** diff --git a/tests/unit/framework/email/ViewResolverTest.php b/tests/unit/framework/email/ViewResolverTest.php new file mode 100644 index 0000000..9185f72 --- /dev/null +++ b/tests/unit/framework/email/ViewResolverTest.php @@ -0,0 +1,57 @@ +testViewPath); + return [ + [ + $alias . '/test', + $aliasPath . '/test.php', + ], + [ + $alias . '/test.tpl', + $aliasPath . '/test.tpl', + ], + [ + 'contact/html', + $viewPath . '/contact/html.php', + ], + [ + 'contact/html.tpl', + $viewPath . '/contact/html.tpl', + ], + ]; + } + + /** + * @dataProvider dataProviderFindViewFile + * + * @param string $view + * @param string $expectedFileName + */ + public function testFindViewFile($view, $expectedFileName) + { + $viewResolver = new ViewResolver(); + $viewResolver->viewPath = $this->testViewPath; + $fileName = $viewResolver->findViewFile($view); + $this->assertEquals($expectedFileName, $fileName); + } +} \ No newline at end of file