5天前 ⋅ 375 ⋅ 11 ⋅ 11laravel
閒話少敘,直接入題。首先,爲何要打印 Laravel 中 Query Builder 構建的 SQL 語句?sql
答案很簡單,就是我要知道到底執行了什麼 SQL 語句,這樣我就能合適地寫 Query Builder、在適合用『熱加載』的場景不會誤用『懶加載』。數據庫
$user = User::where('name', 'Eric')->with('articles')->first();
上面就是熱加載的例子――獲取第一個名爲『Eric』的用戶信息同時,把他的文章也全都取到。閉包
這樣的使用場景下,Query Builder 只使用了相似下面的兩條 SQL 語句。ide
select * from users where name = 'Eric' limit 1;
select * from articles where user_id in (22);
就是說當你用 $user->articles
遍歷用戶文章時,不會再請求數據庫了。post
懶加載和熱加載是相對的。下面就是懶加載的例子。ui
$user = User::where('name', 'Eric')->first(); // 在 Blade 模版裏遍歷打印出用戶文章 @\foreach($user->posts as $post) // ... @endforeach
這種用法能達到效果,可是效率會變低――每次遍歷、處理的一篇文章信息,都是向數據庫執行一次 SQL 獲得的。spa
若是用戶有 N 篇文章,就要執行 N 次 SQL 查詢,再加上以前請求用戶信息的 1 次 SQL,這就是所謂的『N+1』問題。debug
明明能 2 次完成的事,就不要再花 N+1 次了。code
像上面的情況,若是咱們知道底層是怎樣執行 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)的。上面的配置只對基於控制器動做的路由有效。