snowcap/mailjet-bundle
Snowcap Mailjet Bundle provides Symfony integration for the Mailjet email service, including configuration and services for sending transactional messages and accessing Mailjet API features from PHP applications. It helps wire Mailjet credentials and clients into Symfony projects.
The bundle is a thin Symfony2-era wrapper around the Mailjet PHP SDK, so the fastest path to value is: install it, register the bundle, wire credentials, then send one transactional email.
composer require snowcap/mailjet-bundle. Because it targets Symfony 2.x, you'll almost certainly need --with-dependencies resolution tricks or a "minimum-stability": "dev" if Composer complains about the abandoned mailjet/mailjet-apiv3-php constraint.app/AppKernel.php:
new Snowcap\MailjetBundle\SnowcapMailjetBundle(),
app/config/parameters.yml (never in config.yml directly):
parameters:
mailjet_api_key: "%env(MAILJET_API_KEY)%"
mailjet_secret_key: "%env(MAILJET_SECRET_KEY)%"
and in config.yml:
snowcap_mailjet:
api_key: "%mailjet_api_key%"
secret_key: "%mailjet_secret_key%"
The exact option names are the first thing to verify — read DependencyInjection/Configuration.php in the bundle before guessing, because the docs are sparse.snowcap_mailjet or a per-resource service like snowcap_mailjet.message) into a controller and fire a test message. Start with a SendMessage call to a hard-coded recipient before wiring it into any real flow.If you are on Symfony 3.4+, stop here and evaluate whether wrapping mailjet/mailjet-apiv3-php yourself is cheaper — it usually is.
Constructor injection over container access. Always inject the Mailjet service into your own thin mailer class rather than pulling it from the container in controllers. This keeps your code Symfony-version-agnostic and makes it trivial to swap the transport later:
class NotificationMailer
{
public function __construct(private MailjetService $mailjet) {}
public function sendWelcome(User $user): void
{
$this->mailjet->send([
'From' => ['Email' => '[email protected]', 'Name' => 'Example'],
'To' => [['Email' => $user->getEmail()]],
'Subject' => 'Welcome',
'TextPart' => '...',
]);
}
}
Environment-split credentials. Keep sandbox and live API keys in separate parameters_<env>.yml files. Mailjet sandbox mode is invaluable for CI — point the test env at a sandbox key so your functional tests don't burn send quota.
Adapter pattern for testability. Because the underlying client is concrete, wrap it behind your own MailerInterface (or reuse Symfony's Swift_Mailer/Mailer if you're already there). Then in tests you bind a stub; in production you bind the Snowcap wrapper. This is the single most impactful thing you can do given the bundle's low maintenance activity.
Contact and list management as batch jobs. Day-to-day use often isn't transactional email — it's syncing users into Mailjet contact lists. Wrap that in a console command (app/console mailjet:sync-contacts) and run it on a cron, batching 50–100 contacts per API call to stay under rate limits.
Log every send with a correlation ID. Push the Mailjet message ID into your application logs alongside the recipient and template name. When a customer says "I never got the email," this is the difference between a 10-minute investigation and an afternoon.
composer.json ("~1.0" or the actual tag) so a silent breaking change can't land, and budget time to replace it.app/console container:debug | grep mailjet to confirm which service IDs are actually registered before writing services.yml aliases.success payloads — a misconfigured sender address will look like success at the HTTP layer on older API versions..env requires cache:clear — a common source of "it works locally, not in staging" confusion.services.yml (same service ID, your class). This preserves composer update safety.kernel.debug: true and the Mailjet SDK's logger to stderr to see raw request/response bodies. That alone resolves most "why didn't it send" tickets.send(), contact ops). Swapping to the raw mailjet/mailjet-apiv3-php client — or Symfony Mailer's Mailjet bridge on newer Symfony — is typically a one-day refactor, not a rewrite.How can I help you explore Laravel packages today?