Eloquent Collection で絞り込み
Laravel で検索機能を実装すると、Eloquent Builder の where句 で絞り込むことが多いかと思います。
(下記のイメージ)
$productQuery = Product::query();
if ($request->input('price')) {
$productQuery->where('price', '=' ,$request->input('price'));
}
if ($request->input('name')) {
$productQuery->whereHas('user.userDetail', function (Product $productQuery) use ($request) {
$productQuery->where('name', '=', $request->input('name'));
});
}
$products = $productQuery->get();
dd($products->get());
今回は、Eloquent Collection の filterメソッド を使って絞り込んでみようと思います。
filterメソッドはtrueまたはfalseを返します。
$productQuery = Product::cursor();
if ($request->input('price')) {
$priceCursor = $productQuery->filter(function(Product $productQuery) use ($request) {
return $productQuery->price === $request->input('price');
});
$products = $priceCursor->all();
}else{
$products = $productQuery->all();
}
dd($products);
※間違えているところがございましたらご指摘いただけると幸いです。
Eloquent Builder と Eloquent Collection の併用
あんまりないパターンだとは思いますが、検索項目の一部のみ Collection で絞り込む場合。
基本は Eloquent Builder で統一させるはずなので、なにかしらの諸事情によりやらなければならなくなったとき用に備忘録として残します。
流れ
- Eloquent Builder で SQL に where 付与
- SQL 実行して結果を LazyCollection (cursor) で取得
- LazyCollection::filter() で結果をフィルタリング
$productQuery = Product::with('user.userDetail');
$productQuery->where('colors', $request->input('colors'));
// cursor()した時点でLaravel6.x であれば LazyCollectionになる
$productCursor = $productQuery->cursor();
if ($request->input('name')) {
$filtered = $productCursor->filter(function (Product $app) use ($request) {
// filter()のコールバック関数の引数はBuilderではなくModelになりRelationshipsが使える
return $app->user->userDetail->name === $request->input('name');
});
$products = $filtered->all();
Eloquent Builder は基本、操作がミュータブルなので $productQuery の状態を変化(Builder自身を変化)しますが、
Eloquent Collection の filerメソッドは操作がイミュータブルな(自身の状態を変えずに新しいCollectionを作る)ので、変数をあてる必要があります。
ミュータブルかイミュータブルかどうかは、\Illuminate\Support\Collection のソースを見ます。
(戻り値が Collection ならだいたいイミュータブルではあるそうです。)
@ return staticとなってるのがイミュータブル
@ return $thisとなてるのはミュータブル
だそうです。
実際に今回使った filter() を確認してみると、
return new static とあるのでイミュータブルです。
流れのソースまとめ
$productQuery = Product::with('user.userDetail');
if ($request->input('price')) {
$productQuery->where('price', $inputs['price']);
});
$productCursor = $productQuery->cursor();
if ($request->input('name')) {
$filtered = $productCursor->filter(function (Product $app) use ($request) {
return $app->user->userDetail->name === $request->input('name');
});
$products = $filtered->all();
}else{
$products = $productCursor->all();
}
参考文献
この記事は以下の情報を参考にしました。