在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?
Posted
技术标签:
【中文标题】在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?【英文标题】:How can I get the raw query string from Laravel's query builder BEFORE executing the query? 【发布时间】:2013-12-01 11:34:01 【问题描述】:我有一个由几个条件创建的复杂查询,我想从即将执行的构建器对象中获取最终的 SQL 查询。我可以这样做吗?
【问题讨论】:
【参考方案1】:你可以做到:
$query = DB::table('brands')
->join('products','a','=','c')
->whereNull('whatever');
echo $query->toSql();
但是 Laravel 不会在查询中显示参数,因为它们是在准备好查询之后绑定的。
所以你也可以这样做:
print_r( $query->getBindings() );
【讨论】:
返回带有占位符而不是实际参数的查询 要使用 getBinings() 我需要使用任何名称空间或其他东西吗? @MatteoRivaObject of class Illuminate\Database\Eloquent\Builder could not be converted to string
这就是我尝试从模型查询中获取toSQL
时得到的结果。它给了我一个 Builder 实例。如何从构建器实例中获取 sql?
原来我用的是toSQL
而不是toSql
,这给了我完全不同的结果【参考方案2】:
对于调试,这可能会非常方便,因为它会返回带有绑定的 SQL,因此您可以立即将其放入数据库控制台。
/**
* Combines SQL and its bindings
*
* @param \Eloquent $query
* @return string
*/
public static function getEloquentSqlWithBindings($query)
return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding)
return is_numeric($binding) ? $binding : "'$binding'";
)->toArray());
【讨论】:
这对我有用。比上面接受的答案要好得多。 我得到的 vsprintf 参数太少 这就是要找的东西【参考方案3】:我想在我的查询中添加换行符和大写 mysql 关键字,因此添加到 andi79h 的答案中。不是特别优雅,我敢肯定有很多边缘情况无法使用,但我仍然觉得它很有用......
function debug_query($query)
$query = vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding)
return is_numeric($binding) ? $binding : "'$binding'";
)->toArray());
$double_linebreak_words = ['(', ')'];
$double_linebreak_words_replace = array_map(function($str) return php_EOL . $str . PHP_EOL; , $double_linebreak_words);
$query = str_replace($double_linebreak_words, $double_linebreak_words_replace, $query);
$mysql_keywords = ['ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC', 'AUTO_INCREMENT', 'BDB', 'BERKELEYDB', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB', 'BOTH', 'BTREE', 'BY', 'CASCADE', 'CASE', 'CHANGE', 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN', 'COLUMNS', 'CONSTRAINT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY_HOUR', 'DAY_MINUTE', 'DAY_SECOND', 'DEC', 'DECIMAL', 'DEFAULT', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'ELSE', 'ENCLOSED', 'ERRORS', 'ESCAPED', 'EXISTS', 'EXPLAIN', 'FALSE', 'FIELDS', 'FLOAT', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULLTEXT', 'FUNCTION', 'GEOMETRY', 'GRANT', 'GROUP', 'HASH', 'HAVING', 'HELP', 'HIGH_PRIORITY', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF', 'IGNORE', 'INDEX', 'INFILE', 'INNER', 'INNODB', 'INSERT', 'INTEGER', 'INTERVAL', 'INTO', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEFT', 'LIKE', 'LIMIT', 'LINES', 'LOAD', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT', 'LOW_PRIORITY', 'MASTER_SERVER_ID', 'MATCH', 'MEDIUMBLOB', 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_SECOND', 'MOD', 'MRG_MYISAM', 'NATURAL', 'NOT', 'NULL', 'NUMERIC', 'ON', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 'ORDER', 'OUTER', 'OUTFILE', 'PRECISION', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', 'PURGE', 'READ', 'REAL', 'REFERENCES', 'REGEXP', 'RENAME', 'REPLACE', 'REQUIRE', 'RESTRICT', 'RETURNS', 'REVOKE', 'RIGHT', 'RLIKE', 'RTREE', 'SELECT', 'SET', 'SHOW', 'SMALLINT', 'SOME', 'SONAME', 'SPATIAL', 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT', 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'STRIPED', 'TABLE', 'TABLES', 'TERMINATED', 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING', 'TRUE', 'TYPES', 'UNION', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USER_RESOURCES', 'USING', 'VALUES', 'VARBINARY', 'VARCHAR', 'VARCHARACTER', 'VARYING', 'WARNINGS', 'WHEN', 'WHERE', 'WITH', 'WRITE', 'XOR', 'YEAR_MONTH', 'ZEROFILL', 'INT', 'OR', 'IS', 'IN'];
$mysql_keywords = array_map(function($str) return " $str "; , $mysql_keywords);
$mysql_keywords_lc = array_map(function($str) return strtolower($str); , $mysql_keywords);
$query = str_replace($mysql_keywords_lc, $mysql_keywords, $query);
$linebreak_before_words = ['INNER JOIN', 'LEFT JOIN', 'OUTER JOIN', 'RIGHT JOIN', 'WHERE', 'FROM', 'GROUP BY', 'SELECT'];
$linebreak_before_words_replace = array_map(function($str) return PHP_EOL . $str; , $linebreak_before_words);
$query = str_replace($linebreak_before_words, $linebreak_before_words_replace, $query);
$linebreak_after_words = [','];
$linebreak_after_words_replace = array_map(function($str) return $str . PHP_EOL; , $linebreak_after_words);
$query = str_replace($linebreak_after_words, $linebreak_after_words_replace, $query);
$query = str_replace('select ', 'SELECT ', $query);
return $query;
【讨论】:
【参考方案4】:从 andi79h 的回答中回过头来。该函数运行良好,但它假设绑定没有任何可能破坏查询的引号。
我在 $bindings 中添加了“addslashes”以使其更安全一些。虽然理想情况下,如果您有一个活动连接可以使用,它应该通过 mysqli_real_escape_string() 运行。 https://www.php.net/manual/en/mysqli.real-escape-string.php
/**
* Combines SQL and its bindings
*
* @param \Eloquent $query
* @return string
*/
public static function getEloquentSqlWithBindings($query)
return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding)
$binding = addslashes($binding);
return is_numeric($binding) ? $binding : "'$binding'";
)->toArray());
【讨论】:
【参考方案5】:更新@andi79h 的答案以处理查询中有 % 的情况。 (例如,DATE_FORMAT(report_date, '%c/%e/%Y')
会导致参数太少的错误。
/**
* Combines SQL and its bindings
*
* @param \Eloquent $query
* @return string
*/
public static function getEloquentSqlWithBindings($query)
return vsprintf(str_replace('?', '%s', str_replace('%', '%%', $query->toSql())), collect($query->getBindings())->map(function ($binding)
return is_numeric($binding) ? $binding : "'$binding'";
)->toArray());
【讨论】:
以上是关于在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用查询构建在 Laravel 中执行 WHERE NOT