Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Query Detector Laravel Package

beyondcode/laravel-query-detector

Detect N+1 query issues in Laravel during development. Monitors database queries in real time and alerts you when repeated queries suggest missing eager loading, helping you optimize performance and reduce unnecessary database calls.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package in development mode:

composer require beyondcode/laravel-query-detector --dev

First use case: Enable it immediately in debug mode. No configuration needed—it automatically detects N+1 queries and shows browser alerts. Test by:

  1. Running php artisan serve in debug mode (APP_DEBUG=true in .env).
  2. Accessing a route that loads related models without eager loading (e.g., @foreach($posts as $post) {{ $post->author }} @endforeach).
  3. Observe the alert highlighting the inefficient query.

Key files to check:

  • config/querydetector.php (published via php artisan vendor:publish --provider="BeyondCode\QueryDetector\QueryDetectorServiceProvider").
  • app/Providers/EventServiceProvider.php (for custom event listeners).

Implementation Patterns

Core Workflow

  1. Detect: The package hooks into Laravel’s query log and tracks relation queries (e.g., Post::find(1)->author).
  2. Threshold Check: If a relation query runs more than config('querydetector.threshold') times (default: 1), it triggers.
  3. Output: Displays via the configured channel (e.g., browser alert, log, or Debugbar).

Common Patterns

1. Eager Loading Fixes

  • Before: @foreach($posts as $post) {{ $post->comments }} @endforeach
  • After: $posts = Post::with('comments')->get();
  • Pattern: Use with() on the parent query to load relations upfront.

2. Whitelisting Relations

Configure config/querydetector.except to ignore specific relations:

'except' => [
    Post::class => [
        Comment::class,
        'comments',
    ],
],

Use case: Exclude relations that are intentionally loaded lazily (e.g., for pagination).

3. Custom Output Channels

Extend functionality by adding new output classes (e.g., Slack notifications):

// config/querydetector.php
'output' => [
    \BeyondCode\QueryDetector\Outputs\Log::class,
    \App\Outputs\SlackOutput::class, // Custom class
],

4. Event-Driven Actions

Listen for \BeyondCode\QueryDetector\Events\QueryDetected to trigger custom logic:

// app/Providers/EventServiceProvider.php
protected $listen = [
    \BeyondCode\QueryDetector\Events\QueryDetected::class => [
        \App\Listeners\LogToSentry::class,
    ],
];

5. API-Specific Optimizations

Use the JSON output to embed warnings in API responses:

'output' => [
    \BeyondCode\QueryDetector\Outputs\Json::class,
],

Example response:

{
    "data": [...],
    "meta": {
        "warnings": [
            {
                "query": "SELECT * FROM `comments` WHERE `post_id` = ?",
                "relation": "comments",
                "model": "App\Models\Post",
                "count": 5
            }
        ]
    }
}

6. Lumen Integration

Manually register the provider in bootstrap/app.php:

$app->register(\BeyondCode\QueryDetector\LumenQueryDetectorServiceProvider::class);

Gotchas and Tips

Pitfalls

  1. False Positives with Dynamic Queries

    • Issue: The detector may flag queries like Post::where('id', $id)->first()->author as N+1 even if $id varies.
    • Fix: Adjust the threshold or whitelist dynamic relations:
      'except' => [
          Post::class => ['author'], // Ignore all author queries
      ],
      
  2. Debugbar Conflicts

    • Issue: If using both barryvdh/laravel-debugbar and fruitcake/laravel-debugbar, the package may fail to resolve the Debugbar facade.
    • Fix: Update to v2.3.0+ or explicitly bind the facade in AppServiceProvider.
  3. Threshold Too Low

    • Issue: Setting threshold: 0 will alert on every relation query, overwhelming output.
    • Tip: Start with threshold: 2 to catch only obvious inefficiencies.
  4. Lumen Compatibility

    • Issue: Forgetting to register the Lumen provider will silently disable detection.
    • Fix: Always include the provider in bootstrap/app.php for Lumen.
  5. Empty Queries

    • Issue: Queries like Post::find(1)->relation where the parent model doesn’t exist may trigger false alerts.
    • Fix: Use the empty_queries config (v2.0+) to ignore them:
      'empty_queries' => true,
      

Debugging Tips

  1. Inspect the Query Log Enable Laravel’s query log to correlate alerts with actual queries:

    DB::enableQueryLog();
    $posts = Post::all();
    dd(DB::getQueryLog());
    
  2. Check the Backtrace The alert includes a backtrace. Look for:

    • Blade templates: @foreach($posts as $post) {{ $post->author }} @endforeach
    • Controller methods: $post->comments in loops.
  3. Test with Realistic Data Use seeders to populate test data that triggers N+1 (e.g., 10 posts with 5 comments each).

  4. Disable for Production Set QUERY_DETECTOR_ENABLED=false in .env to avoid runtime overhead in production.

Extension Points

  1. Custom Output Classes Extend \BeyondCode\QueryDetector\Outputs\OutputInterface to create new channels (e.g., Datadog, Sentry):

    namespace App\Outputs;
    use BeyondCode\QueryDetector\Outputs\OutputInterface;
    
    class DatadogOutput implements OutputInterface {
        public function output(array $queries) {
            // Send to Datadog API
        }
    }
    
  2. Modify the Threshold Dynamically Override the threshold per request:

    config(['querydetector.threshold' => 3]); // For a specific route
    
  3. Exclude Specific Routes Disable detection for non-critical routes (e.g., APIs):

    if (request()->is('api/*')) {
        config(['querydetector.enabled' => false]);
    }
    
  4. Integrate with CI/CD Use the JSON output to fail builds with N+1 queries:

    # In GitHub Actions
    if grep -q '"warnings"' response.json; then
        echo "N+1 queries detected!" >&2
        exit 1
    fi
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope