Laravel

【Laravel】掲示板を作成する(7)クエリーの調整(Eagerロード)、キーワード検索機能

Laravelによる掲示板の作成、第7回です。
今回は『N+1』問題を解決するEagerロードというクエリーの調整方法と、一覧画面にキーワード検索機能を追加してみたいと思います。

13.クエリーの調整(Eagerロード)

有名な『N+1問題』を解決する方法がLaravelには備わっているということなので、試してみます。
まずは実行されているクエリがわかるように、SQLの実行ログを取ってみます。

サービスプロバイダ登録

app/Providers/AppServiceProvider.phpを開いて、「boot」メソッドを以下のように変更します。

上記コードを記述した状態で一覧ページをにアクセスすると、下記のようなログファイルが取得できます。
ログファイルは storage\logs 以下に作成されます。

ログ内容

タイムスタンプ等は今回端折っています。

同じタイムスタンプで合計23回のクエリが発行されていました。

一覧画面には「投稿のリスト」と「投稿に紐づくコメントの合計数」を表示しているため、取得した投稿の回数(10回)分コメント数をカウントするクエリが発行されてしまっています。

具体的に書くと以下のSQLでリストを取得し、

次のSQLでコメント数を取得しています。
このコメント取得SQLが10回走っています。

さらにカテゴリー名を取得するのにも同じようなクエリが10回発行されています。

このような問題を『n+1』問題と言いまずが、Laravelでは一覧取得時に with メソッドを利用することで解決することができるとのこと。

with() を使ったEagerローディング

PostsControllerのindexメソッドで投稿のリストを取得している処理を、以下のように変更します。

編集ファイル:app\Http\Controllers\PostsController.php

なお、withの中に記述している comments が複数形なのは、post対commentは「1対多」としているからです。

この状態で一覧にアクセスしてログを確認すると。。。おお、コメントを取得していたSQLがINを使ったものになって、回数は大幅に減っています。

ただし カテゴリーの処理がまだそのままですので、さらに先程のwithにcategoryを設定してみます。

このcategoryは単数形で設定。postから見た場合のcategoryは1つという設定だからです。

最終的なログ

最終的に、発行されるSQLは5個になりました。おお、すごい。

ログファイルの取得には以下のサイト様を参考にしました。

14.キーワード検索機能

名前(投稿者名)や本文を対象にキーワード検索できるようにしてみます。
まずは「名前」を対象とした検索機能を作成してみます。

名前検索

検索にはカテゴリーでも使用したスコープを使います。

Postモデル編集

編集ファイル:app\Post.php

名前検索用のスコープを追加します。

Postsコントローラー編集

編集ファイル:app\Http\Controllers\PostsController.php

indexメソッドへ以下を追加します。

一覧取得する箇所のチェーンメソッドに先程のスコープを追加します。

検索ワードをviewに渡せるようにします。

ビューを編集

編集ファイル:resources\views\bbs\index.blade.php

検索フォームを任意の場所に追加します。

ページ送りに渡す引数を追加します。

一覧画面キャプチャ(検索前)

一覧画面キャプチャ(検索実行後)

「田」(田園の田)という字を検索ワードにしてみました。

名前・投稿内容を『OR検索』する

次に、1つの検索ワードで名前と投稿内容をOR検索ができるようにしてみます。
「名前」もしくは「投稿内容」に『形』という検索ワードが含まれているものを表示するというイメージです。

Postモデル編集

編集ファイル:app\Post.php

名前・投稿内容検索用のスコープを作成します。

「use」を使ってクロージャへ $searchword 変数を渡すのがポイントです。

Postコントローラー編集

例によって、チェーンメソッドを名前・投稿内容検索用のスコープに変更します。

編集ファイル:app\Http\Controllers\PostsController.php

検索結果のキャプチャ

『形』という検索ワードでの結果です。

ID:19の名前には「形」が含まれていますが、本文には含まれていません。
逆にID:17番の名前には「形」は含まれていませんが、本文に含まれていました。

うまくいっているようですね。

ID:19の詳細キャプチャ

ID:17の詳細キャプチャ

今回はここまでとなります。

-Laravel
-, ,