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

Statamic Notifications Laravel Package

goldnead/statamic-notifications

A Statamic addon that brings a flexible notification system to your control panel, making it easy to send, manage, and display notifications for users and editors without custom code.

View on GitHub
Deep Wiki
Context7

Getting Started

v1.12.0 is a small additive release: deferred delivery behaves exactly as in v1.11.0 (immediate notifications still work, deferred delivery stays opt-in), and the only functional change is how the addon's settings entry is placed in the Control Panel navigation. There is no migration and no new config key.

Minimal steps:

  1. Upgrade the package and make sure the v1.11.0 migration has been run:
    composer update <package>
    php artisan migrate
    
  2. Publish/check config/notifications.php for deferred.max_attempts and backoff_seconds. In the Control Panel these settings are reachable from the addon's own navigation entry when statamic-brand-context 1.16+ is installed; on older versions the previous layout is kept.
  3. Add the dispatcher to the scheduler or cron:
    $schedule->command('notifications:dispatch-pending')
        ->everyMinute()
        ->withoutOverlapping();
    
  4. Look first at:
    • The notification_deliveries migration/schema.
    • DeferredChannel implementation for in-app and mail.
    • DeferredRecipientResolver.
    • The notifications:dispatch-pending command.
    • The notifications.deferred config block.
    • settingsNavParent() if you are debugging where the settings screen appears in the CP.

First use case: enqueue a non-urgent notification inside a business transaction, such as a follow-up reminder after signup or an order follow-up, then let the scheduled command deliver in-app and mail notifications later with retry/backoff.

Implementation Patterns

Use deferred delivery when the business write must be atomic but delivery does not need to happen in the request. Call Notifications::notifyDeferred(...) inside the same transaction as the domain change so the notification and one delivery row per channel are committed together:

DB::transaction(function () use ($user, $order) {
    $order->markAsAwaitingFollowUp();

    Notifications::notifyDeferred($user, new FollowUpOrder($order), [
        'in-app',
        'mail',
    ]);
});

Only channels implementing DeferredChannel can carry deferred rows. In v1.12.0 that includes in-app and mail; treat other channels as unsupported unless you add the interface yourself.

Schedule the command from the host application. The addon does not schedule anything, so without a scheduler/cron entry deferred rows will accumulate:

$schedule->command('notifications:dispatch-pending')
    ->everyMinute()
    ->withoutOverlapping();

Recipients are resolved at send time through DeferredRecipientResolver. This is useful when the recipient may change between enqueue and delivery, but it means the resolver must handle deleted users, unsubscribed addresses, disabled accounts, and missing recipient data gracefully.

Monitor outcomes rather than assuming every row sends. Rows can end as accepted, skipped, retry, cancelled, or failed. Treat skipped/cancelled as normal business outcomes, retry as transient, and failed as actionable. Tune notifications.deferred.max_attempts and backoff_seconds per environment; the command frequency controls minimum delivery latency, while exponential backoff controls retry spacing.

For custom channels, implement DeferredChannel and ensure the recipient resolver returns the data your channel needs. Test both the happy path and the skipped/cancelled/failed paths before relying on it in production.

Gotchas and Tips

  • The scheduler is not installed by the package. If notifications:dispatch-pending is not running, deferred notifications will never send.
  • withoutOverlapping() is a good default, but it is not a monitoring strategy. The dispatcher claims rows under a lock; check how stale claims are recovered if the command is killed mid-run.
  • notifyDeferred() writes the notification and delivery rows in one transaction. That is good for consistency, but avoid wrapping large domain operations in the same transaction just to enqueue a notification.
  • Deferred recipient resolution happens later. Do not assume the recipient state at enqueue time is still valid at send time. Store stable identifiers and let the resolver decide whether delivery should proceed, skip, or cancel.
  • Only in-app and mail support deferred delivery out of the box. Attempting to defer arbitrary channels will not work unless the channel implements DeferredChannel.
  • Run the existing migration during deploy and clear config cache after changing notifications.deferred settings:
    php artisan migrate
    php artisan config:clear
    
  • Tune retries with real operational data. Too many attempts can amplify failures; backoff that is too long can delay time-sensitive follow-ups.
  • Plan retention for notification_deliveries. High-volume deferred usage can grow the table quickly; add pruning or archival if the package does not provide it.
  • Settings screen placement is now version-dependent: the addon declares settingsNavParent() ([section, name] of its own CP nav entry), so with statamic-brand-context 1.16+ the settings appear as the sub-item "Einstellungen" beneath that entry, and addons without a nav of their own share a single "Addon-Suite" page under Settings. Older brand-context versions ignore the method and keep the previous layout — if docs and screenshots disagree, check the installed brand-context version first.
  • When debugging, inspect delivery rows by status and attempt count first, then run php artisan notifications:dispatch-pending manually. Check recipient resolver behavior and channel support before assuming the command is broken.
  • Extension points are the DeferredChannel interface, DeferredRecipientResolver, the deferred config block, and (for CP placement only) settingsNavParent(). Keep custom resolver logic deterministic and log enough context to explain skipped/cancelled outcomes.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin