x-one/notifier-bundle
A Laravel bundle for sending notifications across multiple channels, offering a clean API for configuring drivers, templates, and delivery rules with queue and event integration.
This is a Symfony bundle (not Laravel) — it wraps symfony/notifier and adds enhancements on top. Plan to use it inside a Symfony 5.4/6.x/7.x app; there is no Laravel service provider.
composer require symfony/notifier symfony/mailer symfony/twig-bridge
composer require x-one/notifier-bundle
// config/bundles.php
return [
// ...
XOne\NotifierBundle\NotifierBundle::class => ['all' => true],
];
php bin/console config:dump-reference notifier
php bin/console debug:config notifier
framework.notifier (or the bundle's own x_one_notifier key if it uses a separate tree). Typical DSNs:
# config/packages/notifier.yaml
framework:
notifier:
chatter_transports:
slack: '%env(SLACK_DSN)%'
texter_transports:
twilio: '%env(TWILIO_DSN)%'
channel_policy:
urgent: ['chat/slack']
low: ['email']
NotifierInterface, build a Notification, and send it. Getting a single Slack or email notification through the bundle's channel is the fastest way to validate that its enhancements (extra transports, formatters, or policies) are wired correctly:
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\NotifierInterface;
public function __invoke(NotifierInterface $notifier): void
{
$notifier->send(new Notification('Deploy finished', ['chat/slack']));
}
DependencyInjection/Configuration.php (what config keys really exist), DependencyInjection/*Extension.php (what services are registered/aliased), and Resources/config/services.* (what it decorates). These three files tell you more than the README in a low-adoption bundle.Domain notifications as value objects. Keep Notification subclasses in src/Notification/, override getChannels() and asChannels() to control routing per message type. This is where the bundle's enhancements earn their keep — they typically affect how channels are resolved or formatted, so route through getChannels() rather than hard-coding channel strings at call sites.
Inject the notifier, don't build it. Always take NotifierInterface via constructor injection. The bundle's value is in how it assembles that service; instantiating Notifier yourself bypasses every enhancement it adds.
Async by default for anything user-facing. Symfony Notifier dispatches through Messenger when a bus is configured. Let the bundle's transport config stay async and reserve synchronous sending for CLI/cron scripts where a failure should abort immediately:
framework:
messenger:
routing:
'Symfony\Component\Notifier\Message\ChatMessage': async
'Symfony\Component\Notifier\Message\SmsMessage': async
Make notification classes Serializable-friendly (scalar properties, no closures, no entity references) because they will be serialized into the queue.
Channel policy for tiered alerting. Use channel_policy with Notification::IMPORTANCE_URGENT / HIGH / MEDIUM / LOW rather than branching in application code. Set importance on the notification, let policy pick channels:
$n = (new Notification('DB replica lag', ['chat/slack']))
->importance(Notification::IMPORTANCE_URGENT);
$notifier->send($n);
Custom channels for the bundle's extension points. If the package adds transport registration, the clean integration is a class implementing ChannelInterface plus a TransportFactoryInterface, tagged and wired in config/services.yaml. Check whether the bundle already tags notifier.transport_factory itself — if it does, your factory only needs the tag, not a compiler pass.
Testing pattern. In functional tests, swap the transport DSN for the null:// transport, or assert against a Notifier decorated with a fake transport. For unit tests of your own notification classes, assert on getChannels() and asChannels() output — those are pure functions and catch most routing regressions.
Profiler-driven debugging. In dev, the Web Profiler's Notifier panel (if symfony/notifier debug is enabled) shows dispatched messages and transports. This is far faster than adding logging around send().
composer.lock after install, pin an exact version ("x-one/notifier-bundle": "1.2.*"), and read the DI extension before trusting it in production. A small bundle that decorates core services can silently change behaviour across Symfony minors.framework.notifier, both can define transport lists. Run debug:config for both keys after install to confirm which one actually wins; silent shadowing is the most common failure here.notifier or on individual transports. If your own decorator or compiler pass touches the same service, ordering matters — set decoration_priority explicitly and verify with php bin/console debug:container notifier --show-private.retry_strategy per transport and make your handler tolerant of duplicate delivery; a Slack channel that times out mid-send can still have delivered.DateTimeImmutable with odd timezones, or closures fail in the worker, not at dispatch. Keep notification DTOs to scalars and ISO strings.MessageEvent on send (and FailedMessageEvent on failure). Subscribe to these for audit logging or fallback routing instead of forking the bundle — it survives bundle upgrades.notifier.transport_factory in debug:container --tag=notifier.transport_factory. Missing tags are the usual reason a configured DSN is "unknown transport".phpunit bridge assumption. For a package this small, expect no dedicated test helpers. Use null:// DSNs and assert on your own notification objects; don't wait for a bundle-provided fake.SYMFONY_DEPRECATIONS_HELPER=disabled to surface deprecations from the bundle's decorators rather than from core — those are the ones that will break first.How can I help you explore Laravel packages today?