Install the Bundle
composer require badpixxel/brevo-bridge
Enable the bundle in config/bundles.php:
return [
// ...
BadPixxel\BrevoBridge\BrevoBridgeBundle::class => ['all' => true],
];
Configure Brevo API Key
Add your Brevo (Sendinblue) API key to .env:
BREVO_API_KEY=your_api_key_here
Publish the default config (if needed):
php bin/console config:dump-reference BadPixxel\BrevoBridgeBundle
Define a Static Email Class
Create a static class (e.g., src/Email/WelcomeEmail.php) extending BadPixxel\BrevoBridge\Email\AbstractEmail:
namespace App\Email;
use BadPixxel\BrevoBridge\Email\AbstractEmail;
class WelcomeEmail extends AbstractEmail
{
protected static $templateId = 'your_brevo_template_id';
protected static $subject = 'Welcome to Our App!';
}
Send Your First Email
Inject the BrevoBridge service and trigger the email:
use BadPixxel\BrevoBridge\BrevoBridge;
class UserController
{
public function __construct(private BrevoBridge $brevoBridge) {}
public function registerUser()
{
$email = new WelcomeEmail();
$this->brevoBridge->send($email, 'user@example.com');
}
}
Email Definition & Reusability
src/Email/ (e.g., PasswordResetEmail, InvoiceEmail).AbstractEmail to define:
$templateId (Brevo template ID).$subject (fallback subject if template lacks one).$fromEmail, $replyTo, or custom logic in getData().class InvoiceEmail extends AbstractEmail
{
protected static $templateId = 'invoice_template_123';
protected static $subject = 'Your Invoice #{{invoiceId}}';
public function getData($invoice): array
{
return [
'invoiceId' => $invoice->id,
'amount' => $invoice->amount,
'dueDate' => $invoice->dueDate->format('Y-m-d'),
];
}
}
Integration with Symfony Services
use BadPixxel\BrevoBridge\BrevoBridge;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class EmailListener
{
public function __construct(private BrevoBridge $brevoBridge) {}
public function onKernelRequest(RequestEvent $event)
{
if ($event->isMainRequest() && $event->getRequest()->isXmlHttpRequest()) {
return;
}
// Logic to trigger emails...
}
}
use BadPixxel\BrevoBridge\BrevoBridge;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SendNewsletterCommand extends Command
{
protected static $defaultName = 'app:send-newsletter';
public function __construct(private BrevoBridge $brevoBridge) {}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$newsletter = new NewsletterEmail();
$subscribers = $this->getSubscribers(); // Fetch from DB
foreach ($subscribers as $subscriber) {
$this->brevoBridge->send($newsletter, $subscriber->email, [
'user' => $subscriber->name,
]);
}
return Command::SUCCESS;
}
}
Dynamic Data Injection
getData() in your email class to inject dynamic values:
public function getData($user): array
{
return [
'firstName' => $user->firstName,
'verificationLink' => route('verify_email', ['token' => $user->verificationToken]),
];
}
$this->brevoBridge->send($email, 'user@example.com', ['customKey' => 'value']);
Sonata Admin Integration
SonataAdminBundle and DoctrineORMAdminBundle are installed.BrevoEmail entities in Sonata Admin (check config/packages/sonata_admin.yaml for overrides).Queueing Emails
use BadPixxel\BrevoBridge\Message\SendEmailMessage;
$this->messageBus->dispatch(
new SendEmailMessage($email, 'user@example.com', $data)
);
config/packages/messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'BadPixxel\BrevoBridge\Message\SendEmailMessage': async
Template Versioning
templates table to track active Brevo template IDs per email type.class WelcomeEmail extends AbstractEmail
{
protected static function getTemplateId(): string
{
return $this->templateRepository->findActiveTemplate('welcome');
}
}
Fallback Logic
use Symfony\Component\Mailer\MailerInterface;
class BrevoBridge extends AbstractBrevoBridge
{
public function __construct(
private MailerInterface $mailer,
// ...
) {}
public function send(AbstractEmail $email, string $to, array $data = []): void
{
try {
parent::send($email, $to, $data);
} catch (BrevoException $e) {
$this->fallbackToSymfonyMailer($email, $to, $data);
}
}
}
Deprecated Symfony Version
symfony/polyfill).Brevo API Rate Limits
BrevoException for 429 Too Many Requests:
try {
$this->brevoBridge->send($email, $to);
} catch (BrevoException $e) {
if ($e->getCode() === 429) {
sleep($e->getRetryAfter()); // Respect Retry-After header
retry();
}
throw $e;
}
Sonata Admin Conflicts
sonata_project_user is installed and configured.php bin/console cache:clear
php bin/console sonata:admin:rebuild
Static Class Limitations
getData():
public function getData(UserRepository $userRepo, $userId): array
{
$user = $userRepo->find($userId);
return ['name' => $user->name];
}
Then inject the repo when sending:
$this->brevoBridge->send($email, $to, [], [$userRepo]);
Template ID Mismatches
$templateId in email classes may break if Brevo template IDs change.# config/packages/monolog.yaml
handlers:
brevo:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.brevo.log"
level: debug
channels: ["bre
How can I help you explore Laravel packages today?