laramint/queryguard
| Install | |
|---|---|
composer require laramint/queryguard |
|
| Latest Version: | v0.1.0 |
| PHP: | ^8.2 |
| License: | MIT |
| Last Updated: | May 19, 2026 |
| Links: | GitHub · Packagist |
QueryGuard
Zero-config CI gate for Laravel. Auto-instruments your test suite, baselines query counts per test, and fails PRs that introduce N+1s or query regressions — without you adding a single assertion.
Existing Laravel query tools either run only in dev mode (beyondcode/laravel-query-detector, Debugbar) or require devs to opt-in per test with manual assertions (mattiasgeniar/phpunit-query-count-assertions). Neither catches regressions automatically in CI on an existing suite.
QueryGuard does. Install, baseline once, and from then on every PR that pushes a test's query count past its baseline (or introduces a new N+1 pattern) fails the build with a precise diff.
Install
composer require --dev laramint/queryguard
Register the PHPUnit extension in phpunit.xml:
<extensions>
<bootstrap class="QueryGuard\PHPUnit\QueryGuardExtension"/>
</extensions>
Usage
# Record the baseline (do this once, commit the file).
php artisan queryguard:baseline
# In CI, this exits non-zero on regression:
php artisan queryguard:check
# Or run phpunit directly with the env var:
QUERYGUARD_MODE=check vendor/bin/phpunit
Commit tests/.queryguard-baseline.json to git — PR diffs naturally show "this test went from 4 to 17 queries."
Per-test budgets (optional)
use QueryGuard\Attributes\QueryBudget;
#[QueryBudget(max: 5)]
public function test_index_is_fast(): void { /* ... */ }
GitHub Actions
- name: QueryGuard
run: |
php artisan queryguard:check --markdown > queryguard.md || EXIT=$?
gh pr comment "$PR_NUMBER" --body-file queryguard.md
exit ${EXIT:-0}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ github.token }}
Configuration
php artisan vendor:publish --tag=queryguard-config
Then edit config/queryguard.php for tolerances, ignore patterns, slow-query threshold, and N+1 detection threshold.
How it works
- The PHPUnit extension hooks
testPrepared/testFinishedand registers aDB::listencallback that records every query per-test. - Each query's SQL is normalized into a stable signature (literals stripped,
IN (?,?,?)collapsed, keywords lowercased) so the same logical query matches across runs. - At end of run, the recorded profiles are either written to
tests/.queryguard-baseline.json(inbaselinemode) or diffed against it (incheckmode). - Any of the following exits the run non-zero:
- A test's query count exceeds
baseline + tolerance - The same query signature is executed more than
n_plus_one.thresholdtimes in a single test - A
#[QueryBudget]is exceeded
- A test's query count exceeds
Slow queries and new query signatures are reported as warnings (non-fatal) by default.
License
MIT.
Related Packages
Warm-worker test engine for Laravel + Pest — boots the app once per process and...
A powerful Laravel package to accelerate the development and testing process of...
PHP performance tool analyser your script on time, memory usage and db query. Su...
Disposable email inboxes for testing auth flows in CI — OTPs and magic links aut...
Gesso — OpenAPI 3.0/3.1/3.2 contract testing for PHP. Framework-independent core...