How to Send Emails in Symfony with Examples

On March 07, 2024
15min read
Ivan Djuric, an author at Mailtrap
Ivan Djuric Technical Content Writer @Mailtrap
Diana Lepilkina Content Specialist @Mailtrap

I’ve been trying to send emails in Symfony for the past few weeks, and I’ve finally nailed it.

So, I decided to write a comprehensive “Symfony send email” article with code examples you can re-use.

First, I’ll configure the new Mailer and Mime components, which were introduced in the Symfony 4.3. release. Thanks to these two components, Symfony now has an internal email-sending system with a wide set of options, including:

  • CSS inliner
  • Twig templates
  • File attachments
  • Signing and encrypting messages
  • Direct integration with the most popular email-sending providers
  • Asynchronous email sending with Symfony Messenger

Then, I’ll show you how I configured Mailtrap, so you can send emails with either SMTP or API.

Let’s begin!

Note: In this tutorial, I’ll use Symfony 7.0.

Setting up Symfony Mailer

To create and send messages in Symfony, we first need to install the Mime and Mailer components with the following command:

composer require symfony/mailer

Then, to create a message, I autowired the mailer with the MailerInterface, specified the components I was going to use, and created an Email object.

To send an email, you can go to “http://<your-app-url>/email” or use “http://127.0.0.1:8000/email” in development.

The route accessible from the 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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email());

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

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

Additionally, you can play around with Mailer, as it’s straightforward and gives you room for flexibility and experimentation. For example, you can set email addresses in several ways because 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 Address('mailtrap@example.com', 'Mailtrap'))

You can use the same principles to add recipients with Cc, Bcc, ReplyTo, and multiple addresses, like so:

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

If you’re interested in more details and alternatives, refer to the corresponding section in the Symfony Mailer documentation.

How to send email using SMTP

To start sending emails, you need to set up your SMTP configuration. 

Of course, for my sending purposes, I used the Mailtrap email delivery platform. It was an obvious choice as it offers higher deliverability rates than other options on the market, plus it’s easy to set up.

First, register an account and verify your email-sending domain. 

Then, go to the API and SMTP tab in the Sending Domains section, where you’ll find the settings you need to send emails.

Mailtrap SMTP/API Settings

Here, as you can see, you have two options: Transactional and Bulk streams. For this chapter, we’ll use the Transactional Stream, and I’ll cover sending Bulk Stream later. 

If you’ve successfully installed Mailer and Mime components, you should be able to find them in the .env file. There, you need to add the Transactional Stream credentials provided by Mailtrap in the MAILER_DSN section.

It should look something like this:

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

Once you’ve configured Symfony Mailer and Mailtrap SMTP, you can test the email-sending functionality with the mailer:test command:

php bin/console mailer:test mailtrap.club@gmail.com --from=no - reply@mailtrap.club

Note:

  • Make sure the domain of the sender’s email address matches the domain you register with Mailtrap.

How to send email to multiple recipients

For sending email to multiple recipients, I used the following code:

// 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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('mailtrap@example.com')
            ->to('newuser@example.com') // Primary recipient
            ->cc('mailtrapqa@example.com') // CC recipient
            ->bcc('mailtrapdev@example.com') // BCC recipient
            ->replyTo('mailtrap@example.com') // Reply-To address
            ->subject('Your subject here')
            ->text('This is the text version of the email.')
            ->html('<p>This is the HTML version of the email.</p>');

        $mailer->send($email);

        return new Response('Email was sent successfully.');
    }
}

Send email with attachments

To attach files to email, I used the attachFromPath() method to include files hosted locally or from an external link and attach() for reading files from a stream.

Check it out:

$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'));

When sending emails with attachments, make sure that the allow_url_fopen setting is enabled in your PHP configuration, as it’s necessary for Symfony to read files from URLs.

Send HTML email

To send HTML, simply add the HTML part directly to the email object:

$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>
       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>');

Note:

  • Don’t forget to also include the text part for clients that don’t support HTML emails.

Twig templates

Additionally, you can use Twig templating, which is integrated with the Mime component and offers you a wide set of options, including CSS inlining and direct integration with HTML/CSS frameworks.

Once you create a template and save it as a .twig file, instruct the Mime component to render email content from that file with the TemplatedEmail class:

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');

I found Twig templates useful 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

For more details on creating email templates with Twig, refer to the Symfony documentation: Twig section and Templates section.

Sending email with an embedded image

In Symfony Mailer, you can either embed images with direct embedding using embed argument or CID attachment. 

If you go with the CID route, simply refer to your image in the message body by setting its Content-ID and using a standard HTML tag.

Here’s an example:

$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"> ...');

Just make sure that paths and IDs match your actual files and identifiers.

Asynchronous email sending

To integrate asynchronous email sending, we will need Symfony Messenger, which you can add using Composer with the following command:

composer require symfony/messenger

Then, define a transport in config/packages/messenger.yaml that will be used to queue your email messages. For this, you can use various backends (e.g., Doctrine, Redis, RabbitMQ, etc.).

As an example, we’ll use Doctrine database as the transport:

framework:
  messenger:
    transports:
      async: '%env(MESSENGER_TRANSPORT_DSN)%'
    routing:
      'Symfony\Component\Mailer\Messenger\SendEmailMessage': async

Once you define a transport, set the MESSENGER_TRANSPORT_DSN in your .env file to use Doctrine, like so:

MESSENGER_TRANSPORT_DSN=doctrine://default

Note:

  • If you want to use Redis, simply modify the line in your .env file for the Messenger transport DSN to this:
MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages_queue
  • If you want to use RabbitMq, change it to this:
MESSENGER_TRANSPORT_DSN=amqp://user:password@localhost:5672/%2f/messages_queue

Here’s an example of how you can modify sendEmail method in your MailerController:

use Symfony\Component\Mailer\Messenger\SendEmailMessage;
use Symfony\Component\Messenger\MessageBusInterface;

class MailerController extends AbstractController
{
    private $bus;

    public function __construct(MessageBusInterface $bus)
    {
        $this->bus = $bus;
    }

    #[Route('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('mailtrap@example.com')
            ->to('newuser@example.com')
            // Additional email configuration...
            ;

        // Dispatch the email message for asynchronous sending
        $this->bus->dispatch(new SendEmailMessage($email));

        return new Response('Email sending process initiated.');
    }
}

But, to actually send the emails, run a worker that listens to the queue and processes the messages with the following command:

php bin/console messenger:consume async

How to send bulk email

Mailtrap also helped me send bulk emails in Symfony as it offers Bulk Stream, which you can find in the SMTP/API Settings tab when you log into your account.

Mailtrap Bulk Stream

First, I replaced my transactional email Mailtrap credentials with the Bulk Stream ones in the MAILER_DSN method.

Then, I created the EmailSender service that can send bulk email, which looks like this:

// src/Service/EmailSender.php

namespace App\Service;

use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;

class EmailSender
{
    private $mailer;

    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * Send bulk emails using a specified template.
     *
     * @param array $recipients List of email addresses to send to
     * @param string $subject Email subject
     * @param string $template Path to the Twig email template
     * @param array $context Variables to be passed to the email template
     * @throws TransportExceptionInterface
     */
    public function sendBulkEmail(array $recipients, string $subject, string $template, array $context): void
    {
        foreach ($recipients as $recipient) {
            $email = (new TemplatedEmail())
                ->from(new Address('sender@example.com', 'Sender Name')) // Adjust the sender address
                ->to($recipient)
                ->subject($subject)
                ->htmlTemplate($template)
                ->context($context);

            $this->mailer->send($email);
        }
    }
}

How to send email using API

If you haven’t already, register an account and verify your email sending domain name.

Navigate to the API and SMTP tab in the Sending Domains section. There, you will find your API key/token.

Mailtrap API Settings

Now, another benefit of Mailtrap I haven’t mentioned yet is that the platform offers a PHP client, which allows you to easily integrate Mailtrap with your Symfony-based application.

As you can see in the screenshot, I again used the Transactional API. So, simply click on API and store your token credentials.

Then, you need to add MailtrapTransport into your config/service.yaml file:

...
    # add more service definitions when explicit configuration is needed
    # please note that last definitions always *replace* previous ones

    Mailtrap\Bridge\Transport\MailtrapTransportFactory:
        tags:
            - { name: 'mailer.transport_factory' }

And lastly, simply copy your credentials inside your .env file and copy your API key into the same block:

MAILER_DSN=mailtrap+api://YOUR_API_KEY_HERE@default

# or

MAILER_DSN=mailtrap+api://YOUR_API_KEY_HERE@send.api.mailtrap.io

To test:

php bin/console mailer:test to@example.com

Note: Make sure that the email matches with the registered domain email.

For more information, check out the official documentation for Symfony framework bridge on GitHub.

How to send email to multiple recipients

Here’s a code example of how I managed to send email to multiple recipients with a Mailtrap API configuration:

// 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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('mailtrap@example.com')
            ->to('newuser@example.com') // Primary recipient
            ->cc('mailtrapqa@example.com') // CC recipient
            ->bcc('mailtrapdev@example.com') // BCC recipient
            ->replyTo('mailtrap@example.com') // Reply-To address
            ->subject('Your subject here')
            ->text('This is the text version of the email.')
            ->html('<p>This is the HTML version of the email.</p>');

        $mailer->send($email);

        return new Response('Email was sent successfully.');
    }
}

Send email with attachments

Similar to sending attachments with Mailtrap SMTP configuration, with the attachFromPath() method, I included files hosted locally or from an external link and used attach() for reading files from a stream.

Check it out:

// 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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('sender@example.com')
            ->to('recipient@example.com')
            ->subject('Here is your PDF')
            ->text('This email contains an attached PDF.')
            ->html('<p>This email contains an attached PDF.</p>')
            // Attach local file
            ->attachFromPath('/path/to/yourconfirmation.pdf')
            // Attach file from external URL
            ->attachFromPath('http://mailtrap.io/path/to/yourconfirmation', 'Your Confirmation', 'application/msword')
            // Attach file from PHP resource
            ->attach(fopen('/path/to/yourconfirmation.pdf', 'r'), 'confirmation.pdf');

        $mailer->send($email);

        return new Response('Email with attachments sent successfully.');
    }
}

Of course, don’t forget to ensure the allow_url_fopen setting is enabled in your PHP configuration, without which Symfony won’t be able to read files from URLs.

Send HTML email

To send HTML email, all I did was add the HTML part directly to the email object, like so:

$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>
       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>');

Twig templates

As with SMTP, you can use Twig templates if you go with the API route.

Simply create a template, save it as a .twig file, and instruct the Mime component to render email content from that file. For this, you can use the TemplatedEmail class:

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');

Sending email with embedded images

Sending email with embedded images with an API configuration is similar to sending them through SMTP.

You can either do it with direct embedding using embed argument or CID attachment. Just don’t forget to check whether the paths and IDs match the actual files and identifiers.

$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"> ...');

Asynchronous email sending

To integrate asynchronous communication, we will again have to use Symfony Messenger and Doctrine as an example.

You can use this command to install Symfony Messenger:

composer require symfony/messenger

Then, let’s define a transport in config/packages/messenger.yaml that will be used to queue your email messages.

Again, we’ll be using Doctrine:

framework:
  messenger:
    transports:
      async: '%env(MESSENGER_TRANSPORT_DSN)%'
    routing:
      'Symfony\Component\Mailer\Messenger\SendEmailMessage': async

Once you define a transport, set the MESSENGER_TRANSPORT_DSN IN YOUR .env file to use Doctrine, like so:

MESSENGER_TRANSPORT_DSN=doctrine://default

Check out an example of how you can modify sendEmail method in your MailerController:

use Symfony\Component\Mailer\Messenger\SendEmailMessage;
use Symfony\Component\Messenger\MessageBusInterface;

class MailerController extends AbstractController
{
    private $bus;

    public function __construct(MessageBusInterface $bus)
    {
        $this->bus = $bus;
    }

    #[Route('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('mailtrap@example.com')
            ->to('newuser@example.com')
            // Additional email configuration...
            ;

        // Dispatch the email message for asynchronous sending
        $this->bus->dispatch(new SendEmailMessage($email));

        return new Response('Email sending process initiated.');
    }
}

Lastly, to send a message, you will need to run a worker that listens to the queue and processes the messages with the following command:

php bin/console messenger:consume async

How to send bulk emails

To send bulk emails with Mailtrap API configuration, you first need to navigate to the SMTP/API Settings tab in your account and select API in the Bulk Stream window.

There, you will find the host and API Token, which you will replace in your .env file.

Here’s what your .env file should look like:

// src/Service/MailtrapBulkEmailSender.php

namespace App\Service;

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Psr\Log\LoggerInterface;

class MailtrapBulkEmailSender
{
    private $apiKey;
    private $httpClient;
    private $logger; // Add a logger

    public function __construct(string $apiKey, HttpClientInterface $httpClient = null, LoggerInterface $logger)
    {
        $this->apiKey = $apiKey;
        $this->httpClient = $httpClient ?: HttpClient::create();
        $this->logger = $logger; // Initialize logger
    }

    public function sendBulkEmail(array $recipients, string $subject, string $htmlContent, string $textContent): void
    {
        $payload = [
            'subject' => $subject,
            'html' => $htmlContent,
            'text' => $textContent,
            'to' => $recipients,
        ];

        try {
            $response = $this->httpClient->request('POST', 'https://mailtrap.io/api/bulk_send', [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'Api-Token' => $this->apiKey,
                ],
                'json' => $payload,
            ]);

            if ($response->getStatusCode() >= 400) {
                // This throws an exception if the status code is 4xx or 5xx
                throw new \Exception("Error Sending Email: " . $response->getContent(false));
            }

            // Optionally, log successful sends or additional info here
            $this->logger->info('Bulk email sent successfully', ['response' => $response->toArray()]);
            
        } catch (\Exception $e) {
            // Log the error
            $this->logger->error('Failed to send bulk email', [
                'exception' => $e->getMessage(),
                'payload' => $payload
            ]);
            // Depending on your application's needs, you might want to throw the exception or handle it gracefully
        }
    }
}

And as with an SMTP configuration, you can use Twig templates:

{# templates/emails/bulk_email.html.twig #}

<!DOCTYPE html>
<html>
<head>
    <title>{{ subject }}</title>
</head>
<body>
    <h1>Hello, {{ name }}!</h1>
    <p>This is a bulk email sent from our Symfony application.</p>
</body>
</html>

Important functionality and troubleshooting

  • Firewall and network – If you’re encountering connectivity issues, make sure that your firewall or network policies aren’t blocking outgoing connections on the port used by Mailtrap.
  • Sending secure messages – Symfony lets you both sign and encrypt emails with the S/MIME standard. You only need a valid S/MIME certificate and the configured OpenSSL PHP extension. 
  • SSL/TLS connection – To ensure your data is encrypted during transmission, specify encryption=ssl/tls in the .env configuration for Mailtrap. For example:
MAILER_DSN=smtp://username:password@smtp.mailtrap.io:2525/?encryption=ssl&auth_mode=login

Test emails before sending

Now that we have everything in place and set up, it’s time to test if everything works as intended because whether you’re using an SMTP or an API configuration to send emails, many things can go wrong. 

For example, your HTML might not render properly by web browsers, or your personalization variables may be off. Moreover, your emails might be skipping inboxes or getting marked as spam.

To avoid these issues and improve your email deliverability, you can use a platform like Mailtrap Email Testing, which can help you inspect and debug your emails in a safe environment.

Mailtrap Email Testing helps you catch traffic from staging and dev environments and then preview them and analyze their HTML/CSS before you send them out to your recipients.

Mailtrap HTML check

You can also check the spam score of your emails before you send them, which, if you keep below 5, can solve many potential deliverability issues.

Mailtrap Spam Report

Additionally, Mailtrap lets you easily create new projects, add multiple objects within, and share the testing process with your team members.

Mailtrap Multiple Inboxes

Lastly, Mailtrap Email Testing is quite easy to set up, and you can test emails with it regardless of whether Mailtrap is a sending solution or SendGrid, Amazon SES, or Mandrill (now Mailchimp Transactional Email).

SMTP

To start testing with a fake SMTP server, you first need to change your MAILER_DSN and update it with the Mailtrap Email Testing SMTP credentials, so it looks something like this:

MAILER_DSN=smtp://username:password@smtp.mailtrap.io:2525/?encryption=tls

Then, you can use the following code snippet, which includes both HTML and plain text parts, with a PDF file and an embedded image to test your email:

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

use Symfony\Component\Mime\Address;
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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer): Response
    {
        $email = (new Email())
            ->from(new Address('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>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);

        return new Response('Email sent successfully.');
    }
}

In the following example, I used the Mailer class to send the message and included both HTML and plain text parts, with a PDF file and an embedded image:

Then I checked the Mailtrap virtual inbox after a few moments and received the following message:

Mailtrap Best practices of building HTML emails

Success!

API

Now, if you want to integrate Mailtrap Email Testing API for testing automated sequences, you need to have a custom transport configured as described. Your setup should look like this:

MAILER_DSN=mailtrap+api://YOUR_API_KEY_HERE@default

Then again, you can use a code snippet with an HTML, PDF, and an embedded image:

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

use Symfony\Component\Mime\Address;
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('/mailer', name: 'app_mailer')]
    public function sendEmail(MailerInterface $mailer): Response
    {
        $email = (new Email())
            ->from(new Address('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>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);

        return new Response('Email sent successfully.');
    }
}

Pro Tip: You can use the Testing API for Testing and QA automation.

Wrapping up

And with that, we’ve come to the end of our ‘symfony send email’ article!

Regardless of whether you want to use SMTP or API configuration to send emails in Symfony, you can use this article as your go-to resource.

Interested in learning more? Our Full Stack developer, Dmitriy, has written an excellent guide on Sending Emails with Swift Mailer Library, which Symfony’s mailer feature was initially based on. 

Or, you can read other articles from our blog, such as:

Ivan Djuric, an author at Mailtrap
Article by Ivan Djuric Technical Content Writer @Mailtrap

I’m a Technical Content Writer with 5 years of background covering email-related topics in tight collaboration with software engineers and email marketers. I just love to research and share actionable insights with you about email sending, testing, deliverability improvements, and more. Happy to be your guide in the world of emails!

Article by Diana Lepilkina Content Specialist @Mailtrap