roundly-consulting/alerts-for-laravel
Alerts for Laravel offers a simple, fluent API for creating and displaying alerts in Laravel apps. Flash success, error, warning, and info messages to the session and render them in Blade views with minimal setup.
The package is a scheduled health-check runner with a notification layer: you register recurring checks against any model that can be notified, and it dispatches alerts (throttled) when a check fails and again when it recovers.
Getting to a first useful check takes about four steps:
composer require roundly-consulting/alerts-for-laravel
php artisan vendor:publish --tag="alerts-for-laravel-config" # also publish migrations
php artisan migrate
Look at the config file first. The published config is the real contract surface — in particular the model key that binds the package's check model to your application's class. Read that key before writing anything else, because it determines whether you extend the package base model or simply point it at your own.
Make a model notifiable. Any Eloquent model that should receive alerts needs Laravel's Notifiable trait (and, if you want per-model routing, routeNotificationForMail() / routeNotificationForSlack()):
class Endpoint extends Model
{
use Notifiable;
}
First use case: pick a resource you already monitor manually — an API endpoint, a queue-depth probe, a certificate expiry date — and register a check that hits it on an interval. Run php artisan schedule:work locally and you'll see the check execute on schedule; force a failure (point it at a 500) to confirm the notification lands and that a recovery notification fires when you fix it.
One check per concern, as small classes. Resist the temptation to write one giant "health" check. A check class that does exactly one probe (HTTP status, row count, disk threshold) is easier to throttle independently and easier to test. Closure-based checks are fine for throwaway/one-off probes, but anything you'll page someone on deserves a named class.
Let the scheduler be dumb. Register schedule:run in Kernel::schedule() once, every minute. The package stores each check's interval and decides what's due. Don't create N cron entries for N checks — you lose the ability to reschedule without a deploy and you'll fight withoutOverlapping.
Throttling lives with the check, not in the notification. Expect the check record to carry state (last run, consecutive failure count, last alerted timestamp). That means an alert storm is prevented at dispatch time — if you disable the notification class or swap a channel, throttling still behaves. Design your own alerting code the same way: never put "have I already told someone?" logic in the Notification class.
Recovery is a transition, not a state. Treat recovery notifications as failing → passing, not passing. In practice this means the first-ever successful run should be silent. Assert that explicitly in a test, because "recovered!" emails on a clean deploy are the classic embarrassing bug here.
Route channels per severity. For anything on-call, prefer the notification's own via() over model-level routing — that way a critical check goes to Slack and a warning goes to mail without you needing two notifiable models. Keep alert notifications in app/Notifications alongside the rest of your app so developers find them where they expect.
Test with factories, assert against config. The 1.2.2 fix means package factories build the model bound in config. Write your assertions the same way — assertInstanceOf(config('alerts-for-laravel.model'), $check) rather than the concrete package class — so your test suite stays honest when a host swaps models.
Notification::fake();
$this->travel(10)->minutes(); // exercise the throttle window
$this->artisan('schedule:run');
Notification::assertSentTo($endpoint, CheckFailed::class);
Integration tip: if you already have a monitoring stack (Sentry, Healthchecks.io), use this package for the user-facing slice — "your integration is broken, reconnect it" — and let the infra tooling own pager duty. The two overlap less than they appear to.
Verify what you installed. The bundled description.md describes a flash-message/Blade-alert package, which is not what the metadata describes (scheduled health checks with throttled notifications). Confirm the actual API from the README of the tagged version before designing around either description — this is a small package and the docs are thin.
The model swap is contract-shaped, not just config-shaped. Pointing the config key at your own class only works if that class satisfies whatever the package expects (base model, interface, or trait). Get this wrong and you get a failure at factory time or, worse, a silently-wrong object at runtime. Read the resolution code once, and check whether it resolves lazily per call or caches at container-bind time — that determines whether config()->set() inside a test actually takes effect.
Watch for morph map and table-name drift. If you swap in a model with a different table or a different morph alias, the package's migrations and any polymorphic relations will point at the wrong place. Add a regression test that pins the factory output to the configured class; that's the cheapest guard against a future swap going sideways.
Throttle state in cache is a footgun. If the throttle window is tracked in the cache store, cache:clear, a Redis eviction, or a deploy that flushes cache will re-fire every active alert. Confirm where the state lives; if it's cache-backed and you care, either pin it to a durable store or write your own last-alerted column.
Queued notifications distort the throttle window. If notifications are queued, the "last alerted" timestamp is written at dispatch, not at send. A worker backlog means the window starts early and a failed job means the alert silently never arrives. Run a queue worker in staging when you smoke-test this, or you'll validate a path production never takes.
Isolate scheduled checks from the rest of your test suite. RefreshDatabase plus factories that create checks due now will cause unrelated tests to dispatch notifications the moment anything runs the scheduler. Scope those tests, and Notification::fake() in the base test case if you run schedule:run anywhere broadly.
Don't let a notification exception mark a check healthy. Wrap dispatch in a try/catch that logs and leaves the check in its failing state. The failure mode you're guarding against — a check that silently stops alarming because the mailer threw — is exactly the one this package exists to prevent.
Extension points worth knowing: publishable config (model binding, defaults), publishable notifications if you need to change copy/channels, and the check class hierarchy if you need a custom probe type. Override at the notification level first; only swap the model if you genuinely need domain fields on the check record.
Budget: half a day to wire up plus a regression test, assuming you can read the README. Most of that time goes into the model-swap contract and confirming the throttle store, not into the happy path.
How can I help you explore Laravel packages today?