columns(array_filter([ TextColumn::make('id') ->numeric() ->sortable(), TextColumn::make('connection')->searchable(), TextColumn::make('queue')->searchable(), TextColumn::make('payload')->label('Job') ->formatStateUsing(function ($state) { return json_decode($state, true)['displayName']; })->searchable(), TextColumn::make('exception')->wrap()->limit(100), TextColumn::make('failed_at')->searchable(), ])) ->filters(self::getCompatibleFiltersForIndex()) ->recordActions([ RetryJobAction::make()->iconButton()->tooltip(__('Retry Job')), ViewAction::make()->iconButton()->tooltip(__('View Job')), DeleteJobAction::make()->iconButton()->tooltip(__('Delete Job')), ]) ->toolbarActions([ BulkActionGroup::make([ RetryJobsBulkAction::make(), DeleteJobsBulkAction::make(), ]), ]); } /** * Database-agnostic version of getFiltersForIndex */ private static function getCompatibleFiltersForIndex(): array { $jobs = FailedJob::query() ->select(['connection', 'queue']) ->selectRaw(self::getJsonExtractExpression('displayName', 'job')) ->get(); $connections = $jobs->pluck('connection', 'connection')->map(fn ($conn) => ucfirst($conn))->toArray(); $queues = $jobs->pluck('queue', 'queue')->map(fn ($queue) => ucfirst($queue))->toArray(); $jobNames = $jobs->pluck('job', 'job')->toArray(); return [ SelectFilter::make('Connection')->options($connections), SelectFilter::make('Queue')->options($queues), Filter::make('Job') ->schema([ Select::make('job')->options($jobNames), ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['job'], fn (Builder $query, $job): Builder => $query->whereRaw(self::getJsonExtractExpression('displayName').' = ?', [$job]), ); }), Filter::make('failed_at') ->schema([ DatePicker::make('failed_at'), ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['failed_at'], fn (Builder $query, $date): Builder => $query->whereDate('failed_at', '>=', $date), ); }), ]; } /** * Get database-agnostic JSON extraction expression */ private static function getJsonExtractExpression(string $path, ?string $alias = null): string { $driver = DB::getDriverName(); switch ($driver) { case 'mysql': case 'mariadb': $expression = "JSON_UNQUOTE(JSON_EXTRACT(payload, '$.".$path."'))"; break; case 'pgsql': $expression = "payload->>'".$path."'"; break; case 'sqlite': $expression = "json_extract(payload, '$.".$path."')"; break; case 'sqlsrv': $expression = "JSON_VALUE(payload, '$.".$path."')"; break; default: // Fallback to MySQL syntax $expression = "JSON_UNQUOTE(JSON_EXTRACT(payload, '$.".$path."'))"; break; } return $alias ? $expression.' AS '.$alias : $expression; } }