跳到内容

Eloquent:修改器 & 类型转换

简介

访问器、修改器和属性类型转换允许你在检索或设置模型实例上的 Eloquent 属性值时转换它们。例如,你可能想使用 Laravel 加密器 来加密存储在数据库中的值,然后在你访问 Eloquent 模型上的属性时自动解密该属性。或者,你可能想在你通过 Eloquent 模型访问数据库中存储的 JSON 字符串时将其转换为数组。

访问器和修改器

定义访问器

访问器在属性被访问时转换 Eloquent 属性值。要定义访问器,请在你的模型上创建一个受保护的方法来表示可访问的属性。在适用时,此方法名称应与真实底层模型属性/数据库列的“驼峰式”表示形式相对应。

在此示例中,我们将为 first_name 属性定义一个访问器。当尝试检索 first_name 属性的值时,Eloquent 将自动调用该访问器。所有属性访问器/修改器方法都必须声明 Illuminate\Database\Eloquent\Casts\Attribute 的返回类型提示

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Casts\Attribute;
6use Illuminate\Database\Eloquent\Model;
7 
8class User extends Model
9{
10 /**
11 * Get the user's first name.
12 */
13 protected function firstName(): Attribute
14 {
15 return Attribute::make(
16 get: fn (string $value) => ucfirst($value),
17 );
18 }
19}

所有访问器方法都返回一个 Attribute 实例,该实例定义了属性将如何被访问,以及可选地如何被修改。在此示例中,我们仅定义属性将如何被访问。为此,我们向 Attribute 类构造函数提供 get 参数。

如你所见,列的原始值被传递给访问器,允许你操作并返回值。要访问访问器的值,你可以简单地访问模型实例上的 first_name 属性

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$firstName = $user->first_name;

如果你希望将这些计算值添加到模型的数组/JSON 表示形式中,你将需要附加它们

从多个属性构建值对象

有时你的访问器可能需要将多个模型属性转换为单个“值对象”。为此,你的 get 闭包可以接受 $attributes 的第二个参数,该参数将自动提供给闭包,并将包含模型所有当前属性的数组

1use App\Support\Address;
2use Illuminate\Database\Eloquent\Casts\Attribute;
3 
4/**
5 * Interact with the user's address.
6 */
7protected function address(): Attribute
8{
9 return Attribute::make(
10 get: fn (mixed $value, array $attributes) => new Address(
11 $attributes['address_line_one'],
12 $attributes['address_line_two'],
13 ),
14 );
15}

访问器缓存

当从访问器返回值对象时,对值对象所做的任何更改都将在模型保存之前自动同步回模型。之所以如此,是因为 Eloquent 保留了访问器返回的实例,以便每次调用访问器时都可以返回相同的实例

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

但是,你有时可能希望为原始值(如字符串和布尔值)启用缓存,特别是当它们在计算上是密集型的时候。为了实现这一点,你可以在定义访问器时调用 shouldCache 方法

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

如果你想禁用属性的对象缓存行为,你可以在定义属性时调用 withoutObjectCaching 方法

1/**
2 * Interact with the user's address.
3 */
4protected function address(): Attribute
5{
6 return Attribute::make(
7 get: fn (mixed $value, array $attributes) => new Address(
8 $attributes['address_line_one'],
9 $attributes['address_line_two'],
10 ),
11 )->withoutObjectCaching();
12}

定义修改器

修改器在属性被设置时转换 Eloquent 属性值。要定义修改器,你可以在定义属性时提供 set 参数。让我们为 first_name 属性定义一个修改器。当我们尝试在模型上设置 first_name 属性的值时,将自动调用此修改器

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Casts\Attribute;
6use Illuminate\Database\Eloquent\Model;
7 
8class User extends Model
9{
10 /**
11 * Interact with the user's first name.
12 */
13 protected function firstName(): Attribute
14 {
15 return Attribute::make(
16 get: fn (string $value) => ucfirst($value),
17 set: fn (string $value) => strtolower($value),
18 );
19 }
20}

修改器闭包将接收正在设置到属性上的值,允许你操作该值并返回操作后的值。要使用我们的修改器,我们只需要在 Eloquent 模型上设置 first_name 属性

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

在此示例中,将使用值 Sally 调用 set 回调。然后,修改器将应用 strtolower 函数到名称,并在模型的内部 $attributes 数组中设置其结果值。

修改多个属性

有时你的修改器可能需要在底层模型上设置多个属性。为此,你可以从 set 闭包返回一个数组。数组中的每个键都应与模型关联的底层属性/数据库列相对应

1use App\Support\Address;
2use Illuminate\Database\Eloquent\Casts\Attribute;
3 
4/**
5 * Interact with the user's address.
6 */
7protected function address(): Attribute
8{
9 return Attribute::make(
10 get: fn (mixed $value, array $attributes) => new Address(
11 $attributes['address_line_one'],
12 $attributes['address_line_two'],
13 ),
14 set: fn (Address $value) => [
15 'address_line_one' => $value->lineOne,
16 'address_line_two' => $value->lineTwo,
17 ],
18 );
19}

属性类型转换

属性类型转换为你提供了类似于访问器和修改器的功能,而无需你在模型上定义任何其他方法。相反,你模型的 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

为了演示属性类型转换,让我们将数据库中存储为整数(01)的 is_admin 属性转换为布尔值

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Model;
6 
7class User extends Model
8{
9 /**
10 * Get the attributes that should be cast.
11 *
12 * @return array<string, string>
13 */
14 protected function casts(): array
15 {
16 return [
17 'is_admin' => 'boolean',
18 ];
19 }
20}

定义类型转换后,当你访问 is_admin 属性时,它将始终被转换为布尔值,即使底层值在数据库中存储为整数

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

如果你需要在运行时添加新的临时类型转换,你可以使用 mergeCasts 方法。这些类型转换定义将被添加到模型上已定义的任何类型转换中

1$user->mergeCasts([
2 'is_admin' => 'integer',
3 'options' => 'object',
4]);

null 属性将不会被类型转换。此外,你不应定义与关联关系同名的类型转换(或属性),也不应将类型转换分配给模型的主键。

Stringable 类型转换

你可以使用 Illuminate\Database\Eloquent\Casts\AsStringable 类型转换类将模型属性转换为 fluent Illuminate\Support\Stringable 对象

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Casts\AsStringable;
6use Illuminate\Database\Eloquent\Model;
7 
8class User extends Model
9{
10 /**
11 * Get the attributes that should be cast.
12 *
13 * @return array<string, string>
14 */
15 protected function casts(): array
16 {
17 return [
18 'directory' => AsStringable::class,
19 ];
20 }
21}

数组和 JSON 类型转换

当处理存储为序列化 JSON 的列时,array 类型转换特别有用。例如,如果你的数据库具有包含序列化 JSON 的 JSONTEXT 字段类型,则向该属性添加 array 类型转换将自动将该属性反序列化为 PHP 数组,当你访问 Eloquent 模型上的该属性时

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Model;
6 
7class User extends Model
8{
9 /**
10 * Get the attributes that should be cast.
11 *
12 * @return array<string, string>
13 */
14 protected function casts(): array
15 {
16 return [
17 'options' => 'array',
18 ];
19 }
20}

一旦定义了类型转换,你就可以访问 options 属性,它将自动从 JSON 反序列化为 PHP 数组。当你设置 options 属性的值时,给定的数组将自动序列化回 JSON 以进行存储

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$options = $user->options;
6 
7$options['key'] = 'value';
8 
9$user->options = $options;
10 
11$user->save();

要使用更简洁的语法更新 JSON 属性的单个字段,你可以使该属性可批量赋值,并在调用 update 方法时使用 -> 运算符

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

数组对象和集合类型转换

虽然标准的 array 类型转换足以满足许多应用程序,但它确实有一些缺点。由于 array 类型转换返回原始类型,因此无法直接修改数组的偏移量。例如,以下代码将触发 PHP 错误

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

为了解决这个问题,Laravel 提供了 AsArrayObject 类型转换,该类型转换将你的 JSON 属性转换为 ArrayObject 类。此功能是使用 Laravel 的 自定义类型转换 实现的,该实现允许 Laravel 智能地缓存和转换已修改的对象,从而可以修改单个偏移量而不会触发 PHP 错误。要使用 AsArrayObject 类型转换,只需将其分配给属性

1use Illuminate\Database\Eloquent\Casts\AsArrayObject;
2 
3/**
4 * Get the attributes that should be cast.
5 *
6 * @return array<string, string>
7 */
8protected function casts(): array
9{
10 return [
11 'options' => AsArrayObject::class,
12 ];
13}

类似地,Laravel 提供了 AsCollection 类型转换,该类型转换将你的 JSON 属性转换为 Laravel Collection 实例

1use Illuminate\Database\Eloquent\Casts\AsCollection;
2 
3/**
4 * Get the attributes that should be cast.
5 *
6 * @return array<string, string>
7 */
8protected function casts(): array
9{
10 return [
11 'options' => AsCollection::class,
12 ];
13}

如果你希望 AsCollection 类型转换实例化自定义集合类而不是 Laravel 的基本集合类,你可以提供集合类名称作为类型转换参数

1use App\Collections\OptionCollection;
2use Illuminate\Database\Eloquent\Casts\AsCollection;
3 
4/**
5 * Get the attributes that should be cast.
6 *
7 * @return array<string, string>
8 */
9protected function casts(): array
10{
11 return [
12 'options' => AsCollection::using(OptionCollection::class),
13 ];
14}

日期类型转换

默认情况下,Eloquent 会将 created_atupdated_at 列转换为 Carbon 的实例,Carbon 扩展了 PHP DateTime 类并提供了一系列有用的方法。你可以通过在模型的 casts 方法中定义其他日期类型转换来转换其他日期属性。通常,日期应使用 datetimeimmutable_datetime 类型转换类型进行转换。

当定义 datedatetime 类型转换时,你还可以指定日期的格式。当 模型被序列化为数组或 JSON 时,将使用此格式

1/**
2 * Get the attributes that should be cast.
3 *
4 * @return array<string, string>
5 */
6protected function casts(): array
7{
8 return [
9 'created_at' => 'datetime:Y-m-d',
10 ];
11}

当列被类型转换为日期时,你可以将相应的模型属性值设置为 UNIX 时间戳、日期字符串 (Y-m-d)、日期时间字符串或 DateTime / Carbon 实例。日期的值将被正确转换并存储在你的数据库中。

你可以通过在你的模型上定义 serializeDate 方法来自定义所有模型的日期默认序列化格式。此方法不会影响你的日期在数据库中存储的格式

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

要指定在实际将模型的日期存储在数据库中时应使用的格式,你应该在你的模型上定义 $dateFormat 属性

1/**
2 * The storage format of the model's date columns.
3 *
4 * @var string
5 */
6protected $dateFormat = 'U';

日期类型转换、序列化和时区

默认情况下,datedatetime 类型转换会将日期序列化为 UTC ISO-8601 日期字符串 (YYYY-MM-DDTHH:MM:SS.uuuuuuZ),无论你的应用程序的 timezone 配置选项中指定的时区是什么。强烈建议你始终使用此序列化格式,并且通过不更改应用程序的 timezone 配置选项的默认 UTC 值来将应用程序的日期存储在 UTC 时区中。在你的应用程序中始终如一地使用 UTC 时区将为与其他用 PHP 和 JavaScript 编写的日期操作库提供最大程度的互操作性。

如果自定义格式应用于 datedatetime 类型转换,例如 datetime:Y-m-d H:i:s,则 Carbon 实例的内部时区将在日期序列化期间使用。通常,这将是你的应用程序的 timezone 配置选项中指定的时区。但是,重要的是要注意,诸如 created_atupdated_at 之类的时间戳列不受此行为的约束,并且始终以 UTC 格式格式化,而与应用程序的时区设置无关。

枚举类型转换

Eloquent 还允许你将属性值转换为 PHP 枚举。为了实现这一点,你可以在模型的 casts 方法中指定属性和你希望转换的枚举

1use App\Enums\ServerStatus;
2 
3/**
4 * Get the attributes that should be cast.
5 *
6 * @return array<string, string>
7 */
8protected function casts(): array
9{
10 return [
11 'status' => ServerStatus::class,
12 ];
13}

一旦你在模型上定义了类型转换,当你与属性交互时,指定的属性将自动转换为枚举和从枚举转换

1if ($server->status == ServerStatus::Provisioned) {
2 $server->status = ServerStatus::Ready;
3 
4 $server->save();
5}

类型转换枚举数组

有时你可能需要你的模型在单个列中存储枚举值数组。为了实现这一点,你可以使用 Laravel 提供的 AsEnumArrayObjectAsEnumCollection 类型转换

1use App\Enums\ServerStatus;
2use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
3 
4/**
5 * Get the attributes that should be cast.
6 *
7 * @return array<string, string>
8 */
9protected function casts(): array
10{
11 return [
12 'statuses' => AsEnumCollection::of(ServerStatus::class),
13 ];
14}

加密类型转换

encrypted 类型转换将使用 Laravel 的内置 加密 功能加密模型的属性值。此外,encrypted:arrayencrypted:collectionencrypted:objectAsEncryptedArrayObjectAsEncryptedCollection 类型转换的工作方式与其未加密的对应项类似;但是,正如你可能期望的那样,底层值在存储在你的数据库中时会被加密。

由于加密文本的最终长度是不可预测的,并且比其纯文本对应项更长,因此请确保关联的数据库列类型为 TEXT 类型或更大。此外,由于值在数据库中被加密,因此你将无法查询或搜索加密的属性值。

密钥轮换

你可能知道,Laravel 使用你的应用程序的 app 配置文件中指定的 key 配置值来加密字符串。通常,此值与 APP_KEY 环境变量的值相对应。如果你需要轮换应用程序的加密密钥,你将需要使用新密钥手动重新加密你的加密属性。

查询时类型转换

有时你可能需要在执行查询时应用类型转换,例如在从表中选择原始值时。例如,考虑以下查询

1use App\Models\Post;
2use App\Models\User;
3 
4$users = User::select([
5 'users.*',
6 'last_posted_at' => Post::selectRaw('MAX(created_at)')
7 ->whereColumn('user_id', 'users.id')
8])->get();

此查询结果上的 last_posted_at 属性将是一个简单的字符串。如果我们可以在执行查询时将 datetime 类型转换应用于此属性,那将是太好了。值得庆幸的是,我们可以使用 withCasts 方法来实现这一点

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

自定义类型转换

Laravel 有各种内置的、有用的类型转换类型;但是,你有时可能需要定义自己的类型转换类型。要创建类型转换,请执行 make:cast Artisan 命令。新的类型转换类将放置在你的 app/Casts 目录中

1php artisan make:cast Json

所有自定义类型转换类都实现 CastsAttributes 接口。实现此接口的类必须定义 getset 方法。get 方法负责将数据库中的原始值转换为类型转换值,而 set 方法应将类型转换值转换为可以存储在数据库中的原始值。例如,我们将重新实现内置的 json 类型转换类型作为自定义类型转换类型

1<?php
2 
3namespace App\Casts;
4 
5use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
6use Illuminate\Database\Eloquent\Model;
7 
8class Json implements CastsAttributes
9{
10 /**
11 * Cast the given value.
12 *
13 * @param array<string, mixed> $attributes
14 * @return array<string, mixed>
15 */
16 public function get(Model $model, string $key, mixed $value, array $attributes): array
17 {
18 return json_decode($value, true);
19 }
20 
21 /**
22 * Prepare the given value for storage.
23 *
24 * @param array<string, mixed> $attributes
25 */
26 public function set(Model $model, string $key, mixed $value, array $attributes): string
27 {
28 return json_encode($value);
29 }
30}

一旦你定义了自定义类型转换类型,你就可以使用其类名将其附加到模型属性

1<?php
2 
3namespace App\Models;
4 
5use App\Casts\Json;
6use Illuminate\Database\Eloquent\Model;
7 
8class User extends Model
9{
10 /**
11 * Get the attributes that should be cast.
12 *
13 * @return array<string, string>
14 */
15 protected function casts(): array
16 {
17 return [
18 'options' => Json::class,
19 ];
20 }
21}

值对象类型转换

你不限于将值类型转换为原始类型。你还可以将值类型转换为对象。定义将值类型转换为对象的自定义类型转换与类型转换为原始类型非常相似;但是,set 方法应返回键/值对数组,该数组将用于在模型上设置原始的可存储值。

例如,我们将定义一个自定义类型转换类,该类将多个模型值转换为单个 Address 值对象。我们将假设 Address 值对象具有两个公共属性:lineOnelineTwo

1<?php
2 
3namespace App\Casts;
4 
5use App\ValueObjects\Address as AddressValueObject;
6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
7use Illuminate\Database\Eloquent\Model;
8use InvalidArgumentException;
9 
10class Address implements CastsAttributes
11{
12 /**
13 * Cast the given value.
14 *
15 * @param array<string, mixed> $attributes
16 */
17 public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject
18 {
19 return new AddressValueObject(
20 $attributes['address_line_one'],
21 $attributes['address_line_two']
22 );
23 }
24 
25 /**
26 * Prepare the given value for storage.
27 *
28 * @param array<string, mixed> $attributes
29 * @return array<string, string>
30 */
31 public function set(Model $model, string $key, mixed $value, array $attributes): array
32 {
33 if (! $value instanceof AddressValueObject) {
34 throw new InvalidArgumentException('The given value is not an Address instance.');
35 }
36 
37 return [
38 'address_line_one' => $value->lineOne,
39 'address_line_two' => $value->lineTwo,
40 ];
41 }
42}

当类型转换为值对象时,对值对象所做的任何更改都将在模型保存之前自动同步回模型

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$user->address->lineOne = 'Updated Address Value';
6 
7$user->save();

如果你计划将包含值对象的 Eloquent 模型序列化为 JSON 或数组,你应在值对象上实现 Illuminate\Contracts\Support\ArrayableJsonSerializable 接口。

值对象缓存

当解析类型转换为值对象的属性时,它们会被 Eloquent 缓存。因此,如果再次访问该属性,将返回相同的对象实例。

如果你想禁用自定义类型转换类的对象缓存行为,你可以在你的自定义类型转换类上声明一个公共 withoutObjectCaching 属性

1class Address implements CastsAttributes
2{
3 public bool $withoutObjectCaching = true;
4 
5 // ...
6}

数组 / JSON 序列化

当使用 toArraytoJson 方法将 Eloquent 模型转换为数组或 JSON 时,只要你的自定义类型转换值对象实现了 Illuminate\Contracts\Support\ArrayableJsonSerializable 接口,它们通常也会被序列化。但是,当使用第三方库提供的值对象时,你可能无法将这些接口添加到对象中。

因此,你可以指定你的自定义类型转换类将负责序列化值对象。为此,你的自定义类型转换类应实现 Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes 接口。此接口声明你的类应包含一个 serialize 方法,该方法应返回你的值对象的序列化形式

1/**
2 * Get the serialized representation of the value.
3 *
4 * @param array<string, mixed> $attributes
5 */
6public function serialize(Model $model, string $key, mixed $value, array $attributes): string
7{
8 return (string) $value;
9}

入站类型转换

有时,你可能需要编写一个自定义类型转换类,该类仅转换正在模型上设置的值,并且在从模型检索属性时不执行任何操作。

仅入站自定义类型转换应实现 CastsInboundAttributes 接口,该接口仅需要定义 set 方法。可以使用 --inbound 选项调用 make:cast Artisan 命令来生成仅入站类型转换类

1php artisan make:cast Hash --inbound

仅入站类型转换的一个经典示例是“哈希”类型转换。例如,我们可以定义一个类型转换,该类型转换通过给定的算法哈希入站值

1<?php
2 
3namespace App\Casts;
4 
5use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
6use Illuminate\Database\Eloquent\Model;
7 
8class Hash implements CastsInboundAttributes
9{
10 /**
11 * Create a new cast class instance.
12 */
13 public function __construct(
14 protected string|null $algorithm = null,
15 ) {}
16 
17 /**
18 * Prepare the given value for storage.
19 *
20 * @param array<string, mixed> $attributes
21 */
22 public function set(Model $model, string $key, mixed $value, array $attributes): string
23 {
24 return is_null($this->algorithm)
25 ? bcrypt($value)
26 : hash($this->algorithm, $value);
27 }
28}

类型转换参数

当将自定义类型转换附加到模型时,可以通过使用 : 字符分隔类型转换参数和类名,并使用逗号分隔多个参数来指定类型转换参数。这些参数将传递给类型转换类的构造函数

1/**
2 * Get the attributes that should be cast.
3 *
4 * @return array<string, string>
5 */
6protected function casts(): array
7{
8 return [
9 'secret' => Hash::class.':sha256',
10 ];
11}

可类型转换的

你可能希望允许你的应用程序的值对象定义它们自己的自定义类型转换类。除了将自定义类型转换类附加到你的模型之外,你还可以附加实现 Illuminate\Contracts\Database\Eloquent\Castable 接口的值对象类

1use App\ValueObjects\Address;
2 
3protected function casts(): array
4{
5 return [
6 'address' => Address::class,
7 ];
8}

实现 Castable 接口的对象必须定义一个 castUsing 方法,该方法返回负责类型转换为 Castable 类和从 Castable 类类型转换的自定义类型转换器类的类名

1<?php
2 
3namespace App\ValueObjects;
4 
5use Illuminate\Contracts\Database\Eloquent\Castable;
6use App\Casts\Address as AddressCast;
7 
8class Address implements Castable
9{
10 /**
11 * Get the name of the caster class to use when casting from / to this cast target.
12 *
13 * @param array<string, mixed> $arguments
14 */
15 public static function castUsing(array $arguments): string
16 {
17 return AddressCast::class;
18 }
19}

当使用 Castable 类时,你仍然可以在 casts 方法定义中提供参数。这些参数将传递给 castUsing 方法

1use App\ValueObjects\Address;
2 
3protected function casts(): array
4{
5 return [
6 'address' => Address::class.':argument',
7 ];
8}

可类型转换 & 匿名类型转换类

通过将“可类型转换”与 PHP 的 匿名类 结合使用,你可以将值对象及其类型转换逻辑定义为单个可类型转换对象。为了实现这一点,从你的值对象的 castUsing 方法返回一个匿名类。匿名类应实现 CastsAttributes 接口

1<?php
2 
3namespace App\ValueObjects;
4 
5use Illuminate\Contracts\Database\Eloquent\Castable;
6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
7 
8class Address implements Castable
9{
10 // ...
11 
12 /**
13 * Get the caster class to use when casting from / to this cast target.
14 *
15 * @param array<string, mixed> $arguments
16 */
17 public static function castUsing(array $arguments): CastsAttributes
18 {
19 return new class implements CastsAttributes
20 {
21 public function get(Model $model, string $key, mixed $value, array $attributes): Address
22 {
23 return new Address(
24 $attributes['address_line_one'],
25 $attributes['address_line_two']
26 );
27 }
28 
29 public function set(Model $model, string $key, mixed $value, array $attributes): array
30 {
31 return [
32 'address_line_one' => $value->lineOne,
33 'address_line_two' => $value->lineTwo,
34 ];
35 }
36 };
37 }
38}

Laravel 是最有效的方式来
构建、部署和监控软件。