Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Notifications Laravel Package

orchestra/notifications

Orchestra Notifications adds a complete notification layer to Laravel and Orchestra Platform: define notification types and templates, deliver messages across channels like database and mail, and let users read or dismiss alerts from a central inbox.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Check the framework pin before anything else. v6.0.0's only documented change is Laravel 8 support, so the package's composer.json constraints are the gate. Confirm your app's Laravel version first:

composer show laravel/framework | grep versions
composer require orchestra/notifications:^6.0 --dry-run

If you are on Laravel 10/11, resolution will fail loudly — that is the cheap failure. If you are on Laravel 8, it installs, but you are stacking an EOL framework under a package that just caught up to it.

2. Let auto-discovery register the provider, then confirm what it bound. The package ships Orchestra\Notifications\NotificationServiceProvider, which registers the notification manager and channel drivers. In a fresh install, verify with:

php artisan about            # Laravel 9+; on Laravel 8 use `php artisan route:list`/provider list workarounds
php artisan config:show orchestra/notifications   # or dump config() in tinker

3. Publish and read the config. This is the first thing to look at, because the config is where templates, channel defaults, and message formatting live:

php artisan vendor:publish --provider="Orchestra\Notifications\NotificationServiceProvider"

Then read src/NotificationServiceProvider.php, src/Notification.php (the manager), and src/Channels/ in that order. Those three files tell you what the package actually adds over Laravel.

4. Set up the database channel (the one feature you will use daily). Use the migration stub from your framework version, not a copy from the package:

php artisan notifications:table
php artisan migrate

Add Notifiable to your User model, then send your first notification:

use Illuminate\Support\Facades\Notification;

Notification::send($user, new InvoicePaid($invoice));

First real use case: transactional email + in-app record for a domain event (order shipped, invoice paid, password changed) — one notification class, via() returning ['mail', 'database'], template content centralized in a message object.

Implementation Patterns

Keep domain notifications on Laravel's base class. The package is a manager and message-formatting layer over Illuminate\Notifications\Notification. Write your notification classes against the framework base class and touch the package only for centralization — this keeps your day-to-day code portable if the package stalls.

class InvoicePaid extends \Illuminate\Notifications\Notification
{
    public function __construct(public Invoice $invoice) {}

    public function via($notifiable): array
    {
        return ['mail', 'database'];
    }

    public function toMail($notifiable): MailMessage
    {
        return (new MailMessage)
            ->subject(__('billing.invoice_paid.subject'))
            ->markdown('emails.billing.invoice-paid', ['invoice' => $this->invoice]);
    }

    public function toDatabase($notifiable): array
    {
        return [
            'invoice_id' => $this->invoice->id,
            'amount'     => $this->invoice->formatted_total,
            'url'        => route('invoices.show', $this->invoice),
        ];
    }
}

Centralize templates and recipients in one place. The package's pitch is "templates, recipients, and delivery logic neatly organized" — so make a small NotificationTemplate resolver (config map of notification key => subject + view) and a RecipientResolver (who gets this: the user, the billing contact, the on-call rota). Both are plain classes; the package gives you the seam, you supply the policy.

Register custom channel drivers once, in boot(). The manager is a driver registry:

public function boot(): void
{
    Notification::extendSafely('sms', fn ($app) => new SmsChannel($app->make(SmsClient::class)));
}

Guard against duplicate registration under Octane/long-running workers.

Use a custom channel as an audit/dead-letter sink. A log or audit driver that just persists the rendered payload is the cheapest way to debug "did the notification fire?" without mocking mail.

Hook delivery, not dispatch. Listen to Illuminate\Notifications\Events\NotificationSent (and NotificationSending) for metrics and audit. This is the package-agnostic hook that survives version bumps.

Queue everything, and commit-safe it.

class InvoicePaid extends \Illuminate\Notifications\Notification implements ShouldQueue
{
    public $afterCommit = true;   // Laravel 8+
    public $tries = 3;
    public $backoff = [10, 60];
}

Dispatch from domain events (InvoiceWasPaid listener), never from controllers — delivery becomes a side effect of the domain event, not a second business rule.

Test at both layers. Notification::fake() for "did we dispatch it to the right recipient on the right channel," plus direct unit tests on your template resolver and toMail() output — a fake will not catch a broken Blade view or a missing subject.

Gotchas and Tips

The EOL substrate is the dominant risk, not the package. Laravel 8 went EOL in January 2023. Any framework-level CVE is unpatchable in place, and the maintainer will correctly close "not our bug" issues. Decide this consciously before writing integration code; pin with ^6.0 and record a revisit date.

A one-line changelog for a major version is the real cost line. Nothing documents what changed between v5 and v6. Derive the surface yourself: diff the published config keys, the provider bindings, and the manager's public methods against the version you currently reference. Budget half a day of investigation, not a spike.

Two Notification symbols collide. The native facade and the package manager both want the name. Pick one and enforce it:

use Illuminate\Support\Facades\Notification;          // send
use Orchestra\Notifications\Notification as Manager; // configure

Inconsistent mixing is a classic source of "the channel config was ignored" bug reports.

Config caching bites hard. config/orchestra/notifications.php edits are invisible until php artisan config:clear. In deployment pipelines this manifests as "works locally, not in staging." Also check that config:cache is rebuild-only after the provider merges its defaults.

Database notification schema drift. Use php artisan notifications:table from your installed framework; older stubs use a different key type. Watch the morph column: notifiable_type stores the FQCN unless Relation::enforceMorphMap() is set — introducing a morph map later silently orphans existing rows. Index (notifiable_type, notifiable_id, read_at); the unread-count query is the one that hurts at scale.

Queued notifications serialize models. SerializesModels re-fetches the model at dispatch. If the record was deleted or soft-deleted in between, you get a dropped job or a ModelNotFoundException in failed_jobs — not an error in the request. Always check failed_jobs after deploying anything touching notifications.

Notification::fake() hides driver misconfiguration. It short-circuits the channel manager entirely, so bad credentials, unregistered drivers, and missing views all pass green. Send one real message to a staging sink per channel as part of your smoke test.

Octane/long-running workers cache the resolved manager. Mutating channel config at runtime will not take effect; reset the manager in a RequestTerminated/worker-stopping hook if you rely on that.

Keep the integration thin deliberately. Zero dependents and a thin wrapper around a first-class framework feature means the correct posture is: framework-native notification classes, package only for the manager and template seam. If you ever need to exit, swapping the manager for a plain service provider binding is a one-file change — provided you resisted writing package-specific code into your domain classes.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle