Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony Mime / Mailer

Symfony Mime / Mailer

Introducing the new Symfony Mime and Symfony Mailer components.

Avatar for Fabien Potencier

Fabien Potencier

March 28, 2019
Tweet

More Decks by Fabien Potencier

Other Decks in Programming

Transcript

  1. use Symfony\Component\Mime\Email; $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('Some subject')

    ->text('Some text message') ->html('<b>Some HTML message</b>') ->attach('doc.txt') ; The basics
  2. $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('Some subject') ->text('Some text

    message') ->html('<b>Some HTML message</b>') ; echo strlen(serialize($email)); Email is a data object Only 2k vs 16k for Swiftmailer
  3. $email = (new Email()) ->text('Some text message') ->html('<b>Some HTML message</b>')

    ; $email = (new \Swift_Message()) ->setBody('Some text message') ->addPart('<b>Some HTML message</b>', 'text/html') ; A better data object model 16k serialized 38 objects complex serialization "fixed" headers 2k serialized 7 objects simple serialization "dynamic" headers
  4. $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('Some subject') ->text(fopen('email.txt', 'r'))

    ->html(fopen('email.html', 'r')) ; echo $email->toString(); foreach ($email->toIterable() as $chunk) { echo $chunk; } String or resources, your choice
  5. // Email extends Message use Symfony\Component\Mime\Message; use Symfony\Component\Mime\Part\TextPart; use Symfony\Component\Mime\Header\Headers;

    $body = new TextPart('Some content'); $headers = (new Headers()) ->addMailboxListHeader('From', ['[email protected]']) ->addMailboxListHeader('To', ['[email protected]']) ->AddTextHeader('Subject', 'Some subject') ; $email = new Message($headers, $body); And you get full control!
  6. $txt = new TextPart('Some content'); $html = new TextPart('<b>Some content</b>',

    'html'); $body = new AlternativePart($txt, $html); $email = new Message($headers, $body); Get creative
  7. A "complete" email multipart/mixed | |------------> multipart/related | | |

    |------------> multipart/alternative | | | | | ------------> text/plain | | | | | ------------> text/html | | | ------------> image/png | ------------> application/pdf ->text() ->html() ->embed() ->attach()
  8. $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('Some subject') ->text('Some text')

    ->html('<b>The new logo: <img src="cid:logo.jpg"></b>') ->embedFromPath('logo-small.jpg', 'logo.jpg') ; Embeds
  9. // Email extends Message extends RawMessage use Symfony\Component\Mime\RawMessage; $message =

    new RawMessage($email->toString()); $message->toString(); Go raw! Serialize an email as a string instead of a PHP object
  10. Creating Emails with Twig Twig is perfect for emails aka,

    Twig is not dead and still very useful :)
  11. use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Twig\Environment; use Twig\Loader\FilesystemLoader; use Symfony\Component\Mime\NamedAddress;

    $twig = new Environment($loader = new FilesystemLoader(__DIR__.'/templates')); $loader->addPath(__DIR__.'/images', 'images'); $email = (new TemplatedEmail()) ->from('[email protected]') ->to(new NamedAddress('[email protected]', 'Fabien')) ->text('Some text content') ->htmlTemplate('simple.html.twig') ->context([ 'city' => 'Lille' ]) ; $renderer = new BodyRenderer($twig); $renderer->render($email); echo $email->toString(); Native integration with Twig
  12. <p> Welcome <b>{{ email.toName }}</b> from {{ city }}! </p>

    <p> <img src="{{ email.image('@images/photo.jpg') }}"> </p> Native integration with Twig Symfony\Bridge\Twig\Mime\ WrappedTemplatedEmail Twig template name Template context
  13. {% do email.attach('@docs/doc.pdf') %} <p> Welcome <b>{{ email.toName }}</b>! </p>

    <p> <img src="{{ email.image('@images/photo.jpg') }}"> </p> Native integration with Twig
  14. {% do email.priority(5) %} <p> Welcome <b>{{ email.toName }}</b>! </p>

    <p> <img src="{{ email.image('@images/photo.jpg') }}"> </p> Native integration with Twig
  15. {% block config %} {% do email.attach('@images/planfaidherbe.jpeg') %} {% endblock

    %} {% block subject %}Email Subject{% endblock %} {% block text %} Optional text representation {% endblock %} {% block html %} <p>Welcome <b>{{ email.toName }}</b>!</p> <p><img src="{{ email.image('@images/photo.jpg') }}"></p> {% endblock %} Native integration with Twig
  16. {% block text %} {% set formula = "2 >

    1?" %} {% autoescape false %} {{ formula }} {% endautoescape %} {% endblock %} {% block html %} {% set formula = "2 > 1?" %} {{ formula }} {% endblock %} Native integration with Twig HTML escape by default with the "name" strategy From: [email protected] To: Fabien <[email protected]> MIME-Version: 1.0 Date: Thu, 28 Feb 2019 12:10:16 +0100 Message-ID: <[email protected]> Content-Type: multipart/alternative; boundary="_=_symfony_1551352216_9df84f01f42af586d65b48578466206f_= --_=_symfony_1551352216_9df84f01f42af586d65b48578466206f_=_ Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable =20 2 > 1? =20 --_=_symfony_1551352216_9df84f01f42af586d65b48578466206f_=_ Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable =20 2 &gt; 1? --_=_symfony_1551352216_9df84f01f42af586d65b48578466206f_=_--
  17. use Twig\CssInliner\CssInlinerExtension; $twig->addExtension(new CssInlinerExtension()); {% filter inline_css %} <style> b

    { color: red } </style> <p> Welcome <b>{{ email.toName }}</b>! </p> {% endfilter %} Inlining CSS
  18. {% filter inline_css("@css/email.css") %} <style> b { color: red }

    </style> <p> Welcome <b>{{ email.toName }}</b>! </p> {% endfilter %} Inlining CSS
  19. use Twig\Markdown\MarkdownExtension; $twig->addExtension(new MarkdownExtension()); <p> {% filter markdown %} |

    Version | LTS? | Latest | | ------------- |:-------------:| -------:| | 1.0 | Yes | 1.0.1 | | 2.1 | No | 2.1.33 | {% endfilter %} </p> Use Markdown to simplify your templates
  20. {% filter inky|inline_css(source("@zurb/stylesheets/main.css")) %} <container> <row class="header"> <columns> <spacer size="16"></spacer>

    <h4 class="text-center">Symfony Connect</h4> </columns> </row> <row> <columns> <spacer size="32"></spacer> <center><img width="100px" src="{{ email.image("@images/symfony.png") }}"></center> <spacer size="16"></spacer> <h1 class="text-center">Forgot Your Password?</h1> <spacer size="16"></spacer> <p class="text-center">It happens. Click the link below to reset it.</p> <button class="large expand" href="#">Reset Password</button> <hr/> <p><small><center><a href="#">unsubscribe here</a>.</small></center></p> </columns> </row> </container> {% endfilter %} Use Inky to simplify your HTML
  21. Unified behaviour across all providers Same events for all transports

    Unified DSNs / one env var MAILER_DSN Easily switch from SMTP in dev to a "real" provider in prod / same API Same message interface and serialization
  22. SMTP / HTTP / API? SMTP HTTP API Offline Yes

    via Mailcatcher No / Mock No / Mock Mailcatcher Yes No No Standard Yes No No Symfony generated Yes Yes No Fast No Yes Yes
  23. $mailer = new Mailer($transport, $bus); Sync if $bus is null

    OR bus not configured Async is $bus is configured for EnvelopedMessage Sync or Async, your choice
  24. public function email(MailerInterface $mailer) { $email = (new Email())->…; $mailer->send($email);

    return new Response('Sent... eventually'); } Sync or Async, your choice
  25. public function email(MailerInterface $mailer) { $email = (new Email())->…; $mailer->send($email,

    new SmtpEnvelope(...)); return new Response('Sent... eventually'); } Sync or Async, your choice
  26. Emails are sent async via AMQP framework: messenger: routing: 'Symfony\Component\Mailer\EnvelopedMessage':

    amqp Emails are sent immediately framework: messenger: routing: #'Symfony\Component\Mailer\EnvelopedMessage': amqp Sync or Async, your choice
  27. class MessageHandler { private $transport; public function __construct(TransportInterface $transport) {

    $this->transport = $transport; } public function __invoke(EnvelopedMessage $message) { $this->transport->send($message->getMessage(), $message->getEnvelope()); } } <service id="mailer.messenger.message_handler" class="Symfony\Component\Mailer\Messenger\MessageHandler" public="false"> <argument type="service" id="mailer.transport" /> <tag name="messenger.message_handler" /> </service> Here is the magic that makes it work!
  28. * SentMessage instances * Only one event: MessageEvent * Built-in

    listeners: EnvelopeListener / MessageListener * Throttling / SMTP keep-alive More...
  29. Leverages
 the Symfony ecosystem Twig Encore Serializer Messenger PSR logger

    CSS selector Event Dispatcher Dependency Injection Container Symfony Polyfills (punycode, intl, …) … with all the great features you love in Swiftmailer 6 Symfony Mailer