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

Notifier Laravel Package

orchestra/notifier

Orchestra Notifier is a Laravel package that provides a centralized notification system, letting you send, store, and manage user notifications across multiple channels through an extensible, database-backed architecture.

View on GitHub
Deep Wiki
Context7

Getting Started

Establish compatibility before anything else. The only release note available (v6.0.0) says "Update support to Laravel Framework v8," which tells you the supported Laravel line is 8.x and that this is an Orchestra Platform component, not a general-purpose package. Confirm the solver agrees:

composer require orchestra/notifier:^6.0 --dry-run
composer why orchestra/notifier
composer show orchestra/notifier

If it is already present transitively through an Orchestra Platform install, you are inheriting it — plan to contain it. If you are considering adding it to a new application, stop: Illuminate\Notifications covers the same ground with a maintained upstream.

Read the source first, in this order. The README is thin; vendor/orchestra/notifier/ is the documentation:

  1. composer.json — the real PHP/Laravel ceiling, extra.laravel.providers, and whether tests are autoloadable.
  2. src/ — service provider, the notifier/dispatch class, channel driver interfaces, and how it resolves recipients through Orchestra's User/ACL components.
  3. database/migrations/ and config/ — whether it ships its own table, and under what name. This is the highest-risk detail (see Gotchas).
  4. tests/ — if it is absent or does not run, you have your maintenance posture answer.

Publish and migrate, then verify the table exists.

php artisan vendor:publish --provider="<FQCN from extra.laravel.providers>"
php artisan migrate
php artisan migrate:status

An "empty migration, no error" is the classic symptom of a provider that registered but whose publishable assets did not land. Check the actual schema, not the exit code.

First use case: a database-channel admin announcement. Do not start with mail — deliverability, templating, and queue behaviour all add variables. A database-channel notification proves recipient resolution, persistence, and read-state in one shot, and its blast radius is the smallest if something is wrong. Dispatch one from tinker, inspect the row, confirm the recipient identity and payload shape. Add mail only after that works.

Define your own contract before the first call site. Before any controller, listener, or job imports Orchestra\Notifier\*, create App\Contracts\NotificationDispatcher with the two or three methods your product actually needs (notifyUser, notifyAdmins, markRead). Every pattern below depends on that decision being made up front.

Implementation Patterns

Port and adapter, with a native fallback written in the same sprint. Wrap the package behind an application-owned interface:

interface NotificationDispatcher
{
    public function send(Notifiable $recipient, NotificationType $type, array $payload = []): void;
    public function markRead(Notifiable $recipient, string $notificationId): void;
}

Provide two implementations: OrchestraNotificationDispatcher (delegates to this package) and LaravelNotificationDispatcher (uses Illuminate\Notifications primitives). One container binding switches between them. This is not polish — given the release posture, the native implementation is your remediation path for any future CVE or Laravel upgrade. Contract-test both with the same suite to surface behavioural differences in read-state semantics, payload shape, and recipient resolution.

Domain event → queued listener → dispatcher. Keep notification composition out of controllers.

Event::listen(OrderShipped::class, SendOrderShippedNotification::class);

class SendOrderShippedNotification implements ShouldQueue
{
    public function handle(OrderShipped $event, NotificationDispatcher $dispatcher): void
    {
        $dispatcher->send($event->order->user, NotificationType::OrderShipped, [
            'order_id' => $event->order->id,
            'tracking' => $event->order->tracking_number,
        ]);
    }
}

This gives you one place to add idempotency, one place to feature-flag, and one place to log. Retry semantics become your listener's, not the package's — which matters because you cannot patch the package.

Centralise the type → channel map.

// config/notifications.php
'types' => [
    'order_shipped'      => ['database', 'mail'],
    'admin_announcement' => ['database'],
    'password_reset'     => ['mail'],
],

This is the seam where per-user preferences, digests, and temporary suppression get added later, behind the adapter, without touching vendor code. Do not let channel selection scatter across call sites.

Namespace and version your templates. If the package publishes views, publish them, then immediately copy the ones you intend to change into resources/views/notifications/{type}/{channel}.blade.php and reference yours. Editing vendor-published files in place means your diff against upstream is invisible and the next composer update is a coin flip.

Queue configuration that survives a bad deploy. Give notifications their own queue (notifications) so a slow mail transport cannot starve the job pipeline. Set explicit $tries and $backoff on the listener; defaults are too aggressive for a channel you do not control. Log a correlation ID (event ID + recipient ID) at dispatch and put the same ID in the job payload — without it, "did the user get it?" is unanswerable.

Inbox and read-state queries. If notifications are database-backed, index (notifiable_id, read_at) and (notifiable_id, created_at) on day one. The unread badge is the hottest query in any inbox UI and is a table scan without the first index. Add a retention/archive policy before the table is large enough that cleanup itself becomes risky.

Testing. Swap the dispatcher, not the mailer:

$this->mock(NotificationDispatcher::class, fn ($m) => $m->shouldReceive('send')->once());

Then keep a small number of integration tests that use the real adapter, one per channel, asserting both the stored row and the sent mail. Mocked unit tests will pass while the package is silently doing nothing — exactly the failure mode you are exposed to here.

Roll out as a canary. Internal admin announcement first, behind a flag. For customer-visible notifications, shadow-write alongside the existing path and compare payloads for a week before cutting over.

Gotchas and Tips

Table collision is the first thing to check. Laravel's native database channel owns a notifications table (notifiable_type, notifiable_id, read_at, data). If this package writes a table with the same name, or expects a different schema under it, you get silent failures: the migration is skipped because the table exists, or native writes land in a table with the wrong columns. Grep the package's migrations before running migrate, and on an existing install run SHOW CREATE TABLE notifications and compare against what the package expects.

Empty migration, no error. Typical causes: auto-discovery disabled via extra.laravel.dont-discover; provider registered in a cached config; the publish ran against a different environment. Fix with php artisan config:clear && composer dump-autoload before concluding the package is broken.

The version ceiling is the whole story. Laravel 8 is the supported line. Every future framework upgrade is blocked on either a fork or a removal; budget that as a line item at each upgrade planning, with a named owner. The realistic failure mode for an unmaintained dependency on a customer-visible surface is that no one notices it stopped working.

Model serialization on queued listeners. SerializesModels re-fetches on wake. If the recipient is deleted between dispatch and execution you get a ModelNotFoundException — or a silent no-op, depending on how the package handles it. Decide deliberately whether a missing recipient fails the job or is discarded, and guard the dispatch accordingly.

Retries duplicate sends. A job that sends mail and then dies on the database write will re-send on retry. Add an idempotency ledger keyed on event ID + recipient inside your adapter; do not assume the package deduplicates.

Config caching after publish. php artisan config:cache freezes whatever was published. If you change channel config later and forget to re-cache in production, you will debug a phantom. Make cache-clear part of the deploy script, not a manual step.

Debugging "didn't send," in order. (1) Did the event fire — log in the listener. (2) Was the job queued — check the jobs table. (3) Did it fail — failed_jobs with the exception. (4) Did the adapter reach the channel — log immediately before the package call. (5) Did the package do anything — read vendor/ source; there is no active issue tracker to search. Use Mail::fake() or mailpit to close the loop on the mail path.

Extension points you can rely on, since upstream cannot be relied on. Bind your own channel driver if the driver interface is stable; decorate the dispatcher for logging, retry, and metrics; override views through your own namespace. Anything that requires editing vendor/ is a fork, not an extension point — make that decision consciously, in writing, rather than discovering it during an incident.

Pin exactly and audit. Use "orchestra/notifier": "6.0.0", commit the lock file, and run composer audit in CI. Accept that a CVE in this dependency has no upstream fix path; your remediation is the adapter swap. That is why the native-backed implementation of your contract ships alongside the package-backed one, not "later."

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console