tystr/sendgrid-bundle
Symfony bundle that integrates SendGrid into your app with configuration-based setup and a simple mailer service for sending transactional email through SendGrid's API.
This bundle is a thin Symfony2 wrapper around the legacy sendgrid/sendgrid PHP library. Treat it as a DI/configuration convenience, not a full mailing abstraction.
composer require tystr/sendgrid-bundle
app/AppKernel.php:public function registerBundles()
{
$bundles = array(
// ...
new Tystr\SendgridBundle\TystrSendgridBundle(),
);
}
parameters.yml (not a committed config file):parameters:
sendgrid_username: '%sendgrid_username%'
sendgrid_password: '%sendgrid_password%'
Then wire them into the bundle config in config.yml:
tystr_sendgrid:
username: '%sendgrid_username%'
password: '%sendgrid_password%'
php app/console container:debug | grep -i sendgrid
php app/console container:debug tystr_sendgrid
Look first at these files in the vendor tree:
DependencyInjection/Configuration.php — exact config keys and defaultsResources/config/services.xml (or .yml) — service IDs and constructor argumentsDependencyInjection/TystrSendgridExtension.php — how config is loaded into the container$email = new \SendGrid\Email();
$email
->addTo($user->getEmail())
->setFrom('[email protected]')
->setSubject('Reset your password')
->setText($plainBody)
->setHtml($htmlBody);
$this->get('tystr_sendgrid')->send($email); // confirm ID with container:debug
Wrap the service in your own mailer. Do not scatter new \SendGrid\Email() across controllers. Create a small application service (e.g. AppBundle\Mailer\SendGridMailer) that depends on the bundle service and exposes domain methods like sendPasswordReset(User $user, $token). This gives you one place to swap transports later and one place to unit-test.
class NotificationMailer
{
private $sendgrid;
private $twig;
public function __construct($sendgrid, \Twig_Environment $twig)
{
$this->sendgrid = $sendgrid;
$this->twig = $twig;
}
public function sendWelcome(User $user)
{
$email = new \SendGrid\Email();
$email
->addTo($user->getEmail())
->setFrom('[email protected]')
->setSubject('Welcome')
->setHtml($this->twig->render('AppBundle:Email:welcome.html.twig', array('user' => $user)));
try {
$this->sendgrid->send($email);
} catch (\Exception $e) {
// log and decide: rethrow, queue, or swallow
}
}
}
Render bodies with Twig. Keep templates in AppBundle/Resources/views/Email/ and build HTML/text pairs in the mailer. This is where most day-to-day work happens; the bundle itself does not add templating.
Use the service as a client, not a framework. The bundle gives you an authenticated SendGrid instance. Use the SDK's features directly: attachments, categories, substitutions (for old templating), setBcc, setReplyTo.
Config per environment. Keep production credentials in parameters.yml.dist placeholders and real values in parameters.yml. For test/dev, point at a sandbox or use a null mailer so tests do not send real mail. Because the SDK talks to the network synchronously, a fake implementation of your own mailer interface is usually better than mocking the SendGrid client.
Queue when latency matters. A synchronous SendGrid call in a request cycle is fine for low volume. For anything user-facing and slow (large attachments, batch sends), dispatch a message to a queue (OldSoundRabbitMqBundle, Bernard, or a database queue) and do the SendGrid call in a worker. The bundle does not do this for you.
Batch and loop carefully. For multiple recipients, build one SendGrid\Email per recipient or use the SDK's batch/substitution features if your SDK version supports them. Do not put hundreds of addTo() calls on one email unless you actually want everyone to see everyone else.
Log around the send. Wrap sends in a listener or in your mailer service and record recipient, subject, and response/exception. SendGrid API failures are easy to miss because the SDK returns/throws inconsistently across versions.
Testing. In config_test.yml, define a fake mailer or a test double with the same interface your application code depends on. Assert on the built email object rather than making HTTP calls.
This is legacy territory. The bundle targets Symfony2 and the old SendGrid PHP library. Do not expect it to work on Symfony 3/4/5 without changes, and check composer.json constraints for PHP and symfony/* versions before adding it to a modern project.
Authentication has moved on. SendGrid deprecated username/password API auth in favor of API keys. If the bundled SDK version only supports username/password, you are on a deprecated path. Consider using the official sendgrid/sendgrid SDK directly, or SendGrid's SMTP relay via SwiftMailer, which is often the least invasive upgrade for a Symfony2 app.
Confirm service IDs with container:debug. Documentation for small bundles is often stale. The service may be tystr_sendgrid, tystr_sendgrid.mailer, or similar. container:debug | grep -i sendgrid is faster than reading the extension class.
Credentials belong in parameters.yml. Never commit the SendGrid username/password or API key. Use parameters.yml.dist with placeholder values and deploy real values via your config management.
The SDK may throw, return false, or return a response object depending on version. Normalize this in your mailer wrapper. Check the bundled SDK version in composer.lock and handle failures explicitly; silent failures in transactional email are painful to diagnose.
Rate limits and quotas are real. SendGrid will throttle or reject sends. Add retry/backoff around your wrapper for transient errors, and monitor bounce/spam-report webhooks separately — the bundle does not provide webhook handling.
Attachments and memory. Attaching large files loads them into memory. Stream or offload to SendGrid's hosted assets where possible, and be careful with batch jobs under memory limits.
Extension points are limited but useful. If you need to change behavior without forking, override the bundle's service definition in your app's services.yml (same service ID), or decorate your own mailer around it. The configuration class is the source of truth for supported keys; anything else requires a compiler pass or manual service definition.
Plan an exit. Given zero dependents, seven stars, and Symfony2-era code, treat this as a short-term integration. Keep your own mailer interface in front of it so you can replace the transport with the official SDK or SMTP without touching call sites.
How can I help you explore Laravel packages today?