symfony/mailer
Symfony Mailer is a flexible component for sending emails via SMTP and other transports. Compose rich messages with Symfony Mime, add CC/BCC and priorities, and optionally render Twig templates with TemplatedEmail and BodyRenderer.
Install the package:
composer require symfony/mailer
Laravel already includes Symfony Mailer as a dependency, so no additional installation is needed.
Configure .env:
Add your mail transport configuration (e.g., SMTP, Sendmail, or API-based):
MAIL_MAILER=smtp
MAIL_HOST=mail.example.com
MAIL_PORT=587
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
First Use Case: Send a simple email in a controller or service:
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mime\Email;
Mail::send(new Email()
->from('noreply@example.com')
->to('user@example.com')
->subject('Welcome!')
->text('Thanks for signing up!')
);
Where to Look First:
Mail facade (wraps Symfony Mailer).Mail::send(new Email()
->to('user@example.com')
->subject('Plain Text Email')
->text('This is a plain text email.')
);
Mail::send(new Email()
->to('user@example.com')
->subject('HTML Email')
->html('<h1>Hello!</h1><p>This is an HTML email.</p>')
);
Laravel’s Mailable classes leverage Twig under the hood. Example:
// app/Mail/WelcomeMail.php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class WelcomeMail extends Mailable
{
use Queueable, SerializesModels;
public $user;
public function __construct($user)
{
$this->user = $user;
}
public function build()
{
return $this->subject('Welcome!')
->view('emails.welcome', ['user' => $this->user]);
}
}
Send it via:
Mail::to('user@example.com')->send(new WelcomeMail($user));
$email = new Email()
->to('user@example.com')
->subject('Email with Attachments')
->text('Check the attachment!')
->attachFromPath('/path/to/file.pdf', 'filename.pdf')
->embedFromPath('/path/to/image.png', 'image_id');
$email->html('<img src="cid:image_id">');
Mail::send($email);
Configure in .env:
MAIL_MAILER=sendgrid
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=apikey
MAIL_PASSWORD=SG.your_api_key
Use Laravel’s Mail facade as usual—it abstracts the transport.
Laravel’s queue system works seamlessly with Symfony Mailer:
Mail::to('user@example.com')->queue(new WelcomeMail($user));
Configure queue in .env:
QUEUE_CONNECTION=database
Symfony Mailer supports event listeners for logging or modifying emails:
// app/Providers/AppServiceProvider.php
use Symfony\Component\Mailer\EventListener\MessageListener;
use Symfony\Component\Mime\Email;
public function boot()
{
Mail::getSwiftMailer()->registerPlugin(new MessageListener(
function (Email $email) {
// Log or modify the email before sending
\Log::info('Sending email to: ' . $email->getTo()[0]);
}
));
}
Use Laravel’s MailFake for testing:
use Illuminate\Mail\Testing\Mails;
use Illuminate\Support\Facades\Mail;
public function test_welcome_email()
{
Mail::fake();
$this->post('/register', ['email' => 'user@example.com']);
Mail::assertSent(WelcomeMail::class, function ($mail) {
return $mail->hasTo('user@example.com');
});
}
DSN Configuration:
Ensure your .env DSN is correct. For SMTP:
MAIL_MAILER=smtp
MAILER_DSN=smtp://user:pass@smtp.example.com:587?encryption=tls
For API-based transports (e.g., SendGrid):
MAILER_DSN=sendgrid://apikey:SG.key@example.com
Gotcha: Forgetting ?encryption=tls or ?encryption=ssl can cause connection failures.
TLS/SSL Issues: If emails fail silently, check:
587 (TLS) or 465 (SSL).telnet or openssl to test connectivity:openssl s_client -connect smtp.example.com:587 -starttls smtp
HTML vs. Text Parts:
Always include a text part for emails. Some clients (e.g., Outlook) ignore HTML if no plain text is provided.
Example:
$email->text('Plain text fallback')->html('<h1>HTML</h1>');
Queueing Delays:
Emails sent via queues may not appear immediately in logs or tests. Use Mail::flush() to force delivery in tests:
Mail::fake();
Mail::to('user@example.com')->send(new WelcomeMail($user));
Mail::flush(); // Force delivery for testing
Attachment Size Limits: Some transports (e.g., SendGrid) have size limits (~20MB for SMTP). For larger files, use cloud storage (S3) and link to the file.
Enable Verbose Logging:
Add to .env:
MAIL_LOG_LEVEL=debug
Check logs in storage/logs/laravel.log.
Inspect Raw Emails:
Use Symfony’s RawMessage to debug:
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\RawMessage;
$mailer = app(MailerInterface::class);
$rawMessage = $mailer->createTransport()->getRawMessage();
\Log::info($rawMessage->getBody());
Test with Mailtrap:
Configure Mailtrap in .env:
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
Mailtrap provides a sandbox for testing without sending real emails.
Batch Emails:
Use RoundRobinTransport to distribute emails across multiple transports:
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
$transport1 = Transport::fromDsn('smtp://user:pass@smtp1.example.com');
$transport2 = Transport::fromDsn('smtp://user:pass@smtp2.example.com');
$roundRobin = new RoundRobinTransport([$transport1, $transport2]);
$mailer = new Mailer($roundRobin);
Configure in Laravel via config/mail.php:
'transports' => [
'roundrobin' => [
'dsn' => 'roundrobin://smtp://user:pass@smtp1.example.com,smtp://user:pass@smtp2.example.com',
],
],
Avoid Blocking Calls: Always queue emails in long-running processes (e.g., commands, jobs):
Mail::to('user@example.com')->queue
How can I help you explore Laravel packages today?