今回はLaravelのwithメソッドの意味について紹介します。
当初、自分で理解するのが難しかった為、ここに解りやすくまとめました。私と同じく悩んでいる開発者の役に立てれば嬉しいです。
LaravelのEloquentを使用してリレーション先のデータを取得する際、withメソッドを使用できます。
しかし、withメソッドを使用せずともリレーション先のデータの取得は可能です。
そこで、どういった用途でwithメソッドが使用されるのかを調査しました。
LaravelのEloquentではデフォルトでLazy Loading(遅延読み込み)がサポートされています。
Lazy Loadingとは、オブジェクトやデータを必要となった時点で取得する方法です。
具体的には、オブジェクトのプロパティや関連データがアクセスされるまで、それらのデータの取得を遅延させます。
例えば、以下のコードでは、Companyモデルのemployeesリレーションが遅延読み込みされます。
$company = Company::find(1);
$employees = $company->employees;
$company->employees がアクセスされた時点で、関連するEmployeeモデルがデータベースから取得されます。
実際にはそれぞれの行で以下のクエリが実行されます。
SELECT * FROM companies WHERE id = 1;
SELECT * FROM employees WHERE company_id = 1;
Lazy Loadingにより、アプリケーションが不要なデータを先読みせずに効率的に動作することができます。
ただし、Lazy Loadingは過度に使用するとN+1問題によるパフォーマンスの問題を引き起こすことがあります。
N+1問題とは、データベースのクエリを実行する際に発生する効率性の問題です。
関連するエンティティのデータを取得するために必要なクエリの数が多くなる場合に発生します。
1つのクエリでN個のメインのエンティティを取得した後に、関連するエンティティを取得するためにN回の追加のクエリを発行する場合、総クエリ数がN+1になることがN+1問題の名前の由来です。
例えば、3個の会社があり、各会社に社員がいる場合、Eloquentでは以下のコードで社員の取得を行うことができます。
$companies = Company::all();
foreach($companies as $company)
{
$employees = $company->employees;
}
この時、会社の取得に1回、各会社の社員の取得に1回ずつで計4回のクエリが実行されます。
SELECT * FROM companies;
SELECT * FROM employees WHERE company_id = 1;
SELECT * FROM employees WHERE company_id = 2;
SELECT * FROM employees WHERE company_id = 3;
このようにクエリ数が増えると、データベースやネットワークの負荷が増加し、パフォーマンスに悪影響を及ぼす可能性があります。
N+1問題を解決するための方法の一つに、Eager Loading(積極的読み込み)の使用があります。
Eager Loadingとは、必要な時点で追加のクエリを実行してデータを取得するのではなく、初めに全ての必要なデータを取得する方法です。
Eager Loadingでは、関連データをあらかじめ取得しておくことで、N+1問題を回避することができます。
Laravelでは、Eager Loadingを簡単に実現するためにwithメソッドを提供しています。
withメソッドを使用して関連データを指定することで、N+1問題を回避し、効率的なデータ取得を実現できます。
先程の例で言うと、Eager Loadingを行うと以下コードのようになります。
$companies = Company::with('employees')->all();
foreach($companies as $company)
{
$employees = $company->employees;
}
この時、会社の取得と社員の取得の計2回のクエリでデータの取得を行うことができます。
SELECT * FROM companies;
SELECT * FROM employees WHERE company_id IN (1, 2, 3);
結論
複数のエンティティとその関連エンティティを一度に取得する場合には、withメソッドを使用することでパフォーマンスを大きく向上させることができます。
また、たとえ単体のエンティティの取得でwithメソッドを使用したとしてもパフォーマンス上にマイナス点はほとんどありません。
将来的に単体のエンティティ取得が複数のエンティティ取得に変わる可能性がある場合や、その逆の場合でも、Eager Loadingを常に使用することで、そのような変更に対応するためのコードの修正を最小限に抑えることができます。
したがって、パフォーマンスの向上や、コードベース全体での一貫性を保つことができる点から、単数でも複数でも関連データを必要とする場合は、withメソッドを使用してEager Loadingを適用するのがよいでしょう。
例では、簡単なwithメソッドによるEager Loadingを紹介しましたが、複数リレーションのEager Loadingや特定のカラムのみ、リレーション先のリレーションのEager Loadingなど複雑な条件でのEager Loadingも可能です。
詳しくは公式ドキュメントをご覧ください。
公式ドキュメント:Eloquent: Relationships
日本語ドキュメント:Eloquent:リレーション
開発者の皆様、お役に立てたでしょうか? もし役に立った方は以下のいいね!かシェアしていただけると嬉しいです。