前回においてCollectionのeach()
でUserのオブジェクトにageの属性を追加する処理をしましたが、今回はこれを発展させて、UserのCollectionにカスタムメソッドを追加します。
目的
例えば、いつも大人(20歳以上)だけのユーザーを対象にした処理を行いたいとします。
DBから取得したレコードにおいては、birth_dateの属性があるので現時点での大人だけのレコードは、以下のようにfilter()
を利用して、
User::all()->filter(function ($user) { return $user->isAdult(); })
のような処理をすれば大人だけのレコード取得が可能です。
上で使用されているisAdult()
は以下のように定義します。歳の計算は、アクセッサ getAgeAttribute
で行います。
namespace App; use Carbon\Carbon; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use Notifiable; ... public function getAgeAttribute() { return Carbon::parse($this->birth_date)->age; } public function isAdult() { return $this->age >= 20; } }
さて、これで実際に先ほどのコードをtinkerで実行すると、
>>> User::all()->filter(function ($user) { return $user->isAdult(); }); => Illuminate\Database\Eloquent\Collection {#4179 all: [ 0 => App\User {#3306 id: 1, name: "坂本 充", email: "nishida@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$snEjTmJvBDu3uxd1bz0QDupihJA3zjvEH5J0pm/aLHG4UvpSfJe/q", #remember_token: "j8hPjp8ob7", birth_date: "1997-03-02", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, 2 => App\User {#3986 id: 3, name: "近藤 知実", email: "atsushi.ito@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$A7xldQvOiCfUOyNS57EEYu2/s77a70BPmGoOjzDSgTmveeRlcAD8.", #remember_token: "qOFWQFb5ER", birth_date: "1972-09-14", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, ], }
と大人だけのレコードを返してくれます。
さらに、コールバックの定義も必要なしに、以下のようにfilterをあたかも属性のようにしての使用も可能です。短縮されていいですね。
>>> User::all()->filter->isAdult() => Illuminate\Database\Eloquent\Collection {#3299 all: [ 0 => App\User {#4238 id: 1, name: "坂本 充", email: "nishida@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$snEjTmJvBDu3uxd1bz0QDupihJA3zjvEH5J0pm/aLHG4UvpSfJe/q", #remember_token: "j8hPjp8ob7", birth_date: "1997-03-02", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, 2 => App\User {#4240 id: 3, name: "近藤 知実", email: "atsushi.ito@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$A7xldQvOiCfUOyNS57EEYu2/s77a70BPmGoOjzDSgTmveeRlcAD8.", #remember_token: "qOFWQFb5ER", birth_date: "1972-09-14", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, ], } >>>
しかし、凄いことに、これをさらに進めて、以下のようにCollectionのカスタムメソッドの作成してUserのCollectionに適用することも可能なのです。
User::all()->adults()
まず、Collectionを継承したクラスを作成して、adults()のカスタムメソッドを定義します。
namespace App\Collections; use Illuminate\Database\Eloquent\Collection; class UserCollection extends Collection { public function adults() { return $this->filter->isAdult(); } }
そして、そのカスタムクラスをモデルのクラス(User)のnewCollection()
のメソッドで返り値とします。
namespace App; use App\Collections\UserCollection; use Carbon\Carbon; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use Notifiable; ... public function getAgeAttribute() { return Carbon::parse($this->birth_date)->age; } public function isAdult() { return $this->age >= 20; } public function newCollection(array $models = []) { return new UserCollection($models); } }
実行してみましょう。
>>> User::all()->adults(); => App\Collections\UserCollection {#3305 all: [ 0 => App\User {#4024 id: 1, name: "坂本 充", email: "nishida@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$snEjTmJvBDu3uxd1bz0QDupihJA3zjvEH5J0pm/aLHG4UvpSfJe/q", #remember_token: "j8hPjp8ob7", birth_date: "1997-03-02", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, 2 => App\User {#3986 id: 3, name: "近藤 知実", email: "atsushi.ito@example.net", email_verified_at: "2021-05-30 21:55:14", #password: "$2y$10$A7xldQvOiCfUOyNS57EEYu2/s77a70BPmGoOjzDSgTmveeRlcAD8.", #remember_token: "qOFWQFb5ER", birth_date: "1972-09-14", created_at: "2021-05-30 21:55:14", updated_at: "2021-05-30 21:55:14", }, ], }
同じ結果となりました。コードが短くなっただけでなくわかりやすくもなりました。
最後に注意としてですが、今回の例はレコード数がとても多いときはパフォーマンスの点からすると非常に悪い例です。すべてのDBレコードを取得してコードにおいて抽出を行うわけですゆえに。パフォーマンスを考えるとDBのSQLレベルにおいて大人の条件を入れて実行すべきです。
メルマガ購読の申し込みはこちらから。