Skip to content

Commit

Permalink
apply email rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
klimov-paul committed May 9, 2024
1 parent f26a981 commit 324ba56
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 16 deletions.
70 changes: 70 additions & 0 deletions src/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\RawMessage;
use Yii;
use yii1tech\mailer\transport\ArrayTransport;

/**
Expand Down Expand Up @@ -53,6 +54,13 @@ class Mailer extends CApplicationComponent
*/
private $_transport;

/**
* @var \yii1tech\mailer\View|array view instance or its array configuration.
*/
private $_view = [
'class' => View::class,
];

public function send(RawMessage $message, ?Envelope $envelope = null): void
{
foreach ($this->defaultHeaders as $name => $value) {
Expand All @@ -62,6 +70,10 @@ public function send(RawMessage $message, ?Envelope $envelope = null): void
$message->getHeaders()->addHeader($name, $value);
}

if ($message instanceof TemplatedEmailContract) {
$message = $this->render($message);
}

$this->getSymfonyMailer()->send($message, $envelope);
}

Expand Down Expand Up @@ -123,6 +135,12 @@ public function getTransport(): TransportInterface
return $this->_transport;
}

/**
* Creates new transport instance from configuration.
*
* @param callable|string $config transport class name or factory callback.
* @return \Symfony\Component\Mailer\Transport\TransportInterface transport instance.
*/
protected function createTransport($config): TransportInterface
{
if (is_callable($config)) {
Expand All @@ -135,4 +153,56 @@ protected function createTransport($config): TransportInterface

throw new \LogicException('Transport configuration should be either a factory callback or a string class name.');
}

/**
* @return \yii1tech\mailer\View view instance.
*/
public function getView()
{
if (!is_object($this->_view)) {
$this->_view = Yii::createComponent($this->_view);
}

return $this->_view;
}

/**
* @param \yii1tech\mailer\View|array|string $view view instance or its configuration.
* @return static self reference.
*/
public function setView($view): self
{
$this->_view = $view;

return $this;
}

/**
* Renders the email message, populating its body parts from the templates.
*
* @param \Symfony\Component\Mime\RawMessage|\Symfony\Component\Mime\Email|\yii1tech\mailer\TemplatedEmailContract $message raw message.
* @return \Symfony\Component\Mime\RawMessage rendered message.
*/
protected function render(RawMessage $message): RawMessage
{
if ($message->isRendered()) {
return $message;
}

if (($textTemplate = $message->getTextTemplate()) !== null) {
$text = $this->getView()->render($textTemplate, $message->getContext(), $message->getLocale());

$message->text($text);
}

if (($htmlTemplate = $message->getHtmlTemplate()) !== null) {
$html = $this->getView()->render($htmlTemplate, $message->getContext(), $message->getLocale());

$message->html($html);
}

$message->markAsRendered();

return $message;
}
}
8 changes: 6 additions & 2 deletions src/TemplatedEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
use Symfony\Component\Mime\Email;

/**
* TemplatedEmail allows specification of the email body parts as a rendering of the templates.
*
* @see \yii1tech\mailer\View
*
* @author Paul Klimov <[email protected]>
* @since 1.0
*/
class TemplatedEmail extends Email
class TemplatedEmail extends Email implements TemplatedEmailContract
{

use TemplatedEmailTrait;
}
44 changes: 44 additions & 0 deletions src/TemplatedEmailContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace yii1tech\mailer;

/**
* TemplatedEmailContract defines the email message, which content, should be rendered from the templates.
*
* This interface should be applied to descendant of {@see \Symfony\Component\Mime\Email}.
*
* @author Paul Klimov <[email protected]>
* @since 1.0
*/
interface TemplatedEmailContract
{
/**
* @return string|null template name for text body.
*/
public function getTextTemplate(): ?string;

/**
* @return string|null template name for HTML body.
*/
public function getHtmlTemplate(): ?string;

/**
* @return array<string, mixed> template context variables.
*/
public function getContext(): array;

/**
* @return string|null locale to be used during template rendering.
*/
public function getLocale(): ?string;

/**
* @return bool whether the templates have been already rendered.
*/
public function isRendered(): bool;

/**
* Marks this email as already rendered.
*/
public function markAsRendered(): void;
}
116 changes: 116 additions & 0 deletions src/TemplatedEmailTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace yii1tech\mailer;

/**
* Satisfies {@see \yii1tech\mailer\TemplatedEmailContract} interface.
*
* @mixin \Symfony\Component\Mime\Email
*
* @author Paul Klimov <[email protected]>
* @since 1.0
*/
trait TemplatedEmailTrait
{
private $htmlTemplate = null;

private $textTemplate = null;

private $locale = null;

private $context = [];

private $isRendered = false;

/**
* @param string|null $template template name for text body.
* @return static self reference.
*/
public function textTemplate(?string $template): self
{
$this->textTemplate = $template;

return $this;
}

/**
* @param string|null $template template name for HTML body.
* @return static self reference.
*/
public function htmlTemplate(?string $template): self
{
$this->htmlTemplate = $template;

return $this;
}

/**
* @param string|null $locale
* @return static self reference.
*/
public function locale(?string $locale): self
{
$this->locale = $locale;

return $this;
}

/**
* @param array<string, mixed> $context template context variables.
* @return static self reference.
*/
public function context(array $context): self
{
$this->context = $context;

return $this;
}

/**
* @return string|null template name for text body.
*/
public function getTextTemplate(): ?string
{
return $this->textTemplate;
}

/**
* @return string|null template name for HTML body.
*/
public function getHtmlTemplate(): ?string
{
return $this->htmlTemplate;
}

/**
* @return array<string, mixed> template context variables.
*/
public function getContext(): array
{
return $this->context;
}

/**
* @return string|null locale to be used during template rendering.
*/
public function getLocale(): ?string
{
return $this->locale;
}

/**
* @return bool whether the templates have been already rendered.
*/
public function isRendered(): bool
{
return $this->isRendered;
}

/**
* Marks this email as already rendered.
*/
public function markAsRendered(): void
{
$this->isRendered = true;
}
}
15 changes: 10 additions & 5 deletions src/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,20 @@ public function renderFile($viewFile, $data = null, $return = false)
*
* @param string $view name of the view to be rendered. See {@see getViewFile()} for details about how the view script is resolved.
* @param array|null $data data to be extracted into PHP variables and made available to the view script.
* @param string|null $locale locale to be used while template rendering.
* @return string the rendering result.
*/
public function render(string $view, ?array $data = null): string
public function render(string $view, ?array $data = null, ?string $locale = null): string
{
$originalLayout = $this->layout;
$originalLocale = Yii::app()->getLanguage();
$obInitialLevel = ob_get_level();

try {
if ($locale !== null) {
Yii::app()->setLanguage($locale);
}

$content = $this->renderPartial($view, $data, true);

if (!empty($this->layout)) {
Expand All @@ -175,11 +181,13 @@ public function render(string $view, ?array $data = null): string
}

$this->layout = $originalLayout;
Yii::app()->setLanguage($originalLocale);

throw $e;
}

$this->layout = $originalLayout;
Yii::app()->setLanguage($originalLocale);

return $content;
}
Expand All @@ -204,10 +212,7 @@ public function renderPartial(string $view, ?array $data = null, bool $return =
$viewFile = $this->getViewFile($view);

if ($viewFile === false) {
throw new \InvalidArgumentException(Yii::t('yii', '{controller} cannot find the requested view "{view}".', [
'{controller}' => get_class($this),
'{view}' => $view,
]));
throw new \InvalidArgumentException(get_class($this) . ' cannot find the requested view "' . $view . '".');
}

return $this->renderFile($viewFile, $data, $return);
Expand Down
Loading

0 comments on commit 324ba56

Please sign in to comment.