gloudemans/notify
gloudemans/notify is a Laravel package that provides a simple notification system for your application. It offers a lightweight API for creating and managing notifications, making it easier to add user-facing messages and alerts without building the underlying infrastructure yourself.
First, sanity-check the fit. gloudemans/notify (Crinsane/LaravelNotify) is a Laravel 5.0/5.1-era package (17 stars, 0 dependents, last meaningful activity ~2015). It predates Illuminate\Notifications, which shipped in Laravel 5.3 and covers the same ground with channels, queues, and mail. Adopt this only if you are maintaining a legacy app already on it, or you deliberately want its specific model: notifications stored as rows in two tables and rendered by polling, not pushed over a channel.
Install and wire it up:
composer require gloudemans/notify
Register the provider and facade in config/app.php:
Gloudemans\Notify\NotifyServiceProvider::class,
// aliases
'Notify' => Gloudemans\Notify\Facades\Notify::class,
Publish assets and run the migrations:
php artisan vendor:publish --provider="Gloudemans\Notify\NotifyServiceProvider"
php artisan migrate
Where to look first, in this order:
composer.json of the package — check the illuminate/support constraint against your Laravel version. This is the single fastest way to know whether it will even boot.notifications (the catalogue of sent notifications) and user_notifications (per-user delivery + read_at pivot). Understanding these two tables is 80% of understanding the package.config/notify.php — model bindings, table names, view/route behaviour.Notifiable trait and the Notify facade — the entire public API surface.First use case: an in-app notification bell. Something happens (OrderShipped), you write one row via the facade, the user's header polls an endpoint returning unreadNotifications()->count(), and a dropdown renders the feed with a "mark as read" action.
Attach the trait to your user model. This gives you the relations and unread scoping:
use Gloudemans\Notify\Traits\Notifiable;
class User extends Authenticatable
{
use Notifiable;
}
If the trait name collides with another Notifiable, alias it on import (use Gloudemans\Notify\Traits\Notifiable as NotifyNotifiable;).
Send from a service, not a controller. Keep the facade call in one place so copy and types stay consistent:
Notify::send($user, 'Order shipped', 'Your order #1234 is on its way.', 'success');
Fan-out is the common case — pass a collection of users (an order's watchers, a team, an admin group) rather than looping in your controller.
Use notification "types" as your design contract. The package's type field (typically info, success, warning, danger) is what drives Bootstrap-style colouring in the default views. Define a small enum/constant list in your app and never pass raw strings from controllers — this is the cheap extension point that keeps the UI coherent as the product grows.
Build the read path explicitly. Three endpoints/partials cover the bell:
// count for the badge
auth()->user()->unreadNotifications()->count();
// feed
auth()->user()->notifications()->latest()->paginate(20);
// mark as read
$user->notifications()->whereKey($id)->first()->markAsRead();
Always route mark-as-read through a policy or ownership check — the pivot makes it trivial to mark someone else's notification read if you take the id straight from the request.
Poll, don't pretend it's realtime. A 30–60s setInterval fetch of an unread count is the intended pattern. If you need push, this package will not give it to you; layer a broadcast event on top and treat the DB rows as the durable record.
Prune on a schedule. Read notifications accumulate indefinitely. Add a scheduled command deleting user_notifications where read_at is older than N days, and orphaned notifications with no pivot rows. Without this, the badge query degrades on busy accounts within months.
Test against the tables. Use RefreshDatabase and assert assertDatabaseHas('user_notifications', ['user_id' => ..., 'read_at' => null]). Asserting on rows rather than on the facade's return value catches the fan-out and duplicate-delivery bugs that matter.
Illuminate\Notifications\Notifiable; this package ships its own Notifiable. You cannot use both unaliased in one model. Alias one, and be explicit in code review about which is which.notifications table with a data JSON column. This package uses notifications plus user_notifications with different columns. If both exist in one app, the migrations will fight. Rename one set via the published config/migration.$defer provider property was removed in Laravel 5.8. Budget for a fork rather than assuming composer require works.config/app.php or config/notify.php, run php artisan config:cache clears — a stale cached provider list is a classic "facade class not found" red herring.->with(...) on the feed query.resources/views/vendor/notify; anything you edit in place is lost on the next vendor:publish. Extend the package views and swap components instead.onDelete('cascade') to the user_id pivot if the package's migration doesn't; otherwise deleting a user silently corrupts unread counts and renders broken feed rows.DB::transaction with a dedupe row.send() argument order (recipient / title / message / type) is easy to invert silently. Write one integration test that asserts the stored row's columns at the very start of adoption — that test is your real documentation.How can I help you explore Laravel packages today?