Given the following code:
DB::table('users')->get();
I want to get the raw SQL query string that the database query builder above will generate. In this example, it would be SELECT * FROM users
.
How do I do this?
To output to the screen the last queries ran you can use this:
DB::enableQueryLog(); // Enable query log
// Your Eloquent query executed by using get()
dd(DB::getQueryLog()); // Show results of log
I believe the most recent queries will be at the bottom of the array.
You will have something like that:
array(1) {
[0]=>
array(3) {
["query"]=>
string(21) "select * from "users""
["bindings"]=>
array(0) {
}
["time"]=>
string(4) "0.92"
}
}
(Thanks to Joshua's comment below.)
Use the toSql()
method on a QueryBuilder
instance.
DB::table('users')->toSql()
would return:
select * from `users`
This is easier than wiring up an event listener, and also lets you check what the query will actually look like at any point while you're building it.
Note: This method works for query builder or Eloquent, however toSql()
is used instead of first()
or get()
. You cannot run the query and also get the SQL at the same time using this method.
DB::QueryLog()
works only after you execute the query using $builder->get()
.
If you want to get the raw query before or without executing the query, you can use the $builder->toSql()
method.
Example to get the raw SQL and to replace '?' with actual binding values:
$query = str_replace(array('?'), array('\'%s\''), $builder->toSql());
$query = vsprintf($query, $builder->getBindings());
dump($query);
$result = $builder->get();
Or you can deliberately trigger an error, for example, by using a non-existent table or column. Then you can see the generated query in the exception message.
You can listen to the 'illuminate.query' event. Before the query add the following event listener:
Event::listen('illuminate.query', function($query, $params, $time, $conn)
{
dd(array($query, $params, $time, $conn));
});
DB::table('users')->get();
This will print out something like:
array(4) {
[0]=>
string(21) "select * from "users""
[1]=>
array(0) {
}
[2]=>
string(4) "0.94"
[3]=>
string(6) "sqlite"
}
If you are trying to get the Log using Illuminate without Laravel use:
\Illuminate\Database\Capsule\Manager::getQueryLog();
You could also nock up a quick function like so:
function logger()
{
$queries = \Illuminate\Database\Capsule\Manager::getQueryLog();
$formattedQueries = [];
foreach ($queries as $query) :
$prep = $query['query'];
foreach ($query['bindings'] as $binding) :
if (is_bool($binding)) {
$val = $binding === true ? 'TRUE' : 'FALSE';
} else if (is_numeric($binding)) {
$val = $binding;
} else {
$val = "'$binding'";
}
$prep = preg_replace("#\?#", $val, $prep, 1);
endforeach;
$formattedQueries[] = $prep;
endforeach;
return $formattedQueries;
}
EDIT
updated versions seem to have query logging disabled by default (the above returns an empty array). To turn back on, when initialising the Capsule Manager, grab an instance of the connection and call the enableQueryLog
method
$capsule::connection()->enableQueryLog();
EDIT AGAIN
Taking the actual question into consideration, you could actually do the following to convert the current single query instead of all previous queries:
$sql = $query->toSql();
$bindings = $query->getBindings();
If you use laravel 5.1 and MySQL you can use this function made by me:
/*
* returns SQL with values in it
*/
function getSql($model)
{
$replace = function ($sql, $bindings)
{
$needle = '?';
foreach ($bindings as $replace){
$pos = strpos($sql, $needle);
if ($pos !== false) {
if (gettype($replace) === "string") {
$replace = ' "'.addslashes($replace).'" ';
}
$sql = substr_replace($sql, $replace, $pos, strlen($needle));
}
}
return $sql;
};
$sql = $replace($model->toSql(), $model->getBindings());
return $sql;
}
As an input parameter you can use either of these
Illuminate\Database\Eloquent\Builder
Illuminate\Database\Eloquent\Relations\HasMany
Illuminate\Database\Query\Builder
This is the far best solution I can suggest to any one for debug-ing eloquent last query or final query although this has been discussed as well:
// query builder
$query = DB::table('table_name')->where('id', 1);
// binding replaced
$sql = str_replace_array('?', $query->getBindings(), $query->toSql());
// for laravel 5.8^
$sql = Str::replaceArray('?', $query->getBindings(), $query->toSql());
// print
dd($sql);
First You will need to enable the query log by calling:
DB::enableQueryLog();
after queries using the DB facade you can write:
dd(DB::getQueryLog());
the output will like below:
array:1 [▼
0 => array:3 [▼
"query" => "select * from `users` left join `website_user` on `users`.`id` = `website_user`.`user_id` left join `region_user` on `users`.`id` = `region_user`.`user_id` left ▶"
"bindings" => array:5 [▶]
"time" => 3.79
]
]
A 'macroable' replacement to get the SQL query with the bindings.
Add below macro function in AppServiceProvider
boot()
method.
\Illuminate\Database\Query\Builder::macro('toRawSql', function(){
return array_reduce($this->getBindings(), function($sql, $binding){
return preg_replace('/\?/', is_numeric($binding) ? $binding : "'".$binding."'" , $sql, 1);
}, $this->toSql());
});
Add an alias for the Eloquent Builder. (Laravel 5.4+)
\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
return ($this->getQuery()->toRawSql());
});
Then debug as usual. (Laravel 5.4+)
E.g. Query Builder
\Log::debug(\DB::table('users')->limit(1)->toRawSql())
E.g. Eloquent Builder
\Log::debug(\App\User::limit(1)->toRawSql());
Note: from Laravel 5.1 to 5.3, Since Eloquent Builder doesn't make use of the
Macroable
trait, cannot addtoRawSql
an alias to the Eloquent Builder on the fly. Follow the below example to achieve the same.
E.g. Eloquent Builder (Laravel 5.1 - 5.3)
\Log::debug(\App\User::limit(1)->getQuery()->toRawSql());
Simply you can do following stuff using toSql()
method,
$query = DB::table('users')->get();
echo $query->toSql();
If it's not working you can set-up the thing from laravel documentation.
Another way to do it is
DB::getQueryLog()
but if it's returns an empty array then by default it's disabled visit this,
just enable with DB::enableQueryLog()
and it will work :)
for more info visit Github Issue to know more about it.
Hope it helps :)
As of Laravel 5.8.15 the query builder now has dd
and dump
methods so you can do
DB::table('data')->where('a', 1)->dump();
In my opinion, this will be the best approach as a beginner:
echo "<pre>";
print_r($query->toSql());
print_r($query->getBindings());
This is also depicted here. https://stackoverflow.com/a/59207557/9573341
Add this function to your application and simply call.
function getQuery($sql){
$query = str_replace(array('?'), array('\'%s\''), $sql->toSql());
$query = vsprintf($query, $sql->getBindings());
return $query;
}
Output: "select * from user
where lang
= 'en' and status
= '1' order by updated_at
desc limit 25 offset 0"
The most easiest way is to make deliberate mistake. For example, I want to see the full SQL query of the following relation:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
I just to make a column to be not found, here I choose created_at
and I changed it to created_ats
by adding trailing s
to be:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_ats','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
So, the debuger will return the following error:
(4/4) ErrorException SQLSTATE[42S22]: Column not found: 1054 Unknown column 'eqtype_jobs.created_ats' in 'field list' (SQL: select
jobs
.*,eqtype_jobs
.set_id
aspivot_set_id
,eqtype_jobs
.job_id
aspivot_job_id
,eqtype_jobs
.created_ats
aspivot_created_ats
,eqtype_jobs
.updated_at
aspivot_updated_at
,eqtype_jobs
.id
aspivot_id
fromjobs
inner joineqtype_jobs
onjobs
.id
=eqtype_jobs
.job_id
whereeqtype_jobs
.set_id
= 56 order bypivot_created_at
desc limit 20 offset 0) (View: /home/said/www/factory/resources/views/set/show.blade.php)
The above error message returns the full SQL query with the mistake
SQL: select jobs.*, eqtype_jobs.set_id as pivot_set_id, eqtype_jobs.job_id as pivot_job_id, eqtype_jobs.created_ats as pivot_created_ats, eqtype_jobs.updated_at as pivot_updated_at, eqtype_jobs.id as pivot_id from jobs inner join eqtype_jobs on jobs.id = eqtype_jobs.job_id where eqtype_jobs.set_id = 56 order by pivot_created_at desc limit 20 offset 0
Now, just remove the extra s
from created_at and test this SQL as you like in any SQL editor such as phpMyAdmin SQL editor!
Notice:
The solution has been tested with Laravel 5.4.
This is the function, I placed in my base model class. Simply pass the query builder object into it and the SQL string will be returned.
function getSQL($builder) {
$sql = $builder->toSql();
foreach ( $builder->getBindings() as $binding ) {
$value = is_numeric($binding) ? $binding : "'".$binding."'";
$sql = preg_replace('/\?/', $value, $sql, 1);
}
return $sql;
}
There's a lot of information already answered, will just post my own findings that i've been using whenever i need to output the sql query before it's being executed.
Consider below sample:
$user = DB::table('user')->where('id',1);
echo $user->toSql();
echo $user->toSql() = This will just out put the raw query but will not show the parameter(s) passed.
To output the query with the parameter being passed we can use laravel getBindings() and helper str_replace_array like this:
$queryWithParam = str_replace_array('?',$user->getBindings(),$user->toSql());
echo $queryWithParam;
Hope this also helps.
For laravel 5.5.X
If you would like to receive each SQL query executed by your application, you may use the listen method. This method is useful for logging queries or debugging. You may register your query listener in a service provider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
});
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}
My way of doing this, based on the log view, only needs to modify the file app/Providers/AppServiceProvider.php
:
app/Providers/AppServiceProvider.php
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
DB::listen(function ($query) {
$querySql = str_replace(['?'], ['\'%s\''], $query->sql);
$queryRawSql = vsprintf($querySql, $query->bindings);
Log::debug('[SQL EXEC]', [
"raw sql" => $queryRawSql,
"time" => $query->time,
]
);
});
}
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, username '))
->where('uid', '>=', 10)
->limit(100)
->groupBy('username')
->get()
;
dd($users);
storage/logs/laravel-2019-10-27.log
:[2019-10-27 17:39:17] local.DEBUG: [SQL EXEC] {"raw sql":"select count(*) as user_count, username from `users` where `uid` >= '10' group by `username` limit 100","time":304.21}
As much as I love this framework, I hate when it acts like crap.
DB::enableQueryLog()
is totally useless. DB::listen
is equally useless. It showed part of the query when I said $query->count()
, but if I do $query->get()
, it has nothing to say.
The only solution that appears to work consistently is to intentionally put some syntax or other error in the ORM parameters, like an nonexistent column/table name, run your code on the command line while in debug mode, and it will spit out the SQL error with the full frickin' query finally. Otherwise, hopefully the error appears in the log file if ran from the web server.
If you are using tinker and want to log the SQL query formed you can do
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.5 — cli) by Justin Hileman
>>> DB::listen(function ($query) { dump($query->sql); dump($query->bindings); dump($query->time); });
=> null
>>> App\User::find(1)
"select * from `users` where `users`.`id` = ? limit 1"
array:1 [
0 => 1
]
6.99
=> App\User {#3131
id: 1,
name: "admin",
email: "[email protected]",
created_at: "2019-01-11 19:06:23",
updated_at: "2019-01-11 19:06:23",
}
>>>
You can use toSql method - the easiest way
DB::table('users')->toSql();
And also if you have bindings in your query and want to see the query with bindings. You cant use somthing like that:
$query = DB::table('table')->whereIn('some_field', [1,2,30]);
$sql_with_bindings = str_replace_array('?', $query->getBindings(), $query->toSql());
dd($sql_with_bindings);
If you are not using Laravel but using Eloquent package then:
use \Illuminate\Database\Capsule\Manager as Capsule;
use \Illuminate\Events\Dispatcher;
use \Illuminate\Container\Container;
$capsule = new Capsule;
$capsule->addConnection([
// connection details
]);
// Set the event dispatcher used by Eloquent models... (optional)
$capsule->setEventDispatcher(new Dispatcher(new Container));
// Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();
// Setup the Eloquent ORM...(optional unless you've used setEventDispatcher())
$capsule->bootEloquent();
// Listen for Query Events for Debug
$events = new Dispatcher;
$events->listen('illuminate.query', function($query, $bindings, $time, $name)
{
// Format binding data for sql insertion
foreach ($bindings as $i => $binding) {
if ($binding instanceof \DateTime) {
$bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
} else if (is_string($binding)) {
$bindings[$i] = "'$binding'";`enter code here`
}
}
// Insert bindings into query
$query = str_replace(array('%', '?'), array('%%', '%s'), $query);
$query = vsprintf($query, $bindings);
// Debug SQL queries
echo 'SQL: [' . $query . ']';
});
$capsule->setEventDispatcher($events);
echo User::where('status', 1)->toSql();
– Muhammad Shahzad