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](How to Send Emails in Symfony with Examples), 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
  • signing and encrypting messages
  • Twig templates
  • CSS inliner
  • file attachments

The Mailer component was added to the 4.3 release as an experiment. It has proved successful, and has been constantly improved and updated. In this post, we use Symfony 5.1.

If you would like to master sending messages with Swift Mailer, then follow our Sending emails with Swift Mailer guide.

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.)

Mailer component

In Symfony, 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:///email” (in development, you use “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="/blog/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. It’s integrated with the Mime component, which offers you a wide 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="/blog/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:

Image description

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:

To discover the table with SMTP credentials of Amazon SES, Gmail, MailChimp and more [check the article(mailtrap.io/blog/send-emails-in-symfony).

It is also possible to specify several sending providers: a main provider and alternatives. For more detailed information and code samples, refer to the Multiple Email Transports section of the Symfony documentation.

In a similar way, you can configure sending via Mailtrap Email API.

Besides catching and fixing sending issues, our Email API gives developers control over their email deliverability via actionable analytics features.

If you decide to integrate your PHP-based project with our Email API, the setup process is straightforward, once your domain name is set up:

  • From the Sending Domains tab, select API/SMTP Integration.
  • If you want to take the API route, select PHP from the drop-down menu.
  • Copy the example code and run it.
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://send.api.mailtrap.io/api/send',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS =>'{"from":{"email":"mailtrap@mailtrap.club","name":"Mailtrap Test"},"to":[{"email":"viktoriia.ivanenko@railsware.com"}],"subject":"You are awesome!","text":"Congrats for sending test email with Mailtrap!","category":"Integration Test"}',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Bearer c01b6a820e62f3a5d0608eaf73a57add',
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

Select SMTP if you want to take that route and copy the below credentials to your project:

Host: send.smtp.mailtrap.io Port: 587 (recommended), 2525 or 25 Username: api Password: Auth: PLAIN, LOGIN STARTTLS: Required

That’s it, you’re all set to send emails. Thanks to the scalable cloud-based architecture, your app can effortlessly send up to ~10,000 emails/second with the Email API.

Other important functionality

  1. Symfony provides options for sending secure messages — you are able to both sign and encrypt emails with the S/MIME standard. You only need to have a valid S/MIME certificate and the configured OpenSSL PHP extension.
  2. You can queue your emails with the Messenger component by configuring its transport.
  3. Version 5.1 added support for message tags and metadata. This gives you the ability to group and track your emails, build workflows and automations.

Summary

The new Symfony Mailer is more flexible than Swift Mailer, and provides a wider set of options. It has a modern look and is regularly updated. The list of supported integrations with both templating languages and email sending providers, along with security options, make it worth trying.

But you also have the option to send emails on production with Mailtrap Email API. It takes less than 5 minutes to verify your domain. And you get the SMTP credentials to get you started.

For more in-depth learning of Symfony Mailer, you can take the Symfony Mailer: Love Sending Emails Again course on SymfonyCasts. If you don’t have a subscription there, access is available for $12.