Quantcast
Channel: PHP8タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 521

【Laravel】withとwhereHasを同時に実行するscopeを実装

$
0
0
対象読者 以下の基本的な知識がある人 リレーション APIリソース with、whereHasメソッド 実行環境 PHP 8.0.15 Laravel 8.83.1 定番のUser, Postモデルを例に解説していきます。 各モデルのリレーションメソッドは定義済みとします。 通常の書き方 id = 1のユーザー のユーザーの今日以降の投稿 上記データを同時に取得したいとします。 リレーション先のEager Loadingと検索を同時に行いたいときは、 withとwhereHasを組み合わせます。 $users = User::with('posts') ->whereHas('posts', function ($q) { $q->where('created_at', '>=', today()); })->find(1); 問題点 うっかりwithを書き忘れたとしましょう。 $users = User::whereHas('posts', function ($q) { // withがない!!! $q->where('created_at', '>=', today()); })->find(1); さらに、APIリソースではロードしたときのみpostsを返すようになっていました。 UserResource.php class UserResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'posts' => $this->whenLoaded('posts') // ロードしていたらpostsを返却 ]; } } Eager Loadingしていないため、本来存在する投稿データがレスポンスから消えてしまいます (筆者はこれでしばらくハマってしまいました)。 array:1 [ "data" => array:2 [ "id" => 1 "name" => "山田太郎" // postsがない! ] ] 独自scopeの実装 同時に実行するメソッドがあればこのようなミスは防げます。 Modelに独自scopeを定義しましょう。 User.php public function scopeWithWhereHas($query, $relation, $constraint) { return $query->whereHas($relation, $constraint) ->with([$relation => $constraint]); } 以下のように書くと、withとwherehasを組み合わせたときと同じ挙動になります。 コード量も減るのですっきりしますね。 $users = User::withWhereHas('posts', function ($q) { $q->where('created_at', '>=', today()); })->find(1); postsフィールドもしっかり返却されました。 array:1 [ "data" => array:2 [ "id" => 1 "name" => "山田太郎" "posts" => array:2 [ 0 => array:5 [ "id" => 1 "user_id" => 1 "content" => "投稿1" "created_at" => "2022-03-16T02:02:21.000000Z" "updated_at" => "2022-03-16T02:02:21.000000Z" ] 1 => array:5 [ "id" => 2 "user_id" => 1 "content" => "投稿2" "created_at" => "2022-03-16T02:02:21.000000Z" "updated_at" => "2022-03-16T02:02:21.000000Z" ] ] ] ] おわりに .*ServiceProviderにmacroとして定義すると、実装していないUserモデル以外でも 使えるようになります。より便利ですね。 では! 参考文献

Viewing all articles
Browse latest Browse all 521

Trending Articles