How to Send Emails in Symfony with Examples

Symfony is another popular PHP framework and a great toolset for web development. Initially, its mailer feature was based on the Swift Mailer library, also widely used for sending emails from PHP applications. 

In the Symfony 4.3 release, the new Mailer component was introduced along with the Mime component. This way, Symfony now has an internal email sending system with a wide set of options:

  • direct integration with the most popular email sending providers 
  • asynchronous email sending
  • Twig templates 
  • CSS inliner
  • file attachments

The Mailer component was added to the 4.3 release as an experiment. If you want to test it, let’s do it together in this post. Swift Mailer is a validated option; if you prefer to use it, then follow our Sending emails with Swift Mailer tutorial.

We assume that you already work with Symfony and are acquainted with its main components and principles.

What we will be doing in this post with Symfony Mailer

  1. Install Mailer and Mime components.
  2. Define mail transport (SMTP) and set up Mailtrap for our email experiments.
  3. Review the code for creating a new message and sending it to several recipients.
  4. Add email content as pure HTML, with an embedded image, and a file attachment.
  5. Refer to the Twig templates and go over its main capabilities.
  6. Craft the full email template and send it to Mailtrap to check how it works.
  7. List the main sending options and their configurations (Gmail, Amazon SES, Sendgrid, etc.)

Symfony 4.3: Mailer component 

In Symfony 4.3, message creation and sending can be done with the help of two components: Mailer and Mime. Let’s start by installing them both:

composer require symfony/mailer

Similar to Swift Mailer, the next step is creating a Transport – defining the method of delivering your messages. SMTP is the easiest and most preferable option. And as usual, we will be running our experiments with Mailtrap, an online tool for email testing in pre-production environments. It will keep our test emails safe by catching and displaying  them in virtual inboxes. To start, you can create a free account in just three clicks. 

Go to your inbox in Mailtrap: you will need to copy your username and password and paste them to the .env file in your Symfony project as follows: 

MAILER_DSN=smtp://username:password@smtp.mailtrap.io:2525/?encryption=ssl&auth_mode=login

Note: in Swift Mailer the DSN format differs.

Mailer has libraries for such popular email sending providers as Gmail, Sendgrid, Amazon SES, and others. We will get back to them at the end of this post, as soon as we successfully complete our email testing. Then, we will be ready to send emails to the real inboxes. 

Define email parameters

In Symfony Mailer, email is a data object. To create a message, we will need to autowire the mailer with the MailerInterface, specify components we are going to use, and create an Email object. To send an email, go to “http://<your-app-url>/email” (in development, you use “http://127.0.0.1:8000/email”). The route accessible from web is created with the help of @Route:

// src/Controller/MailerController.php
namespace App\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

class MailerController extends AbstractController
{
    /**
     * @Route("/email")
     */
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())

//….
  $mailer->send($email);

        // …
      return new Response(
          'Email was sent'
       );
    }
}

Mailer is straightforward and gives you flexibility and options for experiments. You can set email addresses in several ways as both strings and addresses are supported.

To set from() address you can use either:

   // simple string
    ->from('mailtrap@example.com')

    // object
    ->from(new Address('mailtrap@example.com'))

    // name object
    ->from(new NamedAddress('mailtrap@example.com', 'Mailtrap'))

Use the same principles to add recipients. Cc, Bcc, ReplyTo, and multiple addresses are available to use:

->to('newuser@example.com')
->cc('mailtrapqa@example.com')
->addCc('staging@example.com')
->bcc('mailtrapdev@example.com')
->replyTo('mailtrap@example.com')

For more details and alternatives, refer to the corresponding section in the Symfony Mailer documentation

Build HTML email and add content

Symfony creators promote the crafting of email content with the help of Twig templates, another Symfony project. Twig is a template engine for PHP and indeed is a great option to use when creating beautiful emails. It can be customized and offers a list of integrations and extensions. For example, you can use it with the Foundation for Emails framework or create your templates with Markdown. Either way, you can use pure HTML. Let’s start with HTML and then explore Twig’s advanced options. 

Add the HTML part directly to the email object (and don’t forget to include the text part, as well): 

 $email = (new Email())
    // ...
    ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
    ->html(‘<html>
  <body>
    <p>Hey<br>
       Hey! Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
    <p><a href="https://blog.mailtrap.io/build-html-email/">Mailtrap’s Guide on How to Build HTML Email</a> is live on our blog</p>
  </body>
</html>')

Embed images

In Symfony Mailer, you have two main options: direct embedding with embed argument or CID attachment. (Refer to your image in the message body by setting its Content-ID and using a standard HTML tag.)

If you have used Swift Mailer before, you will notice that embedding images looks pretty similar:

$email = (new Email())
    // ...
    //image from a PHP resource, for example, GD
    ->embed(fopen('/path/to/newlogo.png', 'r'), 'logo')
    //image hosted locally or on some external resource
    ->embedFromPath('/path/to/newcover.png', 'new-cover-image')
    // CID attachment
    ->html('<img src="cid:logo"> ... <img src="cid:new-cover-image"> ...')

Attach files

The way you can attach files to your email is very similar to embedding images. 

Here, you have the attachFromPath() method to include files hosted locally or from an external link, as well as attach(), for reading files from a stream:

$email = (new Email())
    // …
// local file
    ->attachFromPath('/path/to/yourconfirmation.pdf')
// external URL - make sure that allow_url_fopen is enabled in your PHP installation
->attachFromPath('http://mailtrap.io/path/to/yourconfirmation', Your Confirmation', 'application/msword')
// PHP resource
   ->attach(fopen('/path/to/yourconfirmation.pdf', 'r'));

Twig templates

Let’s get back to Twig. Now, it’s integrated with the Mime component, which offers you a wider set of options, including CSS inlining and direct integration with HTML/CSS frameworks. 

We won’t dive into Twig templating here – just review its capabilities and give an example of rendering a Twig template in Symfony. For more details on creating email templates with Twig, refer to the Symfony documentation: Twig section and Templates section

Once you have created your template and saved it as a .twig file, instruct the Mime component to render email content from that file. Use the TemplatedEmail class for this purpose:

use Symfony\Bridge\Twig\Mime\TemplatedEmail;
$email = (new TemplatedEmail())
    ->from('mailtrap@example.com')
    ->to('alex@example.com', 'Alex'))
    ->subject('Experimenting with Symfony Mailer and Mailtrap')
    // path to your Twig template
    ->htmlTemplate('path/experiment.html.twig')
    ])
;

The current Twig templates are special because they offer support for other frameworks, which you can install as extensions:

  • MarkdownExtension for Markdown
  • InkyExtension for Foundation for Emails (earlier it was called Inky)
  • CssInlinerExtension for CSS inlining

Sending a message in Symfony

Above, we have examined all main Symfony Mailer functions and capabilities. Let’s build the whole message and test if everything works as intended with Mailtrap. We have already set the MAILER_DSN value in the .env file. To send a message, we need to use the Mailer class. 

In this example we are including both HTML and plain text parts, adding a PDF file, and embedding an image:

// src/Controller/MailerController.php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

/**
* @Route("/email")
* @param MailerInterface $mailer
* @return Response
*/
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
	    ->from(new NamedAddress('mailtrap@example.com', 'Mailtrap'))
		->to('newuser@example.com')
		->cc('mailtrapqa@example.com')
		->addCc('staging@example.com')
		->bcc('mailtrapdev@example.com')
		->replyTo('mailtrap@example.com')
		->subject('Best practices of building HTML emails')
   		 ->embed(fopen('/path/to/newlogo.png', 'r'), 'logo')
  ->embedFromPath('/path/to/newcover.png', 'new-cover-image')
 ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
     ->html('<html>
<body>
		<p><br>Hey</br>
		Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
		<p><a href="https://blog.mailtrap.io/build-html-email/">Mailtrap’s Guide on How to Build HTML Email</a> is live on our blog</p>
		<img src="cid:logo"> ... <img src="cid:new-cover-image">
				</body>
			</html>')
->attachFromPath('/path/to/offline-guide.pdf')
 
 $mailer->send($email);

In a few moments, we can check the message in the Mailtrap virtual inbox:

Now, after successful testing, it’s time to review the options for sending real emails from the production.

Sending a message via SMTP server

It is very easy to specify an external SMTP server to send your emails from a Symfony app. With the Mailer component, this task becomes even easier: there is built-in support for the popular ESPs. It means that you should install an appropriate component and then specify a corresponding transport (it can be used from the DSN as well) as follows:

ServiceInstallationSMTP credentials
Amazon SEScomposer require symfony/amazon-mailersmtp://ACCESS_KEY:SECRET_KEY@ses
Gmailcomposer require symfony/google-mailersmtp://USERNAME:PASSWORD@gmail
MailChimpcomposer require symfony/mailchimp-mailersmtp://USERNAME:PASSWORD@mandrill
Mailguncomposer require symfony/mailgun-mailersmtp://USERNAME:PASSWORD@mailgun
Postmarkcomposer require symfony/postmark-mailersmtp://ID:ID@postmark
SendGridcomposer require symfony/sendgrid-mailersmtp://apikey:KEY@sendgrid

You can also integrate all of these providers, except Gmail via API, and Amazon SES, Mandrill, and Mailgun via HTTP. 

Summary

The new Symfony Mailer looks promising and more flexible than Swift Mailer. In the early Symfony Mailer days, when it is still in its experimental mode, it is definitely worth trying if you are new to Symfony. However, If you have the email infrastructure already set up with Swift Mailer, then we don’t see any compelling reasons to switch now. 

If you want to know more about Symfony Mailer, you may be interested in checking out these slides by Fabien Potencier, the creator of Symfony. He presented the new Symfony components at the SymfonyLive conference in March 2019. 

In addition, the course on using this new Mailer component is announced on SymfonyCasts: Symfony Mailer: Love Sending Emails Again.