Weave 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 Dump Sql Laravel Package

guanguans/laravel-dump-sql

Laravel package that reveals the real SQL behind your query builder and Eloquent queries. It adds dumpSql() and ddSql() macros so you can print or dump fully interpolated SQL, bindings included, plus helpers for logging queries—making database debugging faster and clearer.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (dev-only, this is a debugging aid):

composer require guanguans/laravel-dump-sql --dev

The service provider is auto-discovered and registers dumpSql() / ddSql() macros on Illuminate\Database\Query\Builder and Illuminate\Database\Eloquent\Builder. No config file, no migrations, no assets — just macros.

Where to look first

  • src/ServiceProvider.php — where the macros are registered.
  • The Macros support class (or trait) — this is where the binding-interpolation lives; read it before trusting the output.
  • The README examples — the whole API is essentially two methods.

First use case — the everyday "why is this query empty/wrong" loop. Instead of the usual dance:

$q = User::where('status', 'active')->whereIn('role', ['admin', 'editor']);
[$sql, $bindings] = [$q->toSql(), $q->getBindings()];
// now manually substitute ?'s ... ugh

you just do:

User::where('status', 'active')->whereIn('role', ['admin', 'editor'])->dumpSql();
// select * from `users` where `status` = 'active' and `role` in ('admin', 'editor')

Drop it in tinker, copy the string into your DB client, and run it.

Implementation Patterns

Inline debugging on the builder chain — call dumpSql() before the terminal call (get, first, paginate). It returns the builder, so the chain still works:

$users = User::with('posts')
    ->where('created_at', '>', now()->subMonth())
    ->dumpSql()          // prints SQL, continues
    ->get();

Hard stop with ddSql() — same thing but dd()s the SQL. Useful in a failing test or a controller you're poking at:

Order::whereBetween('total', [100, 500])->ddSql();

Verify what a scope / macro actually generates — this is the highest-value daily use. Any time you're unsure whether a local scope is adding the right whereNotNull or orWhere grouping, dump it:

Order::query()->active()->thisMonth()->dumpSql();

Assert SQL shape in tests — grab the string and assert on it:

$sql = Order::where('status', 'paid')->dumpSql(); // returns builder in this pkg; use toSql()/getBindings or capture output
$this->assertStringContainsString('`status` = \'paid\'', (string) $sql);

(If dumpSql() only returns the builder here, wrap with output buffering, or prefer toSql() for assertions — see gotchas.)

Log full statements in a local environment — pair with DB::listen() when you want every query, not just one:

DB::listen(fn ($q) => logger()->debug(
    vsprintf(str_replace('?', "'%s'", $q->sql), $q->bindings)
));

This package is the one-shot version; the listener is the "all queries" version.

Copy-paste into your SQL client — the primary workflow. Run the dumped SQL in TablePlus/DataGrip/psql and check row counts or the EXPLAIN plan. This is where the package saves the most time versus hand-substituting bindings.

Nested / eager-loaded queries — dump the base builder, then dump individual relations by calling methods directly:

$q = User::with('posts.tags');
$q->dumpSql();
$q->getRelation('posts') // inspect the relation builder separately if needed

Gotchas and Tips

Laravel 10.15+ already has this built in. Illuminate\Database\Query\Builder ships toRawSql(), dumpRawSql(), and ddRawSql() — the exact same idea. If you're on a recent Laravel, prefer the native methods and skip the dependency. Check method_exists($builder, 'toRawSql') before reaching for the package.

It's naive string interpolation, not real parameter binding. Strings get wrapped in quotes, but escaping of edge cases (embedded quotes, backslashes, binary blobs, DateTime objects, JSON columns) is best-effort. Never run package output against production to "check" something — the dumped SQL is for humans and your dev DB only.

dumpSql() returns the builder, not a string. It prints and returns $this. If you want the string to assert on or log, use ->toSql() + ->getBindings() or capture the dumped output. Don't try to (string) $builder->dumpSql().

Add it with --dev. There is no reason for this to be in a production composer install. Keep it in require-dev and rely on Laravel's auto-discovery only in local/testing.

Watch the SQL dialect. It mirrors Laravel's grammar, which mirrors your configured connection. If your default connection is sqlite but you're debugging against MySQL, the dumped quoting will be "double" vs `backtick` and won't paste cleanly into the other engine. Set DB_CONNECTION to what you actually ship against.

Bindings won't reflect runtime state for now()-style calls. where('created_at', '>', now()) interpolates the current timestamp frozen at the moment of dumpSql(). Re-running the SQL later in your client can therefore return different rows than the app did. Keep that in mind when reproducing bugs.

Complex bindings render awkwardly. whereIn with subqueries, whereJsonContains, and raw expressions often produce output that isn't valid copy-paste SQL — the placeholder is the outer binding while the fragment is opaque. For those, fall back to toSql() + reading the bindings array by eye, or enable Laravel's DB::listen() and grab $query->sql/$query->bindings separately.

No extension points worth caring about. It's macros — nothing is configurable. If its interpolation is wrong for your case, fork the one class or (better) wrap toRawSql()/a small custom macro in your own AppServiceProvider::boot() and control the formatting yourself.

Debugging a "blank" dump. If dumpSql() prints nothing, confirm (a) the macro is actually registered (Builder::hasMacro('dumpSql')), (b) you're calling it on a Builder, not a relationship proxy or Collection, and (c) your output isn't swallowed by an output-buffering layer (Octane, some test harnesses, or a custom dd override). In Octane, restart workers after installing so the macros register.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console