コンテンツへスキップ

Eloquent: ムテーターとキャスト

はじめに

アクセサー、ムテーター、属性キャストを使用すると、モデルインスタンスから取得または設定するときに、Eloquent属性値を変換できます。たとえば、Laravel暗号化機能を使用して、データベースに保存されている値を暗号化し、Eloquentモデルでアクセスするときに自動的に復号化することができます。または、データベースに保存されているJSON文字列を、Eloquentモデルでアクセスするときに配列に変換することもできます。

アクセサーとムテーター

アクセサーの定義

アクセサーは、Eloquent属性値がアクセスされるときにその値を変換します。アクセサーを定義するには、アクセス可能な属性を表す保護されたメソッドをモデルに作成します。このメソッド名は、該当する場合は、実際の基になるモデル属性/データベース列の「キャメルケース」表現に対応している必要があります。

この例では、first_name属性のアクセサーを定義します。アクセサーは、first_name属性の値を取得しようとしたときに、Eloquentによって自動的に呼び出されます。すべての属性アクセサー/ムテーターメソッドは、Illuminate\Database\Eloquent\Casts\Attributeの戻り値型ヒントを宣言する必要があります。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Get the user's first name.
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
);
}
}

すべてのアクセサーメソッドは、属性のアクセス方法と、オプションでミューテーション方法を定義するAttributeインスタンスを返します。この例では、属性のアクセス方法のみを定義しています。そのためには、Attributeクラスコンストラクターにget引数を渡します。

ご覧のとおり、列の元の値はアクセサーに渡されるため、値を操作して返すことができます。アクセサーの値にアクセスするには、モデルインスタンスのfirst_name属性にアクセスするだけです。

use App\Models\User;
 
$user = User::find(1);
 
$firstName = $user->first_name;
lightbulb

これらの計算された値をモデルの配列/JSON表現に追加したい場合は、追加する必要があります

複数の属性から値オブジェクトを構築する

アクセサーでは、複数のモデル属性を単一の「値オブジェクト」に変換する必要がある場合があります。そのためには、getクロージャに$attributesという2番目の引数を使用できます。これはクロージャに自動的に渡され、モデルの現在の属性の配列が含まれます。

use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
 
/**
* Interact with the user's address.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
);
}

アクセサーキャッシュ

アクセサーから値オブジェクトを返す場合、値オブジェクトに加えられた変更は、モデルが保存される前にモデルに自動的に同期されます。これは、Eloquentがアクセサーによって返されたインスタンスを保持するため、アクセサーが呼び出されるたびに同じインスタンスを返すことができるためです。

use App\Models\User;
 
$user = User::find(1);
 
$user->address->lineOne = 'Updated Address Line 1 Value';
$user->address->lineTwo = 'Updated Address Line 2 Value';
 
$user->save();

ただし、特に計算に時間がかかる場合、文字列やブール値などのプリミティブ値に対してキャッシュを有効にしたい場合があります。これを実現するには、アクセサーを定義するときにshouldCacheメソッドを呼び出すことができます。

protected function hash(): Attribute
{
return Attribute::make(
get: fn (string $value) => bcrypt(gzuncompress($value)),
)->shouldCache();
}

属性のオブジェクトキャッシング動作を無効にしたい場合は、属性を定義するときにwithoutObjectCachingメソッドを呼び出すことができます。

/**
* Interact with the user's address.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
)->withoutObjectCaching();
}

ムテーターの定義

ムテーターは、Eloquent属性値が設定されるときにその値を変換します。ムテーターを定義するには、属性を定義するときにset引数を渡すことができます。first_name属性のムテーターを定義してみましょう。このムテーターは、モデルのfirst_name属性の値を設定しようとするときに自動的に呼び出されます。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Interact with the user's first name.
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
set: fn (string $value) => strtolower($value),
);
}
}

ムテータークロージャは、属性に設定されている値を受け取るので、値を操作して操作後の値を返すことができます。ムテーターを使用するには、Eloquentモデルのfirst_name属性を設定するだけです。

use App\Models\User;
 
$user = User::find(1);
 
$user->first_name = 'Sally';

この例では、setコールバックは値Sallyで呼び出されます。その後、ムテーターはstrtolower関数を名前に適用し、その結果の値をモデルの内部$attributes配列に設定します。

複数の属性のミューテーション

ムテーターでは、基になるモデルに複数の属性を設定する必要がある場合があります。そのためには、setクロージャから配列を返すことができます。配列内の各キーは、モデルに関連付けられた基になる属性/データベース列に対応している必要があります。

use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
 
/**
* Interact with the user's address.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
set: fn (Address $value) => [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
],
);
}

属性キャスト

属性キャストは、モデルに追加のメソッドを定義することなく、アクセサーとムテーターと同様の機能を提供します。代わりに、モデルのcastsメソッドは、属性を一般的なデータ型に変換する便利な方法を提供します。

castsメソッドは、キーがキャストされる属性の名前で、値が列をキャストする型である配列を返す必要があります。サポートされているキャスト型は次のとおりです。

  • array
  • AsStringable::class
  • boolean
  • collection
  • date
  • datetime
  • immutable_date
  • immutable_datetime
  • decimal:<precision>
  • double
  • encrypted
  • encrypted:array
  • encrypted:collection
  • encrypted:object
  • float
  • hashed
  • integer
  • object
  • real
  • string
  • timestamp

属性キャストを説明するために、データベースに整数(0または1)として保存されているis_admin属性をブール値にキャストしてみましょう。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_admin' => 'boolean',
];
}
}

キャストを定義した後、基になる値が整数としてデータベースに保存されている場合でも、is_admin属性はアクセスすると常にブール値にキャストされます。

$user = App\Models\User::find(1);
 
if ($user->is_admin) {
// ...
}

実行時に新しい一時的なキャストを追加する必要がある場合は、mergeCastsメソッドを使用できます。これらのキャスト定義は、モデルに既に定義されているキャストに追加されます。

$user->mergeCasts([
'is_admin' => 'integer',
'options' => 'object',
]);
exclamation

nullである属性はキャストされません。また、リレーションシップと同じ名前のキャスト(または属性)を定義したり、モデルの主キーにキャストを割り当てたりすることは決してしないでください。

Stringableキャスト

fluent Illuminate\Support\Stringableオブジェクトにモデル属性をキャストするには、Illuminate\Database\Eloquent\Casts\AsStringableキャストクラスを使用できます。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Casts\AsStringable;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'directory' => AsStringable::class,
];
}
}

配列とJSONキャスト

arrayキャストは、シリアライズされたJSONとして保存されている列を操作する場合に特に便利です。たとえば、データベースにシリアライズされたJSONが含まれているJSONまたはTEXTフィールド型がある場合、その属性にarrayキャストを追加すると、Eloquentモデルでアクセスするときに属性が自動的にPHP配列に逆シリアライズされます。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => 'array',
];
}
}

キャストが定義されたら、options属性にアクセスできます。この属性は自動的にJSONからPHP配列にデシリアライズされます。options属性の値を設定すると、指定された配列は自動的にJSONにシリアライズされて保存されます。

use App\Models\User;
 
$user = User::find(1);
 
$options = $user->options;
 
$options['key'] = 'value';
 
$user->options = $options;
 
$user->save();

より簡潔な構文でJSON属性の単一フィールドを更新するには、属性を大量代入可能にすることができ、updateメソッドを呼び出す際に->演算子を使用できます。

$user = User::find(1);
 
$user->update(['options->key' => 'value']);

配列オブジェクトとコレクションのキャスト

標準的なarrayキャストは多くのアプリケーションで十分ですが、いくつかの欠点があります。arrayキャストはプリミティブ型を返すため、配列のオフセットを直接変更することはできません。例えば、次のコードはPHPエラーを引き起こします。

$user = User::find(1);
 
$user->options['key'] = $value;

これを解決するために、LaravelはAsArrayObjectキャストを提供します。これはJSON属性をArrayObjectクラスにキャストします。この機能はLaravelのカスタムキャスト実装を使用して実装されており、Laravelは変更されたオブジェクトをインテリジェントにキャッシュおよび変換するため、個々のオフセットをPHPエラーを発生させることなく変更できます。AsArrayObjectキャストを使用するには、それを属性に割り当てるだけです。

use Illuminate\Database\Eloquent\Casts\AsArrayObject;
 
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsArrayObject::class,
];
}

同様に、LaravelはAsCollectionキャストを提供します。これはJSON属性をLaravelのコレクションインスタンスにキャストします。

use Illuminate\Database\Eloquent\Casts\AsCollection;
 
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsCollection::class,
];
}

Laravelの基本コレクションクラスではなく、カスタムコレクションクラスをインスタンス化するためにAsCollectionキャストを使用したい場合は、コレクションクラス名をキャスト引数として指定できます。

use App\Collections\OptionCollection;
use Illuminate\Database\Eloquent\Casts\AsCollection;
 
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsCollection::using(OptionCollection::class),
];
}

日付キャスト

デフォルトでは、Eloquentはcreated_atカラムとupdated_atカラムをCarbonのインスタンスにキャストします。CarbonはPHPのDateTimeクラスを拡張し、多くの便利なメソッドを提供します。モデルのcastsメソッド内で追加の日付キャストを定義することにより、追加の日付属性をキャストできます。通常、日付はdatetimeまたはimmutable_datetimeキャストタイプを使用してキャストする必要があります。

dateまたはdatetimeキャストを定義する際には、日付のフォーマットも指定できます。モデルが配列またはJSONにシリアライズされるときに、このフォーマットが使用されます。

/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'created_at' => 'datetime:Y-m-d',
];
}

カラムが日付としてキャストされている場合、対応するモデル属性の値をUNIXタイムスタンプ、日付文字列(Y-m-d)、日時文字列、またはDateTime/Carbonインスタンスに設定できます。日付の値は正しく変換され、データベースに保存されます。

モデルの日付のデフォルトのシリアライズ形式をカスタマイズするには、モデルにserializeDateメソッドを定義します。このメソッドは、日付がデータベースに保存される際のフォーマットには影響しません。

/**
* Prepare a date for array / JSON serialization.
*/
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format('Y-m-d');
}

モデルの日付をデータベースに実際に保存する際に使用するフォーマットを指定するには、モデルに$dateFormatプロパティを定義します。

/**
* The storage format of the model's date columns.
*
* @var string
*/
protected $dateFormat = 'U';

日付のキャスト、シリアライズ、タイムゾーン

デフォルトでは、datedatetimeキャストは、アプリケーションのtimezone設定オプションで指定されたタイムゾーンに関係なく、日付をUTC ISO-8601日付文字列(YYYY-MM-DDTHH:MM:SS.uuuuuuZ)にシリアライズします。このシリアライズ形式を常に使用し、アプリケーションのtimezone設定オプションをデフォルトのUTC値から変更しないことで、アプリケーションの日付をUTCタイムゾーンに保存することを強く推奨します。アプリケーション全体でUTCタイムゾーンを常に使用することで、PHPとJavaScriptで記述された他の日付操作ライブラリとの相互運用性を最大限に高めることができます。

datetime:Y-m-d H:i:sなど、dateまたはdatetimeキャストにカスタムフォーマットが適用されている場合、日付シリアライズ中にCarbonインスタンスの内側のタイムゾーンが使用されます。通常、これはアプリケーションのtimezone設定オプションで指定されたタイムゾーンです。ただし、created_atupdated_atなどのtimestampカラムはこの動作から除外され、アプリケーションのタイムゾーン設定に関係なく、常にUTCでフォーマットされることに注意することが重要です。

列挙型キャスト

Eloquentでは、属性値をPHPの列挙型にキャストすることもできます。これを実現するには、モデルのcastsメソッドでキャストする属性と列挙型を指定します。

use App\Enums\ServerStatus;
 
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'status' => ServerStatus::class,
];
}

モデルにキャストを定義したら、指定された属性は、属性を操作する際に列挙型との間で自動的にキャストされます。

if ($server->status == ServerStatus::Provisioned) {
$server->status = ServerStatus::Ready;
 
$server->save();
}

列挙型の配列のキャスト

モデルが単一のカラム内に列挙型の値の配列を保存する必要がある場合があります。これを実現するには、Laravelによって提供されるAsEnumArrayObjectまたはAsEnumCollectionキャストを使用できます。

use App\Enums\ServerStatus;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
 
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'statuses' => AsEnumCollection::of(ServerStatus::class),
];
}

暗号化キャスト

encryptedキャストは、Laravelの組み込み暗号化機能を使用してモデルの属性値を暗号化します。さらに、encrypted:arrayencrypted:collectionencrypted:objectAsEncryptedArrayObjectAsEncryptedCollectionキャストは、暗号化されていない対応物と同様に機能しますが、予想されるように、基になる値はデータベースに保存されるときに暗号化されます。

暗号化されたテキストの最終的な長さは予測不可能であり、プレーンテキストよりも長いため、関連付けられたデータベースカラムがTEXT型またはそれ以上の型であることを確認してください。また、値はデータベースで暗号化されるため、暗号化された属性値をクエリまたは検索することはできません。

キーローテーション

ご存知のとおり、Laravelはアプリケーションのapp設定ファイルで指定されたkey設定値を使用して文字列を暗号化します。通常、この値はAPP_KEY環境変数の値に対応します。アプリケーションの暗号化キーをローテーションする必要がある場合は、新しいキーを使用して暗号化された属性を手動で再暗号化する必要があります。

クエリ時間キャスト

テーブルから生の値を選択する場合など、クエリの実行中にキャストを適用する必要がある場合があります。例えば、次のクエリを考えてみてください。

use App\Models\Post;
use App\Models\User;
 
$users = User::select([
'users.*',
'last_posted_at' => Post::selectRaw('MAX(created_at)')
->whereColumn('user_id', 'users.id')
])->get();

このクエリの結果のlast_posted_at属性は、単純な文字列になります。クエリの実行時にこの属性にdatetimeキャストを適用できると素晴らしいでしょう。ありがたいことに、withCastsメソッドを使用してこれを実現できます。

$users = User::select([
'users.*',
'last_posted_at' => Post::selectRaw('MAX(created_at)')
->whereColumn('user_id', 'users.id')
])->withCasts([
'last_posted_at' => 'datetime'
])->get();

カスタムキャスト

Laravelにはさまざまな組み込みの便利なキャストタイプがありますが、独自のキャストタイプを定義する必要がある場合があります。キャストを作成するには、make:cast Artisanコマンドを実行します。新しいキャストクラスはapp/Castsディレクトリに配置されます。

php artisan make:cast Json

すべてのカスタムキャストクラスはCastsAttributesインターフェースを実装します。このインターフェースを実装するクラスは、getメソッドとsetメソッドを定義する必要があります。getメソッドは、データベースからの生の値をキャスト値に変換する役割を担い、setメソッドはキャスト値をデータベースに保存できる生の値に変換する必要があります。例として、組み込みのjsonキャストタイプをカスタムキャストタイプとして再実装します。

<?php
 
namespace App\Casts;
 
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
 
class Json implements CastsAttributes
{
/**
* Cast the given value.
*
* @param array<string, mixed> $attributes
* @return array<string, mixed>
*/
public function get(Model $model, string $key, mixed $value, array $attributes): array
{
return json_decode($value, true);
}
 
/**
* Prepare the given value for storage.
*
* @param array<string, mixed> $attributes
*/
public function set(Model $model, string $key, mixed $value, array $attributes): string
{
return json_encode($value);
}
}

カスタムキャストタイプを定義したら、そのクラス名を使用してモデル属性にアタッチできます。

<?php
 
namespace App\Models;
 
use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => Json::class,
];
}
}

値オブジェクトキャスト

値をプリミティブ型にキャストすることには限りません。値をオブジェクトにキャストすることもできます。値をオブジェクトにキャストするカスタムキャストを定義することは、プリミティブ型へのキャストと非常に似ていますが、setメソッドは、モデルに生の、保存可能な値を設定するために使用されるキーと値のペアの配列を返す必要があります。

例として、複数のモデル値を単一のAddress値オブジェクトにキャストするカスタムキャストクラスを定義します。Address値には、lineOnelineTwoの2つの公開プロパティがあると仮定します。

<?php
 
namespace App\Casts;
 
use App\ValueObjects\Address as AddressValueObject;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
 
class Address implements CastsAttributes
{
/**
* Cast the given value.
*
* @param array<string, mixed> $attributes
*/
public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject
{
return new AddressValueObject(
$attributes['address_line_one'],
$attributes['address_line_two']
);
}
 
/**
* Prepare the given value for storage.
*
* @param array<string, mixed> $attributes
* @return array<string, string>
*/
public function set(Model $model, string $key, mixed $value, array $attributes): array
{
if (! $value instanceof AddressValueObject) {
throw new InvalidArgumentException('The given value is not an Address instance.');
}
 
return [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
];
}
}

値オブジェクトにキャストする場合、値オブジェクトに加えられた変更は、モデルが保存される前にモデルに自動的に同期されます。

use App\Models\User;
 
$user = User::find(1);
 
$user->address->lineOne = 'Updated Address Value';
 
$user->save();
lightbulb

値オブジェクトを含むEloquentモデルをJSONまたは配列にシリアライズする予定がある場合は、値オブジェクトにIlluminate\Contracts\Support\ArrayableインターフェースとJsonSerializableインターフェースを実装する必要があります。

値オブジェクトのキャッシング

値オブジェクトにキャストされた属性が解決されると、Eloquentによってキャッシュされます。したがって、属性に再度アクセスした場合、同じオブジェクトインスタンスが返されます。

カスタムキャストクラスのオブジェクトキャッシング動作を無効にする場合は、カスタムキャストクラスに公開withoutObjectCachingプロパティを宣言できます。

class Address implements CastsAttributes
{
public bool $withoutObjectCaching = true;
 
// ...
}

配列/JSONシリアライゼーション

toArrayメソッドとtoJsonメソッドを使用してEloquentモデルが配列またはJSONに変換されると、Illuminate\Contracts\Support\ArrayableインターフェースとJsonSerializableインターフェースを実装している限り、カスタムキャスト値オブジェクトは通常シリアライズされます。ただし、サードパーティライブラリによって提供される値オブジェクトを使用する場合、オブジェクトにこれらのインターフェースを追加できない場合があります。

したがって、カスタムキャストクラスが値オブジェクトのシリアライズを担当することを指定できます。そのためには、カスタムキャストクラスがIlluminate\Contracts\Database\Eloquent\SerializesCastableAttributesインターフェースを実装する必要があります。このインターフェースは、クラスに値オブジェクトのシリアライズされた形式を返すserializeメソッドが含まれている必要があることを示しています。

/**
* Get the serialized representation of the value.
*
* @param array<string, mixed> $attributes
*/
public function serialize(Model $model, string $key, mixed $value, array $attributes): string
{
return (string) $value;
}

インバウンドキャスト

モデルに設定されている値だけを変換し、属性がモデルから取得されるときに操作を実行しないカスタムキャストクラスを作成する必要がある場合があります。

インバウンドのみのカスタムキャストは、CastsInboundAttributesインターフェースを実装する必要があります。これは、setメソッドのみの定義が必要です。make:cast Artisanコマンドは--inboundオプションを使用して、インバウンドのみのキャストクラスを生成できます。

php artisan make:cast Hash --inbound

インバウンドのみのキャストの古典的な例は「ハッシング」キャストです。例えば、指定されたアルゴリズムを使用してインバウンド値をハッシュするキャストを定義できます。

<?php
 
namespace App\Casts;
 
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Database\Eloquent\Model;
 
class Hash implements CastsInboundAttributes
{
/**
* Create a new cast class instance.
*/
public function __construct(
protected string|null $algorithm = null,
) {}
 
/**
* Prepare the given value for storage.
*
* @param array<string, mixed> $attributes
*/
public function set(Model $model, string $key, mixed $value, array $attributes): string
{
return is_null($this->algorithm)
? bcrypt($value)
: hash($this->algorithm, $value);
}
}

キャストパラメーター

カスタムキャストをモデルにアタッチする際、:文字を使用してクラス名から分離し、複数のパラメーターをコンマで区切ることで、キャストパラメーターを指定できます。パラメーターはキャストクラスのコンストラクターに渡されます。

/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'secret' => Hash::class.':sha256',
];
}

キャスト可能

アプリケーションの値オブジェクトが独自のカス��ムキャストクラスを定義できるようにすることができます。カスタムキャストクラスをモデルにアタッチする代わりに、Illuminate\Contracts\Database\Eloquent\Castableインターフェースを実装する値オブジェクトクラスをアタッチすることもできます。

use App\ValueObjects\Address;
 
protected function casts(): array
{
return [
'address' => Address::class,
];
}

Castableインターフェースを実装するオブジェクトは、Castableクラスとの間をキャストする責任を持つカスタムキャスタークラスのクラス名を返すcastUsingメソッドを定義する必要があります。

<?php
 
namespace App\ValueObjects;
 
use Illuminate\Contracts\Database\Eloquent\Castable;
use App\Casts\Address as AddressCast;
 
class Address implements Castable
{
/**
* Get the name of the caster class to use when casting from / to this cast target.
*
* @param array<string, mixed> $arguments
*/
public static function castUsing(array $arguments): string
{
return AddressCast::class;
}
}

Castableクラスを使用する場合でも、castsメソッドの定義で引数を指定できます。引数はcastUsingメソッドに渡されます。

use App\ValueObjects\Address;
 
protected function casts(): array
{
return [
'address' => Address::class.':argument',
];
}

Castableと匿名キャストクラス

「castable」とPHPの匿名クラスを組み合わせることで、値オブジェクトとそのキャスティングロジックを単一のcastableオブジェクトとして定義できます。これを実現するには、値オブジェクトのcastUsingメソッドから匿名クラスを返します。匿名クラスはCastsAttributesインターフェースを実装する必要があります。

<?php
 
namespace App\ValueObjects;
 
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
 
class Address implements Castable
{
// ...
 
/**
* Get the caster class to use when casting from / to this cast target.
*
* @param array<string, mixed> $arguments
*/
public static function castUsing(array $arguments): CastsAttributes
{
return new class implements CastsAttributes
{
public function get(Model $model, string $key, mixed $value, array $attributes): Address
{
return new Address(
$attributes['address_line_one'],
$attributes['address_line_two']
);
}
 
public function set(Model $model, string $key, mixed $value, array $attributes): array
{
return [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
];
}
};
}
}