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 znacznikimage
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" />
Fajny plugin, oszczędził mi trochę pracy i poszukiwań. Dzięki!