Ongiś pisanie maili w HTML-u było naruszeniem netykiety, ale obecnie zdecydowana większość odbiorców jak i nadawców woli otrzymywać maile w HTML-u. Generowanie i wysyłanie takowych w aplikacji opartej na Symfony2 jest banalnie proste dzięki Twigowi i Swift Mailerowi. Nieco mniej przyjemnie zaczyna się robić, kiedy w treści maili ma pojawić się grafika.
Możliwości są dwie i każda rodzi pewne problemy:
- Linkowanie do plików na zdalnym serwerze. Niestety funkcja
url()
w Twigu akceptuje tylko ścieżki zdefiniowane w routingu, nie można nią linkować do grafik. Z kolei znacznik image
obsługiwany przez Assetica domyślnie generuje relatywne URL-e, a poza tym jest trochę nieporęczny w użyciu.
- Osadzenie plików w wiadomości. Tu problem natury programistycznej polega na tym, że żeby osadzić grafikę w wiadomości trzeba skorzystać z metody
Swift_Message::embed()
, do której nie mamy dostępu z poziomu Twiga.
Wszystkie powyższe problemy można rozwiązać tworząc rozszerzenie do Twiga.
namespace MDurys\MailBundle\Twig;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MailExtension extends \Twig_Extension
{
private $kernel;
private $host;
private $imageCache;
private $messageCache;
public function __construct(ContainerInterface $container)
{
$context = $container->get('router')->getContext();
$this->host = $context->getScheme() . '://' . $context->getHost();
$this->kernel = $container->get('kernel');
}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('mail_embed', array($this, 'mailEmbed')),
new \Twig_SimpleFunction('mail_url', array($this, 'mailUrl')),
);
}
public function mailEmbed(\Swift_Message $message, $file)
{
if (!isset($this->messageCache[$message->getId()][$file]))
{
if (!isset($this->imageCache[$file]))
{
$this->imageCache[$file] = \Swift_Image::fromPath($this->kernel->locateResource($file));
}
$this->messageCache[$message->getId()][$file] = $message->embed($this->imageCache[$file]);
}
return $this->messageCache[$message->getId()][$file];
}
public function mailUrl($file)
{
return $this->host . $file;
}
public function getName()
{
return 'mdurys_mail_extension';
}
}
Rozszerzenie udostępnia dwie funkcje Twiga: mail_embed()
i mail_url()
, które odpowiednio umożliwiają osadzenie obrazka w wiadomości i wygenerowanie abosultnego URL-a do zasobu. Osadzane obrazki są cacheowane, więc jeśli dany obrazek jest wykorzystywany kilka razy w danej wiadomości to zostanie osadzony tylko raz, a jeśli obrazek zostanie osadzony w kilku wiadomościach to proces konwersji na obiekt Swift_Image
zostanie wykonany tylko raz.
Aby móc z tego rozszerzenia korzystać należy je uaktywnić w services.yml
bundla. Ważne jest, żeby otagować usługę jako „twig.extension”.
services:
mdurys.twig.mail_extension:
class: MDurys\MailBundle\Twig\MailExtension
arguments: [ "@service_container" ]
tags:
- { name: twig.extension }
Jeśli chcemy osadzić obrazek w wiadomości to podczas renderowania szablonu trzeba przekazać obiekt wiadomości.
$message = \Swift_Message::newInstance();
$htmlPart = $this->renderView(
'MailBundle:Test:email.html.twig',
array('message' => $message)
);
$message
->setFrom('mail@example.com')
->setTo('test@fexample.com')
->setSubject('Mail z obrazkami')
->setBody($htmlPart, 'text/html');
$this->get('mailer')->send($message);
W szablonie Twiga wystarczy wywołać funkcję mail_embed()
przekazując jej jako parametry obiekt wiadomości i lokalizację pliku.
<img src="{{ mail_embed(message, '@MDurysMailBundle/Resources/public/images/smiley.gif') }}" width="16" height="16" alt=":-)" />
Linkowanie do zewnętrznych zasobów jest prostsze i sprowadza się do wywołania funkcji mail_url()
w szablonie:
<img src="{{ mail_url('/bundles/mdurysmail/images/big_logo.jpg') }}" width="600" height="240" alt="Big Logo" />