跳至内容

Eloquent:变量和转换

介绍

访问器、修改器和属性强制转换功能允许您在检索或设置模型实例的 Eloquent 属性值时对其进行转换。例如,您可能希望使用Laravel 加密器在值存储在数据库中时对其进行加密,然后在通过 Eloquent 模型访问该值时自动解密该属性。或者,您可能希望在通过 Eloquent 模型访问数据库中存储的 JSON 字符串时将其转换为数组。

访问器和修改器

定义访问器

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

在此示例中,我们将为属性定义一个访问器first_name。当尝试检索属性值时,Eloquent 将自动调用该访问器first_name。所有属性访问器/修改器方法都必须声明一个返回类型PromptsIlluminate\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实例,该实例定义了如何访问属性,以及(可选)如何修改属性。在本例中,我们只定义了如何访问属性。为此,我们将get参数传递给Attribute类的构造函数。

如你所见,列的原始值被传递给访问器,允许你操作并返回该值。要访问访问器的值,你可以简单地访问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}

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

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

在此示例中,set回调函数将使用值 进行调用Sally。然后,修改器将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

is_admin为了演示属性转换,我们将数据库中存储的整数(0或)属性转换1为布尔值:

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不会被强制转换。此外,切勿定义与关系同名的强制转换(或属性),也不要将强制转换赋给模型的主键。

可串铸

您可以使用Illuminate\Database\Eloquent\Casts\AsStringable转换类将模型属性转换为流畅的 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 转换

这种array强制类型转换在处理以序列化 JSON 格式存储的列时尤其有用。例如,如果你的数据库有一个包含序列化 JSON 的JSONTEXT字段类型,array那么当你在 Eloquent 模型中访问该属性时,向该属性添加强制类型转换将自动将其反序列化为 PHP 数组:

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

JSON 和 Unicode

如果您想要将数组属性存储为具有未转义 Unicode 字符的 JSON,则可以使用以下json:unicode转换:

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

数组对象和集合转换

虽然标准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}

of方法可用于指示集合项应通过集合的mapInto 方法映射到给定类中:

1use App\ValueObjects\Option;
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::of(Option::class)
13 ];
14}

将集合映射到对象时,对象应该实现Illuminate\Contracts\Support\ArrayableJsonSerializable接口来定义如何将其实例以 JSON 形式序列化到数据库中:

1<?php
2 
3namespace App\ValueObjects;
4 
5use Illuminate\Contracts\Support\Arrayable;
6use JsonSerializable;
7 
8class Option implements Arrayable, JsonSerializable
9{
10 public string $name;
11 public mixed $value;
12 public bool $isLocked;
13 
14 /**
15 * Create a new Option instance.
16 */
17 public function __construct(array $data)
18 {
19 $this->name = $data['name'];
20 $this->value = $data['value'];
21 $this->isLocked = $data['is_locked'];
22 }
23 
24 /**
25 * Get the instance as an array.
26 *
27 * @return array{name: string, data: string, is_locked: bool}
28 */
29 public function toArray(): array
30 {
31 return [
32 'name' => $this->name,
33 'value' => $this->value,
34 'is_locked' => $this->isLocked,
35 ];
36 }
37 
38 /**
39 * Specify the data which should be serialized to JSON.
40 *
41 * @return array{name: string, data: string, is_locked: bool}
42 */
43 public function jsonSerialize(): array
44 {
45 return $this->toArray();
46 }
47}

日期转换

默认情况下,Eloquent 会将created_at和列转换为Carbonupdated_at的实例,Carbon 扩展了 PHP类并提供了一系列实用的方法。您可以通过在模型方法中定义额外的日期转换来转换其他日期属性。通常,日期应该使用转换类型进行转换。DateTimecastsdatetimeimmutable_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 日期字符串 ( )。强烈建议您始终使用此序列化格式,并通过不更改应用程序配置选项的默认值,将应用程序的日期存储在 UTC 时区中。在整个应用程序中始终使用 UTC 时区将提供与其他用 PHP 和 JavaScript 编写的日期操作库的最大程度的互操作性。YYYY-MM-DDTHH:MM:SS.uuuuuuZtimezonetimezoneUTC

date如果对或类型转换应用了自定义格式datetime(例如datetime:Y-m-d H:i:s),则在日期序列化过程中将使用 Carbon 实例的内部时区。通常,这将是应用程序timezone配置选项中指定的时区。但是,需要注意的是,timestamp诸如created_at和 之类的列updated_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 提供的AsEnumArrayObject或强制类型转换:AsEnumCollection

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:array, 、encrypted:collectionencrypted:objectAsEncryptedArrayObjectAsEncryptedCollection强制类型转换的工作方式与未加密的强制类型转换类似;然而,正如您所料,底层值在存储在数据库中时是加密的。

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

密钥轮换

您可能知道,Laravel 使用key应用程序app配置文件中指定的配置值来加密字符串。通常,此值对应于环境变量的值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:castArtisan 命令。新的强制类型转换类将放置在你的app/Casts目录中:

1php artisan make:cast AsJson

所有自定义强制类型转换类都实现了该CastsAttributes接口。实现此接口的类必须定义一个getandset方法。该get方法负责将数据库中的原始值转换为强制类型转换值,而该set方法则负责将强制类型转换值转换为可以存储在数据库中的原始值。作为示例,我们将内置json强制类型转换类型重新实现为自定义强制类型:

1<?php
2 
3namespace App\Casts;
4 
5use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
6use Illuminate\Database\Eloquent\Model;
7 
8class AsJson 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(
17 Model $model,
18 string $key,
19 mixed $value,
20 array $attributes,
21 ): array {
22 return json_decode($value, true);
23 }
24 
25 /**
26 * Prepare the given value for storage.
27 *
28 * @param array<string, mixed> $attributes
29 */
30 public function set(
31 Model $model,
32 string $key,
33 mixed $value,
34 array $attributes,
35 ): string {
36 return json_encode($value);
37 }
38}

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

1<?php
2 
3namespace App\Models;
4 
5use App\Casts\AsJson;
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' => AsJson::class,
19 ];
20 }
21}

值对象转换

您不仅可以将值转换为原始类型,还可以将值转换为对象。定义将值转换为对象的自定义转换与转换为原始类型非常相似;但是,如果您的值对象包含多个数据库列,则该set方法必须返回一个键/值对数组,该数组将用于在模型上设置原始的可存储值。如果您的值对象仅影响单个列,则应直接返回可存储的值。

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

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

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

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\Arrayable和接口。JsonSerializable

值对象缓存

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

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

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

数组/JSON 序列化

toArray当使用和方法将 Eloquent 模型转换为数组或 JSON 时toJson,只要自定义转换的值对象实现了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(
7 Model $model,
8 string $key,
9 mixed $value,
10 array $attributes,
11): string {
12 return (string) $value;
13}

入站选角

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

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

1php artisan make:cast AsHash --inbound

一个典型的仅用于入站强制类型转换的例子是“哈希”强制类型转换。例如,我们可以定义一个强制类型转换,通过给定的算法对入站值进行哈希处理:

1<?php
2 
3namespace App\Casts;
4 
5use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
6use Illuminate\Database\Eloquent\Model;
7 
8class AsHash 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(
23 Model $model,
24 string $key,
25 mixed $value,
26 array $attributes,
27 ): string {
28 return is_null($this->algorithm)
29 ? bcrypt($value)
30 : hash($this->algorithm, $value);
31 }
32}

转换参数

将自定义强制类型转换附加到模型时,可以使用逗号分隔多个参数,并将参数与类名分开来指定:强制类型转换参数。这些参数将被传递给强制类型转换类的构造函数:

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' => AsHash::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

1<?php
2 
3namespace App\ValueObjects;
4 
5use Illuminate\Contracts\Database\Eloquent\Castable;
6use App\Casts\AsAddress;
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 AsAddress::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}

Castables 和匿名 Cast 类

通过将“可转换类型”与 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(
22 Model $model,
23 string $key,
24 mixed $value,
25 array $attributes,
26 ): Address {
27 return new Address(
28 $attributes['address_line_one'],
29 $attributes['address_line_two']
30 );
31 }
32 
33 public function set(
34 Model $model,
35 string $key,
36 mixed $value,
37 array $attributes,
38 ): array {
39 return [
40 'address_line_one' => $value->lineOne,
41 'address_line_two' => $value->lineTwo,
42 ];
43 }
44 };
45 }
46}