Laravel Eloquentで値オブジェクトを使う
こんにちは。今回はLaravelのEloquentで値オブジェクトを実装する方法を紹介します。
値オブジェクトとは
値オブジェクトとはエンティティのフィールドに関する業務ルールを扱うオブジェクト(クラス)のことです。Laravelで言い換えると、エンティティ=Eloquentモデル。フィールド=テーブルのカラム。と位置づけられます。フィールド名のクラスを作成し、フィールド特有の業務ルールを記述します。
題材
Todoを管理するTodo Eloquentモデルを作成します。Todoモデルは以下のプロパティを持ちます。
- todo (todoの内容)
- due_date(期日)
- is_finished (完了)
そしてTodoの要件として「due_date(期日)を平日にしか設定できない。」が求められてるとします。この要件はdue_dateの関心事といえます。due_dateをバリューオブジェクトにし、due_dateに設定された値が平日かどうかを確かめるisWeekDayというロジックを実装したいと思います。
作り方
値オブジェクトの作成
due_date値オブジェクトを作成します。クラス名はDueDateVOとします。コンストラクタで値を受け取り、内部変数に保持します。isWeekDay関数を作成し、値が平日かどうかを判定し、論理値を返します。
<?php
namespace App\Models\ValueObjects;
use Carbon\Carbon;
class DueDateVO
{
private $value;
public function __construct($value){
$this->value = Carbon::parse($value);
}
// getter
public function getValue(){
return $this->value;
}
// 設定した日が平日かどうか
public function isWeekDay():bool {
return $this->value->isWeekDay();
}
}
Castを作成する
値オブジェクトの作成が終わったら、TodoモデルにDueDateVOクラスを保持させます。保持させるためにCastの機能を利用します。artisan make:castコマンドでDueDateのCastオブジェクトを生成します。
$ php artisan make:cast DueDate
作成されたCastのクラスにget, setを実装します。 getでは新たにEloquentのDueDateVOを生成し、due_dateフィールドの値を設定して返します。setはDueDateVOオブジェクトを受け取り、その中の値を取り出してdue_dateフィールドに設定します。なおsetでは渡されたオブジェクトがDueDateVOであることをチェックします。
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use App\Models\ValueObjects\DueDateVO;
use InvalidArgumentException;
class DueDate implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
return new DueDateVO($attributes['due_date']);
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
if (! $value instanceof DueDateVO) {
throw new InvalidArgumentException('与えられた引数はDueDateのインスタンスではありません。');
}
return $value->getValue();
}
}
EloquentにCastを設定する
作成したDueDateのCastオブジェクトをTodoモデルに設定します。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Casts\DueDate;
class Todo extends Model
{
use HasFactory;
protected $casts = [
'due_date' => DueDate::class, // DueDate Castクラスを指定
];
}
Tinkerで動作確認する
tinkerでDueDateVO値オブジェクトのisWeekDayメソッドが正しく動作することを確認します。
// まずはオブジェクトをインスタンス化
>>> use App\Models\Todo;
>>> use App\Models\ValueObjects\DueDateVO;
>>> $todo = new Todo();
=> App\Models\Todo {#3722}
// DueDateに2021-11-11を設定(このときDueDateVOをNewして渡します。)
>>> $todo->due_date = new DueDateVO('2021-11-11');
=> App\Models\ValueObjects\DueDateVO {#3731}
// DueDateVOが持つisWeekDayを呼び出します。
>>> $todo->due_date->isWeekDay();
// 平日なのでtrueが返る
=> true
// 試しに休日を設定するとfalseが返ります。
>>> $todo->due_date = new DueDateVO('2021-11-14');
=> App\Models\ValueObjects\DueDateVO {#3729}
>>> $todo->due_date->isWeekDay();
=> false
まとめ
今回はCast機能を使用して値オブジェクトをEloquentモデルで扱う方法を説明しました。値オブジェクトを使うようにすると、値に関する関心事をカプセル化することができるので、コードの見通しが格段によくなり、再利用性も高まります。値オブジェクト自体はドメイン駆動開発(DDD)の考え方ですが、値オブジェクト単体でも十分に利用価値があります。是非利用してみてください。
ディスカッション
コメント一覧
まだ、コメントがありません