halilcosdu/laravel-slower
Detect and log slow Laravel database queries, then get AI-powered suggestions for indexes and query improvements. Configurable thresholds, can run with or without AI, and supports Laravel 10–13 on PHP 8.2+.
The first phase of the 2026 roadmap: safe capture foundation. Slower becomes something you confidently leave on in production — and every capture now knows what it is (fingerprint) and where it came from (origin).
IN (...) sizes normalized away in a single lexer-style pass). The dashboard gains an Events | Grouped toggle: one row per query shape per connection, with occurrence count, avg/max duration and last-seen, with drill-down to the underlying events. The SlowQueryFirstSeen event uses the same per-connection identity. php artisan slower:fingerprint backfills existing records (chunked, idempotent).Controller@action, queue job class, or artisan command — plus the first file:line of application code in the stack (taken only for threshold-exceeding queries, with DEBUG_BACKTRACE_IGNORE_ARGS). Shown on the detail page and included in the AI prompt for sharper advice. The authenticated user id is opt-in (SLOWER_CAPTURE_USER_ID); origin capture itself can be disabled.capture.sample_rate (capture a fraction of slow queries), capture.max_per_execution (hard cap per request/job/command), a 60-second circuit breaker when storing captures fails, and a hardened self-capture guard.ai_payload.send_raw_sql / send_bindings); a configured PayloadRedactor covers every outbound path (raw SQL, bindings, and the EXPLAIN plan), and a misconfigured redactor throws instead of silently passing secrets. Canary-secret tests pin the contract.SLOWER_ANALYZE_QUEUE=<queue> and the dashboard's analyze actions dispatch unique-per-record background jobs instead of blocking the request; slower:analyze --queue does the same for bulk analysis. Unset, everything stays synchronous — no worker required.SlowQueryCaptured (every capture) and SlowQueryFirstSeen (first time a query shape appears) — wire them to Slack/mail/webhooks with a plain Laravel listener; the package deliberately ships no notification channels.fingerprint, fingerprint_version and origin (additive migration; existing rows keep working and can be backfilled).Publish and run the new migration, then optionally backfill fingerprints:
php artisan vendor:publish --tag="slower-migrations"
php artisan migrate
php artisan slower:fingerprint
No breaking changes: the events view, existing config keys, AiServiceDriver, and the sync analysis path are untouched. New config keys all have safe defaults — to customize them, re-publish the config or copy the capture / ai_payload / analyze_queue blocks from the README.
SLOWER_AI_SERVICE + the provider's API key) and every optional override commented out with its real Prism default (URL, organization, project, API version). Added a table of contents, a capture → analyze → recommend flow, a provider/model-default table, richer programmatic-usage examples (facade, counting pending, batch-analyzing the slowest queries), PHP/Laravel/License badges, and GitHub note/tip/warning callouts.SLOWER_AI_SERVICE=openai|anthropic|gemini|ollama|…. Any Prism provider works out of the box; a fully custom backend registers via AiServiceManager::extend().gpt-5.4-mini, claude-haiku-4-5, gemini-2.5-flash), overridable with SLOWER_AI_RECOMMENDATION_MODEL.config/prism.php (OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY); Slower's own open_ai config block is removed. recommendation_model now defaults to null (the provider's default). The AiServiceDriver contract and AiServiceManager::extend() are unchanged.openai-php/laravel dependency with prism-php/prism. The whole integration lives behind one PrismDriver, so the rest of the package is decoupled from the provider layer.Existing OpenAI users need no changes — Prism reads your OPENAI_API_KEY, and a boot-time bridge still honors a legacy slower.open_ai.api_key. To use Claude or Gemini: composer update, set ANTHROPIC_API_KEY/GEMINI_API_KEY, and set SLOWER_AI_SERVICE. If your old published config hardcodes recommendation_model, clear it (or set the new provider's model) before switching providers. AI requests now time out after Prism's default of 30 seconds; if your analyses legitimately run longer (very large schemas or a slow model), raise it with PRISM_REQUEST_TIMEOUT (seconds). Users who previously relied on OPENAI_TIMEOUT should set PRISM_REQUEST_TIMEOUT instead.
Platform modernization. No public API, config, or database changes — only the supported runtime and the development toolchain moved forward.
casts() model method, so Laravel 10 was effectively unsupported; this makes the constraint honest.openai-php/laravel raised to ^0.20.0 — the previous ^0.18.0 capped at Laravel 12 and silently blocked Laravel 13 installs.pest-plugin-laravel 4 (first line to support Laravel 13), PHPStan 2 / larastan 3 (resolving the PHPStan 1-vs-2 dependency conflict), testbench 9–11.notify() hook in SlowerServiceProvider and its call site.No application-code, config, or migration changes are required if you already run PHP 8.3+ and Laravel 11+. Still on PHP 8.2 or Laravel 10? Stay on the ^2.3 line.
/slower (Telescope/Horizon-style) — no npm build, no CDN, no assets to publish. It lists captured slow queries with overview stats (total, pending, average and max duration), search over the resolved SQL, filters by analyzed status and connection, sortable columns, and pagination. A detail page shows keyword-formatted SQL, bindings, and the AI recommendation rendered from markdown.dashboard.analyze_pending_limit pending queries at once, delete a single record, and clean up records older than N days (0 clears everything). Every AI action warns it may incur provider charges; every destructive action requires confirmation.viewSlower gate, which defaults to the local environment only. Define the gate in a service provider to open the dashboard in other environments. The gate is defined in the provider (not in config) so it is config:cache-safe.prefers-color-scheme, persists the manual choice), copy-to-clipboard, confirmation dialogs, and auto-submitting filters — with no runtime dependency added to the package.MarkdownRenderer — a tiny, escape-first markdown renderer (headings, bold/italic, inline code, fenced code blocks, lists) that HTML-escapes all input before any transform, so AI-generated recommendations render richly without becoming a stored-XSS vector.testbench serve environment (with a fake AI driver) for local development and browser testing of the dashboard.dashboard block (enabled, path, domain, middleware, per_page, analyze_pending_limit). Existing keys are unchanged; published configs keep working via defaults.RecommendationService now degrades gracefully when a captured query's connection is unavailable — it analyzes without schema context instead of throwing.Purely additive — no migration required, and the dashboard is disabled outside local by default. To use the dashboard: upgrade, then visit /slower locally (or define a viewSlower gate for other environments). Publish the config with php artisan vendor:publish --tag="slower-config" to customize the dashboard block.
EXPLAIN (never EXPLAIN ANALYZE), resolves the captured query's own connection, picks the correct statement form per database driver (pgsql/mysql → EXPLAIN, sqlite → EXPLAIN QUERY PLAN), skips multi-statement input, and reports EXPLAIN failures without breaking the analysis flow. Previously, explain analyse was run unconditionally, which both fails on MySQL/SQLite and can execute the underlying query against production data.is_analyzed when the AI actually returns a recommendation. Empty results stay is_analyzed=false so they are retried on the next slower:analyze run instead of being silently finalized. The command now reports an Analyzed | Skipped summary.SlowerServiceProvider::createRecord now reports failures (via report() without the raw SQL) instead of silently discarding them, and catches Throwable so logging slow queries can never break the application's own request.slower:clean and slower:analyze now use chunkById to avoid skipping records when the result set changes during iteration.gpt-4 (shut down 2026-10-23) to gpt-5.4-mini. Pin SLOWER_AI_RECOMMENDATION_MODEL=gpt-4 to keep the previous behaviour.openai-php/laravel constraint raised to ^0.18.0, and dependabot/fetch-metadata bumped to 2.5.0.slower.php (ai_service documented) and the OpenAI driver/contract extension point explained.OpenAiDriver now type-hints OpenAI\Contracts\ClientContract instead of the final concrete Client, so it can be substituted/faked in tests.Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v1.1.1...v2.0.0
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v1.0.7...v1.0.8
Full Changelog: https://github.com/halilcosdu/laravel-slower/commits/v1.0.0
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v2.0.2...v2.0.3
How can I help you explore Laravel packages today?