Last active
September 26, 2024 12:40
-
-
Save JesseObrien/7418983 to your computer and use it in GitHub Desktop.
Bind parameters into the SQL query for Laravel ORM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class MyModel extends Eloquent { | |
public function getSql() | |
{ | |
$builder = $this->getBuilder(); | |
$sql = $builder->toSql(); | |
foreach($builder->getBindings() as $binding) | |
{ | |
$value = is_numeric($binding) ? $binding : "'".$binding."'"; | |
$sql = preg_replace('/\?/', $value, $sql, 1); | |
} | |
return $sql; | |
} | |
} | |
When binding is a bool, its print an empty value.
Follow my adaptation fixing this issue:
public static function eloquentSqlWithBindings($queryBuilder)
{
$sql = str_replace('?', '%s', $queryBuilder->toSql());
$handledBindings = array_map(function ($binding) {
if (is_numeric($binding)) {
return $binding;
}
if (is_bool($binding)) {
return ($binding) ? 'true' : 'false';
}
return "'{$binding}'";
}, $queryBuilder->getBindings());
return vsprintf($sql, $handledBindings);
}
@danilopinotti the query fails on the following select:
DB::raw("(STR_TO_DATE(date_string, '%m/%d/%Y')) AS date_time")
error:
vsprintf(): Too few arguments
Here's updated version which fixes quite a few binding value bugs and also aligns more closely with how Laravel serializes true
/ false
values into 1
/ 0
\Illuminate\Database\Query\Builder::macro('fullSql', function () {
$sql = str_replace(['%', '?'], ['%%', '%s'], $this->toSql());
$handledBindings = array_map(function ($binding) {
if (is_numeric($binding)) {
return $binding;
}
$value = str_replace(['\\', "'"], ['\\\\', "\'"], $binding);
return "'{$value}'";
}, $this->getConnection()->prepareBindings($this->getBindings()));
$fullSql = vsprintf($sql, $handledBindings);
return $fullSql;
});
\Illuminate\Database\Query\Builder::macro('ddd', function () {
dd($this->fullSql());
});
Examples:
dump(\DB::query()->where([
'number' => 12,
'decimal' => 5.5,
'bool_true' => true,
'bool_false' => false,
'carbon' => now(),
'polymorphic_classable' => 'App\User',
'with_sprint_formats' => '%M %e %y',
'raw' => \DB::raw('some raw expression'),
'question_string' => 'hello?',
'single_quote' => "'",
'stringable_class' => new class {
public function __toString()
{
return 'foobar';
}
}
])->fullSql());
$query->ddd(); // will dump and die the full sql.
$query->dd(); // Built into Laravel already, will print the sql / bindings seperately.
Output:
select * where (`number` = 12 and `decimal` = 5.5 and `bool_true` = 1 and `bool_false` = 0 and `carbon` = '2020-01-14 23:09:09' and `polymorphic_classable` = 'App\\User' and `with_sprint_formats` = '%M %e %y' and `raw` = some raw expression and `question_string` = 'hello?' and `single_quote` = '\'' and `stringable_class` = 'foobar')
@garygreen awesome. Thanks man!
@JesseObrien thank you!!.
fullSql
@garygreen Looks like it does not work with '%like%' case, when I use whereRaw('name like %test-name%'), it replaces % with %%
laravel-debugbar and/or Telescope will log your queries out too :) This is helpful snippet. Thank you
Slight variation for Laravel 9:
\Illuminate\Database\Query\Builder::macro('toRawSql', fn (): string => str($this->toSql())
->replaceArray('?', collect($this->getConnection()->prepareBindings($this->getBindings()))
->map(fn ($binding) => is_numeric($binding) ? $binding : str($binding)->replace(['\\', "'"], ['\\\\', "\'"])->prepend("'")->append("'")->__toString())
->toArray()
)
);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ijasxyz thank you!!