前回において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レベルにおいて大人の条件を入れて実行すべきです。
メルマガ購読の申し込みはこちらから。