Fully customizable emails using Laravel 9

Feb 1, 2023 by Zacharia Mansouri | 2858 views

PHP Laravel Cyber-Wise

https://cylab.be/blog/256/fully-customizable-emails-using-laravel-9

With the release of Laravel 9, the Swift Mailer (that is no longer maintained) has been replaced by the Symfony Mailer. You can already find some useful information about this change along all the other ones in the Upgrade Guide from Laravel 8.x to 9.0. However this guide does not contain enough information if you want to send fully customized emails. This blog post proposes you a solution coming directly from the Symfony documentation!

yannik-mika-GjFbKfI874o-unsplash.jpg

Preliminary information

In this blog post, the following email parameters are customizable:

  • Email sender:
    • $senderHost e.g. smtp.gmail.com
    • $senderPort e.g. 587 (SMTP encrypted communications)
    • $senderEncryption e.g. TLS
    • $senderUsername usually the same as $senderEmail or any username used when registering the e-mail address
    • $senderPassword e.g. password123
    • $senderEmail e.g. random.sender.address@gmail.com
    • $senderName e.g. My Company Name
  • Email content:
    • $emailSubject e.g. Registration Link
    • $emailBody the (HTML) body composing the content of the email
  • Email receiver:
    • $receiverEmail e.g. random.receiver.address@gmail.com

Driver prerequisites

As stated in the Upgrade Guide, you must install some new dependencies:

composer require symfony/mailgun-mailer symfony/http-client
composer require symfony/postmark-mailer symfony/http-client

Don't hesitate to consult the guide first, as you can consider this blog post as some kind of complement for the specific case of fully customizable emails.

Updating from Laravel 8 to 9

The changes between the ways $message, $transport and $mailer are created will be explained below the following pieces of code.

Old code (Laravel 8.x)

// Message
$message = (new \Swift_Message($emailSubject))
    ->setFrom($senderEmail, $senderName)
    ->setTo($receiverEmail)
    ->setBody($emailBody, 'text/html');

// Transport
$transport = (new \Swift_SmtpTransport($senderHost, $senderPort, $senderEncryption))
    ->setUsername($senderUsername)
    ->setPassword($senderPassword);

// Mailer
$mailer = new \Swift_Mailer($transport);
$mailer->send($message);

New code (Laravel 9.0)

// New imports
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;

// Message
$message = (new Email())
    ->subject($emailSubject)
    ->from(new Address($senderEmail, $senderName))
    ->to($receiverEmail)
    ->html($emailBody);

// Transport
$transport = Transport::fromDsn(
    'smtp://' . $senderUsername . ':' . $senderPassword . '@' . $senderHost . ':' . $senderPort
);

// Mailer
$mailer = new Mailer($transport);
$mailer->send($message);

Differences between the old and the new code

Imported classes

The following classes must be imported:

use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;

Message creation

Instead of building a $message object by instantiating a \Swift_Message object with the $emailSubject and calling the setFrom, setTo and setBody methods on it, we must now create an Email object and call the methods subject, from, to and html. Note that:

  • an Address object with the $senderEmail and the $senderName is instantiated in the from method,
  • we can send a text-only (not HTML) email by calling the text method instead of the html method.
  • the message creation does not change a lot...

Transport channel creation

Instead of building a $transport object by instanciating a \Swift_SmtpTransport object with the $senderHost, $senderPort, $senderEncryption and calling the setUsername and setPassword methods on it, we must now create the $transport object by calling the fromDsn method statically from the Transport class and passing an SMTP URL to it. Here is how that URL looks like if we use the preliminary information written at the beginning of this blog post:

smtp://random.sender.address@gmail.com:password123@smtp.gmail.com:587

Symfony gives you a direct access to the SMTP URL instead of providing you with methods to build it like Swift did.

Note that $senderEncryption does not appear in the new piece of code. As stated in the Symfony documentation, by default, SMTP transports perform TLS peer verification. If needed, you can check that documentation to learn how to disable it (pro-tip: just append ?verify_peer=0 to the URL, but it is not recommended).

Mailer creation and email sending

The only change is that the $mailer object is created by instantiating a Mailer object instead of the old \Swift_Mailer object.

Final words

By combining the information from the Upgrade Guide, the Symfony documentation and this blog post, you are now able to have a total control on the emails you want to send, no matter how many sending email addresses you want to use!

This blog post is licensed under CC BY-SA 4.0