Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
150
Help us understand the problem. What are the problem?

Laravel SQLの実行クエリログを出力する

LaravelではEloquentやQueryBuilderを介してデータベースとやり取りしますが、実際に実行されるSQLを確認する時に使用する方法を3つご紹介します。

環境

  • PHP 8.0
  • MySQL 8.0.17
  • Laravel 8.23.1

1. QueryBuilderをSQLに変換したい時

クエリビルダには、 toSql() メソッドを呼び出すことでSQL文を取得できます。プレースホルダの値は getBindings() で取得できます。

$query = \App\User::where('id', 1);
dd($query->toSql(), $query->getBindings());

tinkerで確認する時にも便利です。

2. 複数のQueryBuilderのSQLを確認したい時

SQL実行前に DB::enableQueryLog() でクエリログを有効化し、SQL実行後に DB::getQueryLog() メソッドを利用することで、そのクエリの内容や、実行時間を取得できます。

\DB::enableQueryLog();
\App\User::all();
dd(\DB::getQueryLog());

有効化した後のクエリログを取得できます。

3. サービスプロバイダーに登録してログファイルに自動出力

  • 環境変数でログ出力の有効/無効を切り替えする

DataBaseQueryServiceProvider を作成

$ php artisan make:provider DataBaseQueryServiceProvider

app/Providers/DataBaseQueryServiceProvider.php ファイルが生成される。

config/app.php へ追記

    'providers' => [
        // ... 省略
        App\Providers\DataBaseQueryServiceProvider::class,
    ],

providers の配列に作成した DataBaseQueryServiceProvider を登録します。

app/Providers/DataBaseQueryServiceProvider.php を編集

<?php declare(strict_types=1);

namespace App\Providers;

use Carbon\Carbon;
use DateTime;
use Illuminate\Database\Events\TransactionBeginning;
use Illuminate\Database\Events\TransactionCommitted;
use Illuminate\Database\Events\TransactionRolledBack;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;

class DataBaseQueryServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        if (config('logging.sql.enable') === false) {
            return;
        }

        DB::listen(function ($query): void {
            $sql = $query->sql;

            foreach ($query->bindings as $binding) {
                if (is_string($binding)) {
                    $binding = "'{$binding}'";
                } elseif (is_bool($binding)) {
                    $binding = $binding ? '1' : '0';
                } elseif (is_int($binding)) {
                    $binding = (string) $binding;
                } elseif ($binding === null) {
                    $binding = 'NULL';
                } elseif ($binding instanceof Carbon) {
                    $binding = "'{$binding->toDateTimeString()}'";
                } elseif ($binding instanceof DateTime) {
                    $binding = "'{$binding->format('Y-m-d H:i:s')}'";
                }

                $sql = preg_replace('/\\?/', $binding, $sql, 1);
            }

            Log::debug('SQL', ['sql' => $sql, 'time' => "{$query->time} ms"]);
        });

        Event::listen(TransactionBeginning::class, function (TransactionBeginning $event): void {
            Log::debug('START TRANSACTION');
        });

        Event::listen(TransactionCommitted::class, function (TransactionCommitted $event): void {
            Log::debug('COMMIT');
        });

        Event::listen(TransactionRolledBack::class, function (TransactionRolledBack $event): void {
            Log::debug('ROLLBACK');
        });
    }
}

config/logging.php を編集

config/logging.php
return [
    /*
    |--------------------------------------------------------------------------
    | Custom Log
    |--------------------------------------------------------------------------
    */

    'sql' => [
        'enable' => env('LOG_SQL_ENABLE', false),
    ],
];

.env を編集

LOG_SQL_ENABLE=true

※本番環境ではパフォーマンスやセキュリティ上の問題から無効化した方が良いです!

お試し実行

routes/web.php

<?php

use App\User;

Route::get('/', function () {
    $user = DB::transaction(function () {
        $user = factory(User::class)->create();
        $user->name = 'change name';
        $user->save();
        $user->delete();

        return factory(User::class)->create();
    });

    User::find($user->id);

    return view('welcome');
});

適当にSQLを発行しそうな処理を加えて、いつものウェルカムページを表示する。

storage/logs/laravel-***.log

ログファイルを確認すると次のようなデータベースクエリログが出力されていればok

[2019-06-15 07:14:09] local.DEBUG: START TRANSACTION  
[2019-06-15 07:14:09] local.DEBUG: SQL {"sql":"insert into `users` (`name`, `email`, `email_verified_at`, `password`, `remember_token`, `updated_at`, `created_at`) values ('Pietro Conn', 'leuschke.kiarra@example.net', '2019-06-15 07:14:09', 'yIXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'rch9DXbn5W', '2019-06-15 07:14:09', '2019-06-15 07:14:09')","time":"2.19 ms"} 
[2019-06-15 07:14:09] local.DEBUG: SQL {"sql":"update `users` set `name` = 'change name', `users`.`updated_at` = '2019-06-15 07:14:09' where `id` = 23","time":"1.66 ms"} 
[2019-06-15 07:14:09] local.DEBUG: SQL {"sql":"delete from `users` where `id` = 23","time":"1.33 ms"} 
[2019-06-15 07:14:09] local.DEBUG: SQL {"sql":"insert into `users` (`name`, `email`, `email_verified_at`, `password`, `remember_token`, `updated_at`, `created_at`) values ('Zoila Wintheiser', 'eokon@example.com', '2019-06-15 07:14:09', 'yIXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Ya6yCp6UTX', '2019-06-15 07:14:09', '2019-06-15 07:14:09')","time":"1.61 ms"} 
[2019-06-15 07:14:09] local.DEBUG: COMMIT  
[2019-06-15 07:14:09] local.DEBUG: SQL {"sql":"select * from `users` where `users`.`id` = 24 limit 1","time":"4.41 ms"} 

関連記事

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
150
Help us understand the problem. What are the problem?