Find and fix Laravel performance issues automatically
romdh4ne/laravel-querycraft is a Laravel package for find and fix laravel performance issues automatically.
It currently has 22 GitHub stars and 37 downloads on Packagist (latest version v1.1.0).
Install it with composer require romdh4ne/laravel-querycraft.
Discover more Laravel packages by romdh4ne
or browse all Laravel packages to compare alternatives.
Last updated
A Laravel performance analysis dashboard for detecting N+1 queries, slow queries, missing indexes, and duplicate queries β in real time.
EXPLAINcomposer require romdh4ne/laravel-querycraft
php artisan vendor:publish --tag=querycraft-config
php artisan vendor:publish --tag=querycraft-assets
php artisan vendor:publish --tag=querycraft-views
php artisan config:clear
php artisan view:clear
http://your-app.test/querycraft
http://your-app.test/querycraft
Or with a custom route prefix set in .env:
http://your-app.test/your-custom-prefix
/api/users)GET, POST, PUT, PATCH, DELETE)Authorization: Bearer token)POST/PUT requestsQueryCraft fires an internal request to your endpoint, collects all queries, runs them through all detectors, and displays the results instantly.
| Element | Description | |---|---| | Score card | 0β100 performance grade with letter (AβF) and emoji indicator | | Query count | Total number of queries executed by the endpoint | | Total time | Combined execution time of all queries in milliseconds | | Issue cards | Each problem with severity, stats, source location and fix suggestion | | Source Location | Exact file path and line number in your app (vendor files filtered out) | | All Queries | Collapsible list of every query fired with individual execution time |
When your endpoint crashes, QueryCraft catches it and displays:
ErrorException, QueryException)Set
APP_DEBUG=truein your.envfor full exception details.
When your endpoint returns a 4xx response (e.g. 422 validation error, 404), QueryCraft shows:
Click the βοΈ icon in the top-right header to open the config panel:
.env immediatelyAnalyze endpoints directly from your terminal without opening a browser.
php artisan querycraft:analyze
{--url= : The endpoint URL to analyze}
{--method=GET : HTTP method (GET, POST, PUT, PATCH, DELETE)}
{--user= : Authenticate as a specific user ID}
{--show-queries : Print all executed queries in the output}
{--body= : JSON body as an inline string}
{--body-file= : Path to a JSON file to use as the request body}
{--header=* : Custom headers in Key:Value format (repeatable)}
# Simple GET
php artisan querycraft:analyze --url=/users
# With authentication
php artisan querycraft:analyze --url=/dashboard --user=1
# POST with inline JSON body
php artisan querycraft:analyze --url=/api/posts --method=POST \
--body='{"title":"Hello","body":"World","category_id":1}'
# POST with many fields β use --body-file to keep it clean
php artisan querycraft:analyze --url=/api/orders --method=POST \
--body-file=./payload.json
# PUT with body and auth
php artisan querycraft:analyze --url=/api/users/1 --method=PUT --user=1 \
--body='{"name":"John","email":"[email protected]"}'
# With custom headers
php artisan querycraft:analyze --url=/api/secret \
--header="Authorization:Bearer your-token" \
--header="X-Team-Id:42"
# Everything combined
php artisan querycraft:analyze --url=/api/orders --method=POST --user=1 \
--body-file=./payload.json \
--header="X-Source:querycraft" \
--show-queries
--body-fileWhen your request has many fields, create a payload.json file instead of cramming everything inline:
{
"customer_id": 5,
"shipping_address": {
"street": "123 Main St",
"city": "Paris",
"zip": "75001"
},
"items": [
{ "product_id": 1, "qty": 2, "price": 29.99 },
{ "product_id": 3, "qty": 1, "price": 49.99 }
],
"coupon": "SAVE10",
"notes": "Leave at door"
}
Then run:
php artisan querycraft:analyze --url=/api/orders --method=POST \
--body-file=./payload.json --user=1 --show-queries
Use
--bodyfor small payloads. Use--body-filefor large or complex payloads β it avoids shell escaping issues and is much easier to read and reuse.
π Analyzing: GET /users
β
Response: 200
π Summary:
+----------------+----------+
| Metric | Value |
+----------------+----------+
| Total Queries | 23 |
| Total Time | 182.5 ms |
| Avg Query Time | 7.93 ms |
| Response Status| 200 |
+----------------+----------+
β οΈ Found 2 issue(s):
π΄ Issue #1: N+1
ββββββββββββββββββββββββββββββββββββββ
Severity: HIGH
Occurrences: 20
Total Time: 140ms
Location: /app/Http/Controllers/UserController.php:45
Query:
select * from `companies` where `id` = ?
π‘ Suggestion:
Add eager loading: ->with('company')
ββββββββββββββββββββββββββββββββββββββ
β‘ Performance Score: 62/100 (Grade: D) π
ββββββββββββββββββββββββββββββββββββββ
π― Top Improvements:
β’ Too many queries (+25 points)
β’ 2 performance issues detected (+20 points)
If your endpoint requires authentication, pass
--user=1to run as a specific user. QueryCraft callsauth()->login($user)before firing the request.
After publishing, the config file is at config/querycraft.php. All values can be overridden via .env:
# Enable or disable the package entirely
QUERY_DEBUGGER_ENABLED=true
# Detectors β toggle individually
QUERYCRAFT_DETECTOR_N1=true
QUERYCRAFT_DETECTOR_SLOW_QUERY=true
QUERYCRAFT_DETECTOR_MISSING_INDEX=true
QUERYCRAFT_DETECTOR_DUPLICATE_QUERY=true
# Thresholds
QUERY_DEBUGGER_N1_THRESHOLD=5 # Flag N+1 after this many repetitions (default: 5)
QUERY_DEBUGGER_SLOW_THRESHOLD=100 # Flag queries slower than this in ms (default: 100)
QUERYCRAFT_DUPLICATE_COUNT=2 # Flag duplicates after this many repeats (default: 2)
# Score weights β must total 100
QUERYCRAFT_WEIGHT_QUERY_COUNT=40
QUERYCRAFT_WEIGHT_QUERY_TIME=30
QUERYCRAFT_WEIGHT_ISSUES=30
# Dashboard route prefix (default: querycraft)
QUERYCRAFT_DASHBOARD_ROUTE=querycraft
// config/querycraft.php
return [
'enabled' => env('QUERY_DEBUGGER_ENABLED', true),
'detectors' => [
'n1' => env('QUERYCRAFT_DETECTOR_N1', true),
'slow_query' => env('QUERYCRAFT_DETECTOR_SLOW_QUERY', true),
'missing_index' => env('QUERYCRAFT_DETECTOR_MISSING_INDEX', true),
'duplicate_query'=> env('QUERYCRAFT_DETECTOR_DUPLICATE_QUERY', true),
],
'thresholds' => [
'n1_count' => env('QUERY_DEBUGGER_N1_THRESHOLD', 5),
'slow_query_ms' => env('QUERY_DEBUGGER_SLOW_THRESHOLD', 100),
'duplicate_count' => env('QUERYCRAFT_DUPLICATE_COUNT', 2),
],
'weights' => [
'query_count' => env('QUERYCRAFT_WEIGHT_QUERY_COUNT', 40),
'query_time' => env('QUERYCRAFT_WEIGHT_QUERY_TIME', 30),
'issues' => env('QUERYCRAFT_WEIGHT_ISSUES', 30),
],
];
Tip: All settings can also be changed from the dashboard βοΈ config panel β changes are saved directly to your
.env.
Normalizes every query (replaces values with ?) and groups them by pattern. If the same pattern fires more than n1_count times, it's flagged. The exact file and line in your app that triggered the repeated query is shown.
Example:
// β N+1 β fires one query per user
$users = User::all();
foreach ($users as $user) {
echo $user->company->name;
}
// β
Fix β one query total
$users = User::with('company')->get();
| Count | Severity | |---|---| | 5β10Γ | low | | 10β20Γ | medium | | 20β50Γ | high | | 50Γ+ | critical |
Flags any query exceeding slow_query_ms milliseconds (default: 100ms). Automatically suggests a fix based on the query structure.
| Time | Severity | |---|---| | > 200ms | low | | > 500ms | medium | | > 1000ms | high | | > 1000ms | critical |
Suggestions shown:
SELECT * β use specific columnsORDER BY without LIMIT β add a limitLIKE queries β consider full-text searchCOUNT(*) on large tables β consider cachingRuns MySQL EXPLAIN on each query and flags full table scans, filesorts, and temporary table usage.
Triggers:
type = ALL with more than 1,000 rows examinedExtra contains Using filesortExtra contains Using temporaryExample:
User::where('email', $email)->first(); // no index on email
// Fix β in a migration:
$table->index('email');
$table->index(['status', 'created_at']); // composite
| Rows examined | Severity | |---|---| | > 1,000 | low | | > 10,000 | medium | | > 100,000 | high | | > 100,000 | critical |
Creates an md5 fingerprint of sql + bindings. If the exact same query with the same parameter values runs more than duplicate_count times (default: 2), it's flagged.
Example:
// β Duplicate β same query + same values fired twice
$settings = Setting::all();
// ... somewhere else in the same request ...
$settings = Setting::all();
// β
Fix
$settings = Cache::remember('settings', 3600, fn() => Setting::all());
Calculated as a weighted average across three dimensions:
| Dimension | Default Weight | Description | |---|---|---| | Query Count | 40% | Fewer queries = higher score | | Query Time | 30% | Faster total time = higher score | | Issues Found | 30% | Fewer/lower severity issues = higher score |
| Score | Grade | Status | |---|---|---| | 90β100 | A π’ | Excellent | | 80β89 | B π‘ | Good | | 70β79 | C π‘ | Acceptable | | 60β69 | D π | Below average | | 0β59 | F π΄ | Critical issues |
Weights are configurable from the dashboard config panel or via .env.
QueryCraft is intended for local development only. Always disable it in production:
# .env.production
QUERY_DEBUGGER_ENABLED=false
composer update romdh4ne/laravel-querycraft
php artisan vendor:publish --tag=querycraft-views --force
php artisan vendor:publish --tag=querycraft-assets --force
php artisan config:clear
php artisan view:clear
composer remove romdh4ne/laravel-querycraft
rm config/querycraft.php
rm -rf resources/views/vendor/querycraft
rm -rf public/vendor/querycraft
# Remove QUERYCRAFT_* and QUERY_DEBUGGER_* lines from your .env
git clone https://github.com/YOUR_USERNAME/laravel-querycraft.git
cd laravel-querycraft
composer install
In a separate Laravel app, add to composer.json:
"repositories": [
{
"type": "path",
"url": "../laravel-querycraft",
"options": { "symlink": true }
}
]
Then:
composer require romdh4ne/laravel-querycraft:@dev
php artisan vendor:publish --tag=querycraft-config
php artisan vendor:publish --tag=querycraft-assets
php artisan config:clear
php artisan serve
Visit http://localhost:8000/querycraft. Any change you make in the package reflects instantly thanks to the symlink.
QueryCraft is open-source software licensed under the MIT license.
Made by Romdh4ne
If this package helps you, give it a β on GitHub!