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