perform/notification-bundle
perform/notification-bundle provides a structured notification layer for Laravel/PHP applications, making it easy to define, dispatch, and manage notifications through a simple, configurable API while keeping delivery logic clean and maintainable.
Verify framework fit before anything else. This is a Symfony bundle (PerformNotificationBundle, -bundle naming, Symfony DI container, config tree, event dispatcher). In a Laravel codebase it will not auto-register, will not use Illuminate\Notifications, and will not speak Laravel's queue or mailer contracts natively. Confirm that before spending a sprint on it.
Fastest path to a working copy (scratch Symfony app):
composer require perform/notification-bundle in a throwaway Symfony 6/7 skeleton — not in your Laravel app yet.config/bundles.php and inspect the vendor dir for DependencyInjection/Configuration.php and Resources/config/services.* — that is where the real configuration surface lives, not in the README.*Notification.php, *Transport*.php, *Handler*.php) to answer "what does it own?" — transport adapters only, or also templating, routing, and preferences.Where to look first, in order:
First real use case: pull it into a scratch branch and map its channel + preference schema against your three highest-value flows (transactional email, in-app alert, SMS). You are evaluating it as a reference implementation, not a dependency. If you can't answer "which of our flows does this model cleanly?" in ~2 days, stop.
Recommended pattern: adapter behind an internal contract. Do not let callers touch the bundle directly. Define your own contract and implement it over both Laravel native and the bundle:
interface NotificationDispatcher
{
public function send(Notifiable $recipient, Notification $n): void;
}
LaravelNotificationDispatcher wraps Illuminate\Notifications\ChannelManager.PerformNotificationDispatcher wraps the bundle's dispatch service.Concept mapping table — write this down before bridging anything:
| Symfony concept | Laravel equivalent |
|---|---|
| Bundle registration | register() in a ServiceProvider + config/perform-notification.php |
| DI service definitions | $this->app->bind() / singleton() |
Config tree / Configuration.php |
config/*.php + mergeConfigFrom |
EventDispatcherInterface |
Illuminate\Contracts\Events\Dispatcher |
| Symfony Mailer / Mime | MailManager, Illuminate\Mail\Mailable |
| Message bus dispatch | dispatch() + ShouldQueue |
TransportInterface |
Custom Channel or queue job |
Reference-model extraction (the highest-value pattern). Even if you never install it, copy the shape:
Notification value object carrying channel list, template key, and payload.Transport interface with one method per transport (send(Recipient, RenderedNotification)).NotificationPreference model with (user_id, channel, type, enabled) — the opt-out schema is the part most teams under-design.RecipientRouter that resolves a notifiable to concrete addresses per channel.Bridge pattern if you genuinely dual-stack. If you run Symfony services alongside Laravel and want one contract across both, put the bundle behind a queue job you own:
final class DispatchPerformNotification implements ShouldQueue
{
public function handle(NotificationDispatcher $d): void { /* ... */ }
}
Queue on the Laravel side, never call the bundle synchronously in a request cycle. This keeps retries, failed(), and backoff under Laravel's semantics rather than a Symfony message bus you aren't monitoring.
Transport adapter pattern. Implement the package's transport interface with a thin class that delegates to a Laravel-native transport (MailManager, an SMS SDK wrapper). This keeps the bundle's routing/preference logic while your actual delivery stays on infrastructure you already observe.
Templating. Do not adopt the bundle's templating if it renders Symfony/Twig shapes. Keep rendering in Laravel (Blade/markdown mailables) and pass fully-rendered content into the bundle — or better, skip the bundle and render into your own dispatcher.
Testing. You will not get Notification::fake(). Wrap dispatch behind your internal contract and fake that in tests; otherwise every test touching notifications becomes an integration test against a third-party transport.
config/bundles.php doesn't exist. You must write a ServiceProvider, publish config, and alias any container IDs by hand. Budget for a permanent bridge shim you own.DependencyInjection/Configuration.php, not documented. Read that file to know what's actually configurable — the description's "configurable templates and recipient routing" may be thinner than it sounds.failed() hooks.Symfony\Mime messages directly, it skips Laravel's mailer — meaning no provider failover, no mail logging, no theming hooks. Verify before trusting it with transactional email.Illuminate\Notifications, and lift this package's preference schema as inspiration. The bundle is a scoping artifact, not a runtime dependency.How can I help you explore Laravel packages today?