tomatophp/tomato-notifications-module
A Laravel notifications module for TomatoPHP apps that centralizes notification management, delivery, and templates, with admin-panel integration for configuring and tracking notifications without building the infrastructure from scratch.
Start by confirming the package fits your stack: tomato-notifications-module is part of the TomatoPHP admin ecosystem and expects a Filament-based panel plus the Tomato admin/auth scaffolding. On a bare Laravel app, install that foundation first — the module's resources, policies, and user menu integration assume it exists.
composer require tomatophp/tomato-notifications-module
php artisan vendor:publish --tag=tomato-notifications-config
php artisan vendor:publish --tag=tomato-notifications-migrations
php artisan migrate
If a publish tag is rejected, run php artisan vendor:publish interactively and pick the provider — tag names have shifted between TomatoPHP releases and are the most common first-run stumble.
Where to look first, in order:
config/tomato-notifications.php — channel toggles, template/table names, queue settings, gateway credentials.src/Filament (or src/Resources) — this is the CRUD surface for notification templates and logs, and tells you the actual schema faster than the migrations do.NotificationsTemplate or similarly named) — inspect $fillable to learn the placeholder/variable convention and the lang column before writing any copy.First use case to prove the wiring end-to-end: create a template in the admin panel with key welcome and a body containing one placeholder, send it from a tinker session or a queued job to a single user, then verify three things — a row in the template's log table, a row in Laravel's notifications table (if the database channel is on), and a rendered email. Getting all three confirms the template lookup, variable substitution, channel dispatch, and logging in one pass. If mail is queued, start a worker before testing or you'll chase a phantom bug.
Template-first vs. class-first notifications. Keep marketing and transactional copy (welcome, password reset, invoice ready) in DB templates so non-developers can edit wording and translations. Keep notifications whose payload is structural — anything where a missing or renamed variable should be a compile-time-ish failure — as ordinary Laravel Notification classes. Mixing the two is fine; the mistake is templating a notification whose body is mostly data.
The send path. The canonical flow is: resolve a user or collection → hand the package the template key plus a key/value array of variables → it renders per recipient → dispatches across the configured channels → writes a log row. Wrap this in your own thin service so your app calls Notify::template('welcome', $user, [...]) rather than the package directly. That indirection is what saves you when a template key gets renamed or a channel is swapped.
Bulk and segmented sends. Chunk recipients (chunkById, ~500) inside a queued job; never loop in a controller. Set $tries, a backoff, and a failed() handler on the job. If you fan out to email or SMS, apply RateLimited or ThrottlesExceptions middleware on the job — provider rate limits are the usual cause of a 40-minute bulk send turning into a 4-hour one.
Queue hygiene. Implement ShouldQueue on anything that reaches mail or an external gateway, and use ->afterCommit() (or after_commit = true on the connection) so you don't notify users about records that a rolled-back transaction erased. Serialize user IDs, not Eloquent models, in job payloads — it keeps payloads small and avoids stale-state surprises.
Localization. Use one template row per lang and set the locale per recipient immediately before dispatch ($user->preferred_locale), not per batch. A batch that sets locale once will silently send everything in the last recipient's language.
Filament integration. Register the plugin in your panel provider; the database-notification bell in the top bar then reads from Laravel's notifications table. Mark-as-read and polling behavior come from Filament, not this package — so if read state looks wrong, look at your Notifiable model and the panel config before blaming the module.
Audit trail. Treat the log table as an append-only delivery record: add your own status, provider_message_id, and failed_reason columns via a separate migration rather than editing the published one. Extend the logging side with a listener on NotificationSent / NotificationFailed so both package-sent and class-sent notifications land in the same place.
Testing. Notification::fake() proves dispatch but not rendering — it will happily pass while your template renders {name} literally. Add one test per template that renders the body with real variables and asserts on the output; snapshot the rendered HTML to catch silent placeholder regressions across releases.
{name}, {{name}}, and :name are all conventions used somewhere in the Laravel ecosystem; this package uses one and does not error on the others — it just renders them literally. Read the substitution code once and write a test with a deliberately missing key to see whether you get an empty string, the literal token, or an exception. That behavior is your contract.tomato-admin version bounds are the usual composer conflict. Pin the whole Tomato set together and upgrade them as a unit rather than bumping one.notifications table migration — Laravel ships one, and a duplicate will either fail or quietly create a mismatched schema. Check what the publish tag actually emits before running migrate.line() instead of html(), recipients see raw tags and the bug looks like a template problem.failed_jobs, not at boot — so a "silent" non-delivery usually means an unconfigured gateway, not a broken module.notification_logs → failed_jobs → storage/logs/laravel.log → Notification::fake() in a test. Fakes hide exactly the channel errors you're hunting, so remove the fake before concluding anything.saved/deleted events — otherwise editors won't see their changes until the TTL expires, which reads as a bug.^1.0, run composer update tomatophp/tomato-notifications-module, re-publish any assets if the install uses them, and re-run your template-rendering tests. If you have production copy in DB templates, export the template rows before upgrading so a schema tweak in a patch release can't strand your content.How can I help you explore Laravel packages today?