「Laravel Collection(2) Collectionを返すメソッド」ではfilterメソッドを紹介しました。今度はfilterメソッドのように条件でCollectionを絞るwhereメソッドの話です。EloquentやSQL文で慣れているならwhereの方がわかりやすいかもしれません。
データの準備
用意するデータは、Laravel Collection(1)1つの値を返すメソッド をもとに、以下のDBレコードがすでに作成されていると仮定します。
mysql> select id, name, birth_date from users; +----+------------------+------------+ | id | name | birth_date | +----+------------------+------------+ | 1 | 石田 裕美子 | 1991-05-26 | | 2 | 佐藤 香織 | 2002-01-12 | | 3 | 杉山 明美 | 2000-03-13 | +----+------------------+------------+ 3 rows in set (0.00 sec)
今回は女の人だけになりました。
where
まず、filter()
を使った例です。
>>> User::all()->filter(function ($user) { return $user->birth_date < '2000-01-01'; }) => Illuminate\Database\Eloquent\Collection {#4317 all: [ App\User {#4260 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], }
これと同じ処理をwhere()
で行うと、
>>> User::all()->where('birth_date', '<', '2000-01-01') => Illuminate\Database\Eloquent\Collection {#3393 all: [ App\User {#4105 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], }
短くてわかりやすいです。
今度はwhere()
を2つ繋げます。
>>> User::all()->where('birth_date', '>=', '1990-01-01') ->where('birth_date', '<=', '2000-01-01') => Illuminate\Database\Eloquent\Collection {#4171 all: [ App\User {#3376 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], }
これは、Laravel Collection(2) Collectionを返すメソッド で掲載したfilter()
を使用した以下とまったく同じ処理なことわかりますか?
>>> User::all()->filter(function ($user) { if ($user->birth_date > '2000-01-01') return false; if ($user->birth_date < '1990-01-01') return false; return true; })
whereBetween()
というメソッドもありますよ。これを使えばもっと短くなります。
>>> User::all()->whereBetween('birth_date', ['1990-01-01', '2000-01-01']) => Illuminate\Database\Eloquent\Collection {#3383 all: [ App\User {#3412 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], }
対象のレコードを条件で絞るという点では、CollectionのwhereメソッドはEloquentやDBクエリのwhereメソッドと同じようなメソッドです。しかし両者のwhereメソッドはどこが違うのでしょうか。
EloquentとCollectionでのwhereメソッドの違い
この違いを理解するには、裏で実行されているSQL文を見る必要があります。
注意:以下のtinkerで使用されているsql()
はユーザー定義です。こちらを参照してください。
まず、Eloquentのクエリでのwhereの実行です。
>>> User::where('birth_date', '<', '2000-01-01')->get() => Illuminate\Database\Eloquent\Collection {#4172 all: [ App\User {#4104 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], } >>> sql() => [ [ "query" => "select * from `users` where `birth_date` < ?", "bindings" => [ "2000-01-01", ], "time" => 5.02, ], ]
SQL文にはしっかりSQLのwhere節が含まれています。つまりDBレベルで条件で絞っています。
次に、Collectionでのwhere()
の実行です。
>>> User::all()->where('birth_date', '<', '2000-01-01') => Illuminate\Database\Eloquent\Collection {#3393 all: [ App\User {#4105 id: 1, name: "石田 裕美子", email: "sasaki.naoko@example.net", email_verified_at: "2021-10-07 02:41:15", #password: "$2y$10$53mZGiOMkJEVm1VDij642eWGi.i4/bWt3fElQjQvp86dfG/9MK5mW", #remember_token: "fCSd0AlA84", birth_date: "1991-05-26", created_at: "2021-10-07 02:41:16", updated_at: "2021-10-07 02:41:16", }, ], } >>> sql() => [ [ "query" => "select * from `users`", "bindings" => [], "time" => 4.45, ], ] >>>
こちらのSQL文にはwhere節は含まれておらず、DBのusersテーブルからすべてのレコードを取得しています。そしてその中から条件に適うレコードをプログラムで抽出しています。今回はDBには3つしかレコードがありませんが、これが何千ともあるならそれらすべてをメモリに保持することになるのでリソースの無駄遣いとなり、さらにはパフォーマンスの低下ともなります。
となると、いつEloquentのクエリでwhere()を使うか、いつCollectionでwhere()を使うかを十分注意すること重要になります。Eloquent、つまりDBレベルでできるだけ取得するレコードを絞って、その結果のCollectionにおいてさらにいろいろな条件で絞るとかの場合にCollectionのwhereメソッドが使われるのが適切です。
メルマガ購読の申し込みはこちらから。