访问器、修改器和属性转换允许你在模型实例上获取或设置 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 实例,该实例定义了属性如何被访问,以及,可选地,被修改。在本例中,我们只定义了属性如何被访问。为此,我们将 get 参数传递给 Attribute 类构造函数。
如你所见,列的原始值被传递给访问器,允许你操作并返回该值。要访问访问器的值,你可以简单地访问模型实例上的first_name属性:
use App\Models\User;
$user = User::find(1);
$firstName = $user->first_name;[!注意]
如果您希望将这些计算值添加到模型的数组/JSON表示中,您需要附加它们。
有时,你的访问器可能需要将多个模型属性转换为单个“值对象”。为此,你的 get 闭包可以接受第二个参数 $attributes,它将自动提供给闭包,并且包含模型所有当前属性的数组:
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 方法应返回一个数组,其中键是正在进行类型转换的属性名称,值是您希望将列转换成的类型。支持的类型转换有:
为了演示属性类型转换,让我们将is_admin属性,该属性在数据库中存储为整数 (0 或 1) 转换为布尔值:
<?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 转换类,将模型属性转换为一个 链式 Illuminate\Support\Stringable 对象:
<?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,
];
}
}array 类型转换在处理存储为序列化 JSON 的列时特别有用。例如,如果您的数据库中有一个 JSON 或 TEXT 字段类型,其中包含序列化的 JSON,将 array 类型转换添加到该属性将自动把该属性反序列化为一个 PHP 数组,当您在 Eloquent 模型中访问它时:
<?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 字符的数组属性,你可以使用 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 提供了一个 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 Collection 实例:
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 会将 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',
];
}当列被转换为日期类型时,您可以将对应的模型属性值设置为 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';默认情况下,date 和 datetime 类型转换会将日期序列化为 UTC ISO-8601 日期字符串 (YYYY-MM-DDTHH:MM:SS.uuuuuuZ),无论您的应用程序的 timezone 配置选项中指定了什么时区。强烈建议您始终使用此序列化格式,以及将应用程序的日期存储在 UTC 时区中通过不将应用程序的 timezone 配置选项从其默认 UTC 值更改。在整个应用程序中一致地使用 UTC 时区将提供与用 PHP 和 JavaScript 编写的其他日期操作库的最大程度的互操作性。
如果自定义格式应用于 date 或 datetime 类型转换,例如 datetime:Y-m-d H:i:s,在日期序列化期间将使用 Carbon 实例的内部时区。通常,这将是您应用程序中 timezone 配置选项中指定的时区。然而,需要注意的是,timestamp 列,例如 created_at 和 updated_at,不受此行为影响,并且始终以 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:array、encrypted:collection、encrypted:object、AsEncryptedArrayObject 和 AsEncryptedCollection 转换的作用类似于它们未加密的对应项;然而,正如你可能预期的那样,当存储在你的数据库中时,基础值会被加密。
由于加密文本的最终长度不可预测且比其对应的明文长,请确保相关的数据库列为 TEXT 类型或更大。此外,由于这些值在数据库中是加密的,您将无法查询或搜索加密的属性值。
如你所知,Laravel 使用 key 配置值加密字符串,该值在你的应用程序的 app 配置文件中指定。通常,此值对应于 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 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 方法必须返回一个键 / 值对数组,该数组将用于在模型上设置原始的、可存储的值。如果您的值对象只影响单个列,您应该简单地返回可存储的值。
作为一个例子,我们将定义一个自定义的类型转换类,它将多个模型值转换成一个单独的 Address 值对象。我们将假设这个 Address 值对象有两个公共属性:lineOne 和 lineTwo:
<?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();[!NOTE]
如果你计划将包含值对象的 Eloquent 模型序列化为 JSON 或数组,你应该在该值对象上实现Illuminate\Contracts\Support\Arrayable和JsonSerializable接口。
当被转换为值对象的属性被解析时,它们会被 Eloquent 缓存。因此,如果该属性再次被访问,将返回相同的对象实例。
如果您希望禁用自定义类型转换类的对象缓存行为,您可以在您的自定义类型转换类上声明一个公共的 withoutObjectCaching 属性:
class AsAddress implements CastsAttributes
{
public bool $withoutObjectCaching = true;
// ...
}当 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',
];
}如果你想定义如何比较两个给定的类型转换值以确定它们是否已更改,你的自定义类型转换类可以实现 Illuminate\Contracts\Database\Eloquent\ComparesCastableAttributes 接口。这使你能够对 Eloquent 认为已更改并因此在模型更新时保存到数据库的值进行细粒度控制。
此接口规定你的类应包含一个 compare 方法,该方法在给定值被视为相等时应返回 true:
/**
* 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 接口的对象必须定义一个 castUsing 方法,该方法返回自定义类型转换器类的类名,该转换器类负责 Castable 类的双向类型转换:
<?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',
];
}通过结合 "castables" 与 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,
];
}
};
}
}