mohammedmanssour / failed-jobs-model
An Eloquent model, query scopes, and Artisan commands to inspect, analyze, and prune Laravel's failed_jobs table — with an indexed display_name for fast lookups and stats.
Package info
github.com/mohammedmanssour/failed-jobs-model
pkg:composer/mohammedmanssour/failed-jobs-model
Requires
- php: ^8.3
- illuminate/contracts: ^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^11.0.0||^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
README
An Eloquent model, query scopes, and Artisan commands to inspect, analyze, and prune Laravel's failed_jobs table — with an indexed display_name for fast lookups and stats.
Installation
composer require mohammedmanssour/failed-jobs-model
The package adds a display_name column (a generated column derived from payload->displayName) and an index on it. The migration runs automatically with:
php artisan migrate
Supports MySQL, PostgreSQL, and SQLite. On MySQL/SQLite the column is
VIRTUAL; on PostgreSQL it isSTORED.
Optionally publish the config and migration:
php artisan vendor:publish --tag="failed-jobs-model-config" php artisan vendor:publish --tag="failed-jobs-model-migrations"
The model
MohammedManssour\FailedJobsModel\FailedJob maps to the failed_jobs table.
use MohammedManssour\FailedJobsModel\FailedJob; $job = FailedJob::findByJobId($uuid); // single job by uuid, or null $job->display_name; // e.g. "App\Jobs\SendEmail" (indexed) $job->payload; // decoded payload object $job->command; // the unserialized job command instance $job->exception; // exception text $job->failed_at; // Carbon instance
Every query is ordered by failed_at descending by default.
Scopes
// Jobs for a given class name (uses the indexed display_name column) FailedJob::whereDisplayName('App\Jobs\SendEmail')->get(); FailedJob::findByDisplayName('App\Jobs\SendEmail'); // shortcut, latest first // Search the exception text (case-insensitive) FailedJob::whereExceptionContains('Connection timed out')->get(); // Drop the default failed_at ordering (e.g. for aggregations) FailedJob::withoutLatestFailed()->get();
Commands
# Most frequently failing jobs (defaults to top 5) php artisan failed-jobs:stats --top=10 # Row count and total size of the failed_jobs table php artisan failed-jobs:size # Prune failed jobs older than N hours (falls back to config) php artisan failed-jobs:prune --hours=24
Scheduled pruning
The package schedules queue:prune-failed for you, configured via config/failed-jobs-model.php:
'pruning' => [ 'enabled' => env('FAILED_JOBS_PRUNE_ENABLED', true), 'hours' => (int) env('FAILED_JOBS_PRUNE_HOURS', 168), // delete jobs older than 7 days 'at' => env('FAILED_JOBS_PRUNE_AT', '01:00'), // daily run time 'environments' => [...], // limit to these envs (empty = all) ],
Testing
composer test
The suite runs on in-memory SQLite by default. To run it against MySQL or PostgreSQL, copy .env.example to .env and set the DB_* values.
License
The MIT License (MIT). Please see License File for more information.