快捷打印 Laravel 中的数据库查询(SQL)语句

闲话少叙,直接入题。首先,为什么要打印 Laravel 中 Query Builder 构建的 SQL 语句?

答案很简单,就是我要知道到底执行了什么 SQL 语句,这样我就能合适地写 Query Builder、在适合用『热加载』的场景不会误用『懒加载』。

热加载

$user = User::where('name', 'Eric')->with('articles')->first();

上面就是热加载的例子――获取第一个名为『Eric』的用户信息同时,把他的文章也全都取到。

这样的使用场景下,Query Builder 只使用了类似下面的两条 SQL 语句。

  1. select * from users where name = 'Eric' limit 1;
  2. select * from articles where user_id in (22);

就是说当你用 $user->articles 遍历用户文章时,不会再请求数据库了。

懒加载

懒加载和热加载是相对的。下面就是懒加载的例子。

$user = User::where('name', 'Eric')->first();

// 在 Blade 模版里遍历打印出用户文章
@\foreach($user->articles as $article)
    // ...
@endforeach

这种用法能达到效果,但是效率会变低――每次遍历、处理的一篇文章信息,都是向数据库执行一次 SQL 得到的。

如果用户有 N 篇文章,就要执行 N 次 SQL 查询,再加上之前请求用户信息的 1 次 SQL,这就是所谓的『N+1』问题。

明明能 2 次完成的事,就不要再花 N+1 次了。

打印 SQL

像上面的状况,如果我们知道底层是怎样执行 SQL 语句的,也许就不会发生了。这就为我们找出了一个有必要打印 SQL 语句来看的理由。

既然文章题目是『快捷打印 SQL』,自然配置起来也很简单。配置到最后的结果的是:

当你的 APP_ENV 设置为 local、请求 URL 后面紧跟 ?sql-debug=1 时,就会打印出请求处理逻辑中涉及到的所有数据库查询语句。

配置

AppServiceProvider 的 boot 方法内写入

use DB;
use Event;

if ( env('APP_ENV') === 'local' ) {
    DB::connection()->enableQueryLog();
    Event::listen('kernel.handled', function ( $request, $response ) {
        if ( $request->has('sql-debug') ) {
            $queries = DB::getQueryLog();
            if (!empty($queries)) {
                foreach ($queries as &$query) {
                    $query['full_query'] = vsprintf(str_replace('?', '%s', $query['query']), $query['bindings']);
                }
            }
            dd($queries);
        }
    });
}

注意:路由有两种形式 —— 基于闭包(Closure)和基于控制器动作(Controller Action)的。上面的配置只对基于控制器动作的路由有效。

参考链接

https://laravel-news.com/quickly-dumping-l...

tags: laravel,sql