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メソッドが使われるのが適切です。

By khino