Laravel の eager loading に関する小ネタ

Laravel の eager loading に関する小ネタ

はじめに

Laravel や Rails のようなフレームワークでは、N+1問題を回避するために eager loading を使用する事が一般的です。本記事では、Laravel の eager loading に関する小ネタをいくつか書いていきます。

Laravel の eager loading 自体に関しては、ドキュメントに詳しく記述されていますので、まずは一通り目を通す事をオススメします。

件数を事前に取得する

実は、上述のドキュメントの eager loading の項ではなく、1つ前の項(Querying Relations -> Counting Related Models)に記載があるのですが、eater loading の書式(::with('table'))と似たような書式(::withCount('table'))で、子テーブルの件数を事前に取得する事が出来ます。

分かりやすい使用例としては、ブログ記事一覧を表示する際に、各記事についているコメント数も合わせて表示する、というのがあります。(上述のドキュメントでも、この例が載っています。)

例えば、以下のようなコードがあった場合、

$posts = App\Post::withCount('comments')->take(10)->get();

実行される SQL は以下のようなものです。

select `posts`.*
  , (select count(*) from `comments` where `posts`.`id` = `comments`.`post_id`) as `comments_count`
from `posts` limit 10

lazy にやるには

lazy に eager loading をする方法はドキュメントに記載されていますが、件数の取得を lazy に行うにはどうすれば良いでしょうか。

Laravel 5.8 からは loadCount メソッドというのが追加されており、load で lazy に eager loading を行うのと同じように、件数も lazy に取得する事ができます。以下の SO の投稿で知りました。

Laravel Eloquent Lazy Eager Load Count – Stack Overflow

複数のカラムでリレーションしている場合

Rails や、Rails に影響を受けている Laravel のようなフレームワークでは、複合主キーは使わず、連番のサロゲートキーを使う事が(それ自体の是非や好き嫌いはさておき)ベストプラクティスです。

ただ、現実の場面では、複数のカラムの値を使って子テーブルを読み込まなければいけないケースは多々あると思います。普通に読み込むだけであれば hasManyhasOne の後ろに where で条件を絞れば良いのですが、それだと eager loading が正しく動作しません。

それを解決するために、以下のようなモジュールがありました。

topclaudy/compoships: Multi-columns relationships for Laravel 5’s Eloquent

この辺りの話題は SO の以下のスレッドを参照して下さい。

Laravel Eloquent: multiple foreign keys for relationship – Stack Overflow

まとめ

Web アプリのパフォーマンス問題を解決するには、eager loading を上手く使う事が必須です。

Laravel の eager loading は機能が豊富で、標準の機能だけで大半の問題は解決できると思いますので、まずはドキュメントを一通り読む事をお勧めします。

複合キーの問題は、この手のフレームワークを使う時に定番の話題だと思います。Laravel を使う場合は、できる限り複合キーを排除した方が良いのですが、複合キーで参照している子テーブルを eager loading したい場合は、Comphoships モジュールの導入を検討してみて下さい。

we are hiring

優秀な技術者と一緒に、好きな場所で働きませんか

株式会社もばらぶでは、優秀で意欲に溢れる方を常に求めています。働く場所は自由、働く時間も柔軟に選択可能です。

現在、以下の職種を募集中です。ご興味のある方は、リンク先をご参照下さい。