Installation:
composer require astonishdesign/mandrill-bundle
Enable the bundle in config/bundles.php:
return [
// ...
AstonishDesign\MandrillBundle\AstonishMandrillBundle::class => ['all' => true],
];
Configuration:
Add Mandrill API key and defaults to config/packages/astonish_mandrill.yaml:
astonish_mandrill:
api_key: '%env(MANDRILL_API_KEY)%' # Use env vars in production
disable_delivery: true # Set to false in production
default:
sender: 'noreply@example.com'
sender_name: 'Your App'
subaccount: 'default' # Optional subaccount
First Email:
Inject the Dispatcher service and send a message:
use AstonishDesign\MandrillBundle\Message;
use AstonishDesign\MandrillBundle\Dispatcher;
public function sendWelcomeEmail(Dispatcher $dispatcher): void
{
$message = (new Message())
->addTo('user@example.com')
->setSubject('Welcome!')
->setHtml('<h1>Hello!</h1>');
$dispatcher->send($message);
}
Message Composition:
Chain methods on Message for clarity:
$message = (new Message())
->setFromEmail('support@example.com')
->setFromName('Support Team')
->addTo('user@example.com')
->addTo('backup@example.com') // Supports multiple recipients
->setSubject('Your Request Status')
->setHtml($this->twig->render('emails/request_status.html.twig'))
->setText($this->twig->render('emails/request_status.txt.twig'))
->setHeaders(['Reply-To' => 'reply@example.com']);
Templates & Reusability: Create a service to generate reusable email templates:
// src/Service/EmailTemplateService.php
class EmailTemplateService
{
public function createPasswordReset(Message $message, string $token): Message
{
return $message
->setSubject('Reset Your Password')
->setHtml($this->twig->render('emails/password_reset.html.twig', ['token' => $token]));
}
}
Event-Driven Emails:
Trigger emails from Symfony events (e.g., KernelEvents::TERMINATE):
// src/EventListener/EmailListener.php
class EmailListener
{
public function onKernelTerminate(KernelEvent $event, Dispatcher $dispatcher): void
{
if ($event->getRequest()->attributes->get('send_welcome_email')) {
$dispatcher->send($this->createWelcomeEmail());
}
}
}
Async Processing: Use Symfony Messenger to queue emails for background processing:
# config/packages/messenger.yaml
framework:
messenger:
transports:
mandrill: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'AstonishDesign\MandrillBundle\Message': mandrill
FOSUserBundle:
Override the mailer service in config/packages/fos_user.yaml:
fos_user:
service:
mailer: astonish_mandrill.fos_user.mailer
Twig Integration: Pass Twig environment to render dynamic content:
$message->setHtml($this->twig->render('emails/invoice.html.twig', ['invoice' => $invoice]));
Testing:
Mock the Dispatcher service in tests:
$dispatcher = $this->createMock(Dispatcher::class);
$dispatcher->expects($this->once())
->method('send')
->with($this->isInstanceOf(Message::class));
API Key Exposure:
config.yml leaks credentials.%env(MANDRILL_API_KEY)%) and .env files.
# config/packages/astonish_mandrill.yaml
astonish_mandrill:
api_key: '%env(MANDRILL_API_KEY)%'
Disable Delivery in Production:
disable_delivery: false in production.astonish_mandrill:
disable_delivery: '%kernel.debug%' # Auto-disables in dev
Recipient Validation:
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email address');
}
Rate Limits:
Dispatcher or use a queue.Response Handling:
Mandrill returns an array with a status key. Check for errors:
$result = $dispatcher->send($message);
if ($result['status'] !== 'sent') {
throw new \RuntimeException('Email failed: ' . $result['_substatus']);
}
Logging:
Enable logging in config/packages/monolog.yaml:
services:
AstonishDesign\MandrillBundle\Dispatcher:
calls:
- [setLogger, ['@monolog.logger.mandrill']]
API Debugging: Use Mandrill’s webhooks to debug failed sends:
astonish_mandrill:
webhook_url: 'https://your-app.com/mandrill/webhook'
Custom Message Classes:
Extend Message to add domain-specific methods:
class InvoiceMessage extends Message
{
public function setInvoice(Invoice $invoice): self
{
return $this
->setSubject("Invoice #{$invoice->getNumber()}")
->setHtml($this->twig->render('emails/invoice.html.twig', ['invoice' => $invoice]));
}
}
Event Subscribers:
Listen to MandrillEvents::SEND to modify messages:
use AstonishDesign\MandrillBundle\Event\MandrillEvent;
public function onSend(MandrillEvent $event): void
{
$message = $event->getMessage();
$message->setHeader('X-Custom-ID', uniqid());
}
Transport Layer:
Override the default MandrillTransport to add retries or logging:
// src/Mandrill/MandrillTransport.php
class CustomMandrillTransport extends MandrillTransport
{
public function send(Message $message): array
{
$this->logger->info('Sending email to ' . $message->getTo());
return parent::send($message);
}
}
Testing with Fake Transport: Replace the transport in tests:
// tests/Service/MandrillTest.php
$container->set('astonish_mandrill.transport', $this->createMock(MandrillTransport::class));
How can I help you explore Laravel packages today?