メインコンテンツまでスキップ
バージョン: 12.x

Eloquent: ミューテタとキャスティング (Eloquent: Mutators & Casting)

導入 (Introduction)

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

アクセサとミューテタ (Accessors and Mutators)

アクセサの定義

アクセサは、アクセス時に 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 インスタンスを返します。この例では、属性にアクセスする方法のみを定義しています。これを行うには、get 引数を Attribute クラス コンストラクターに指定します。

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

use App\Models\User;

$user = User::find(1);

$firstName = $user->first_name;

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

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

場合によっては、アクセサーが複数のモデル属性を 1 つの「値オブジェクト」に変換する必要がある場合があります。これを行うには、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,
],
);
}

属性のキャスト (Attribute Casting)

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

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

  • array
  • AsFluent::class
  • AsStringable::class
  • AsUri::class
  • boolean
  • collection
  • date
  • datetime
  • immutable_date
  • immutable_datetime
  • 10 進数:<精度>
  • 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',
]);

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

ストリング可能な鋳造

Illuminate\Database\Eloquent\Casts\AsStringable キャスト クラスを使用して、モデル属性を fluent Illuminate\Support\Stringable object にキャストできます。

<?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']);

JSON と Unicode

エスケープされていない Unicode 文字を含む JSON として配列属性を保存したい場合は、json:unicode キャストを使用できます。

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

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

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

$user = User::find(1);

$user->options['key'] = $value;

これを解決するために、Laravel は JSON 属性を ArrayObject クラスにキャストする AsArrayObject キャストを提供します。この機能は、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 は、JSON 属性を Laravel Collection インスタンスにキャストする AsCollection キャストを提供します。

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,
];
}

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

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),
];
}

of メソッドは、コレクション項目をコレクションの mapInto メソッド 経由で特定のクラスにマップする必要があることを示すために使用できます。

use App\ValueObjects\Option;
use Illuminate\Database\Eloquent\Casts\AsCollection;

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

コレクションをオブジェクトにマッピングする場合、オブジェクトは Illuminate\Contracts\Support\Arrayable および JsonSerializable インターフェイスを実装して、インスタンスを JSON としてデータベースにシリアル化する方法を定義する必要があります。

<?php

namespace App\ValueObjects;

use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;

class Option implements Arrayable, JsonSerializable
{
public string $name;
public mixed $value;
public bool $isLocked;

/**
* Create a new Option instance.
*/
public function __construct(array $data)
{
$this->name = $data['name'];
$this->value = $data['value'];
$this->isLocked = $data['is_locked'];
}

/**
* Get the instance as an array.
*
* @return array{name: string, data: string, is_locked: bool}
*/
public function toArray(): array
{
return [
'name' => $this->name,
'value' => $this->value,
'is_locked' => $this->isLocked,
];
}

/**
* Specify the data which should be serialized to JSON.
*
* @return array{name: string, data: string, is_locked: bool}
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

バイナリキャスト

Eloquent モデルに、モデルの自動インクリメント ID 列に加えて、バイナリ型 uuid または ulid 列がある場合、AsBinary キャストを使用して、バイナリ表現との間で値を自動的にキャストできます。

use Illuminate\Database\Eloquent\Casts\AsBinary;

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

モデル上でキャストが定義されたら、UUID / ULID 属性値をオブジェクト インスタンスまたは文字列に設定できます。 Eloquent は値をバイナリ表現に自動的にキャストします。属性の値を取得するときは、常にプレーンテキストの文字列値を受け取ります。

use Illuminate\Support\Str;

$user->uuid = Str::uuid();

return $user->uuid;

// "6e8cdeed-2f32-40bd-b109-1e4405be2140"

デートキャスト

デフォルトでは、Eloquent は created_at 列と updated_at 列を 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',
];
}

When a column is cast as a date, you may set the corresponding model attribute value to a UNIX timestamp, date string (Y-m-d), date-time string, or a DateTime / Carbon instance.日付の値は正しく変換され、データベースに保存されます。

モデルで 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';

日付のキャスト、シリアル化、およびタイムゾーン

デフォルトでは、date および datetime キャストは、アプリケーションの 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 Enums にキャストすることもできます。これを実現するには、モデルの 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();
}

列挙型の配列のキャスト

場合によっては、モデルで列挙値の配列を 1 つの列に格納する必要がある場合があります。これを実現するには、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 の組み込み encryption 機能を使用してモデルの属性値を暗号化します。さらに、encrypted:arrayencrypted:collectionencrypted:objectAsEncryptedArrayObject、および AsEncryptedCollection キャストは、暗号化されていないキャストと同様に機能します。ただし、ご想像のとおり、基になる値はデータベースに保存されるときに暗号化されます。

暗号化されたテキストの最終的な長さは予測できず、対応する平文よりも長いため、関連するデータベース列が 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();

カスタムキャスト (Custom Casts)

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

php artisan make:cast AsJson

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

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class AsJson 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\AsJson;
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' => AsJson::class,
];
}
}

値オブジェクトのキャスト

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

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

<?php

namespace App\Casts;

use App\ValueObjects\Address;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;

class AsAddress implements CastsAttributes
{
/**
* Cast the given value.
*
* @param array<string, mixed> $attributes
*/
public function get(
Model $model,
string $key,
mixed $value,
array $attributes,
): Address {
return new Address(
$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 Address) {
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();

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

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

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

カスタム キャスト クラスのオブジェクト キャッシュ動作を無効にしたい場合は、カスタム キャスト クラスでパブリック withoutObjectCaching プロパティを宣言できます。

class AsAddress implements CastsAttributes
{
public bool $withoutObjectCaching = true;

// ...
}

配列/JSONシリアル化

Eloquent モデルが toArray および toJson メソッドを使用して配列または 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 AsHash --inbound

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

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Database\Eloquent\Model;

class AsHash 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' => AsHash::class.':sha256',
];
}

キャスト値の比較

指定された 2 つのキャスト値を比較して、それらが変更されたかどうかを判断する方法を定義したい場合は、カスタム キャスト クラスで Illuminate\Contracts\Database\Eloquent\ComparesCastableAttributes インターフェイスを実装できます。これにより、モデルの更新時に Eloquent がどの値が変更されたとみなしてデータベースに保存するかをきめ細かく制御できるようになります。

このインターフェイスは、指定された値が等しいとみなされる場合に true を返す compare メソッドをクラスに含める必要があることを示しています。

/**
* Determine if the given values are equal.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $firstValue
* @param mixed $secondValue
* @return bool
*/
public function compare(
Model $model,
string $key,
mixed $firstValue,
mixed $secondValue
): bool {
return $firstValue === $secondValue;
}

キャスタブル

アプリケーションの値オブジェクトが独自のカスタム キャスト クラスを定義できるようにしたい場合があります。カスタム キャスト クラスをモデルにアタッチする代わりに、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\AsAddress;

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 AsAddress::class;
}
}

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

use App\ValueObjects\Address;

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

キャスタブルと匿名キャスト クラス

「キャスト可能」を PHP の 匿名クラス と組み合わせることで、値オブジェクトとそのキャスト ロジックを単一のキャスト可能オブジェクトとして定義できます。これを実現するには、値オブジェクトの 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,
];
}
};
}
}