跳转到主要内容

什么是 Cast

Eloquent 的 Cast 是将从数据库取出的原始值转换为 PHP 数据类型的机制,保存时会反向转换回去。在 casts 方法中定义。
protected function casts(): array
{
    return [
        'is_admin'   => 'boolean',
        'settings'   => 'array',
        'created_at' => 'datetime',
    ];
}

内置 Cast 一览

Laravel 标准提供的 Cast 列表如下。
Cast说明
integer / int转换为整数
float / double转换为浮点数
string转换为字符串
boolean / bool转换为布尔值(包括 0/1
arrayJSON 字符串 ↔ PHP 数组
collectionJSON 字符串 ↔ Collection 实例
objectJSON 字符串 ↔ stdObject 实例
datetime字符串 ↔ Carbon 实例
immutable_datetime字符串 ↔ CarbonImmutable 实例
date字符串 ↔ Carbon(无时间)
timestamp字符串 ↔ UNIX 时间戳
encrypted保存时加密,取出时解密
hashed保存时哈希(与只读 Cast 组合使用)
AsStringable::class字符串 ↔ Stringable 对象
AsArrayObject::classJSON ↔ ArrayObject 实例
AsCollection::classJSON ↔ Collection 实例
AsArrayObjectAsCollection 为了让你能直接修改数组的特定偏移,在 Laravel 内部实际是以自定义 Cast 实现的。

创建自定义 Cast 类

当内置 Cast 无法满足转换需求时,可以通过实现 CastsAttributes 接口创建自定义 Cast。

接口定义

框架本体的契约定义如下。
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.php

interface CastsAttributes
{
    /**
     * 将 DB 的原始值转换为 PHP 值(读取时)
     *
     * @param  array<string, mixed>  $attributes  模型的全部属性值
     */
    public function get(Model $model, string $key, mixed $value, array $attributes);

    /**
     * 将 PHP 值转换为可存入 DB 的形式(写入时)
     *
     * @param  array<string, mixed>  $attributes  模型的全部属性值
     */
    public function set(Model $model, string $key, mixed $value, array $attributes);
}
$attributes 参数中包含模型的全部属性,因此也可以做跨多列的转换(参见后文的 Value Object 模式)。

基础自定义 Cast 实现

使用 make:cast 命令生成模板。
php artisan make:cast AsMoney
将生成 app/Casts/AsMoney.php。举例来说,我们实现一个将金额(以整数存储)转换为 Money Value Object 的 Cast。
<?php

namespace App\Casts;

use App\ValueObjects\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class AsMoney implements CastsAttributes
{
    /**
     * 将 DB 的整数(以最小货币单位)转换为 Money 对象
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Money {
        return new Money((int) $value);
    }

    /**
     * 将 Money 对象转换为可存入 DB 的整数
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): int {
        if ($value instanceof Money) {
            return $value->amount;
        }

        return (int) $value;
    }
}
在模型上应用该 Cast。
<?php

namespace App\Models;

use App\Casts\AsMoney;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected function casts(): array
    {
        return [
            'price' => AsMoney::class,
        ];
    }
}
这样 $order->price 就会返回 Money 实例。

Value Object Cast

将多个 DB 列打包为一个 Value Object 的模式。

实现示例:地址 Cast

address_line_oneaddress_line_two 两列合并为 Address Value Object。
<?php

namespace App\ValueObjects;

use Illuminate\Contracts\Support\Arrayable;

class Address implements Arrayable, \JsonSerializable
{
    public function __construct(
        public readonly string $lineOne,
        public readonly string $lineTwo,
    ) {}

    public function toArray(): array
    {
        return [
            'line_one' => $this->lineOne,
            'line_two' => $this->lineTwo,
        ];
    }

    public function jsonSerialize(): array
    {
        return $this->toArray();
    }
}
<?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
{
    /**
     * 从多列组装出 Address 对象
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Address {
        return new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        );
    }

    /**
     * 将 Address 对象拆分为按列的数组返回
     *
     * @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,
        ];
    }
}
set 方法返回数组时,Eloquent 会以键作为列名,将值保存到相应的列。单列 Cast 时则返回字符串或整数。
在模型中应用并使用如下:
<?php

namespace App\Models;

use App\Casts\AsAddress;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected function casts(): array
    {
        return [
            'address' => AsAddress::class,
        ];
    }
}
$user = User::find(1);

// 以 Address 对象形式取出
echo $user->address->lineOne;

// 修改 Value Object 后,保存时会自动写入 DB
$user->address = new Address('123 Main St', 'Apt 4B');
$user->save();

Value Object 的缓存

被转换为 Value Object 的属性值会被 Eloquent 缓存。对同一属性访问两次会返回同一对象实例。 若要禁用缓存,在 Cast 类中添加 $withoutObjectCaching 属性。
class AsAddress implements CastsAttributes
{
    public bool $withoutObjectCaching = true;

    // ...
}

入站 Cast(仅写入)

只在写入 DB 时进行转换,读取时不做转换的 Cast。实现 CastsInboundAttributes 接口即可。 典型用途是哈希。仅在保存密码或密钥时转换,读取时直接返回哈希值。
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
{
    public function __construct(
        protected string|null $algorithm = null,
    ) {}

    /**
     * 保存时进行哈希
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): string {
        return is_null($this->algorithm)
            ? bcrypt($value)
            : hash($this->algorithm, $value);
    }
}

Cast 参数

若要向自定义 Cast 传入参数,在类名后用冒号分隔指定,多参数用逗号分隔。
protected function casts(): array
{
    return [
        'secret' => AsHash::class.':sha256',
        'data'   => AsHash::class.':sha512',
    ];
}
参数会传递给 Cast 类的构造函数。
class AsHash implements CastsInboundAttributes
{
    public function __construct(
        protected string|null $algorithm = null, // 会传入 'sha256'
    ) {}
}

Castables:让 Value Object 持有 Cast 逻辑

实现 Castable 接口的 Value Object 拥有返回其 Cast 类的 castUsing 方法。这样模型侧无需知道具体的 Cast 类,领域逻辑更加整洁。
<?php

namespace App\ValueObjects;

use App\Casts\AsAddress;
use Illuminate\Contracts\Database\Eloquent\Castable;

class Address implements Castable
{
    /**
     * 返回用于该对象 Cast 的类
     *
     * @param  array<string, mixed>  $arguments
     */
    public static function castUsing(array $arguments): string
    {
        return AsAddress::class;
    }
}
模型侧改为指定 Value Object 类,而不是 Cast 类。
protected function casts(): array
{
    return [
        'address' => Address::class,
    ];
}
Castable 与匿名类结合,可以把 Value Object 与 Cast 逻辑写在同一个文件里。
class Address implements Castable
{
    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,
                ];
            }
        };
    }
}

$appends$hidden 的交互

Cast 与 $appends$hidden 是相互独立的机制,但组合使用时需要注意。
class User extends Model
{
    protected function casts(): array
    {
        return [
            'address' => AsAddress::class,
        ];
    }

    // 在 toArray() / toJson() 时排除 address
    protected $hidden = ['address_line_one', 'address_line_two'];

    // 将 Cast 后的 address 加入序列化结果
    protected $appends = ['address'];
}
$hidden 中要指定的是 DB 列名。请填写原列名(address_line_one, address_line_two),而不是通过 Cast 生成的属性名(address)。

运行时追加 Cast

当只想为特定查询或请求追加 Cast 时,使用 mergeCasts 方法。
$user = User::find(1);

$user->mergeCasts([
    'extra_data' => 'array',
]);

下一步

Eloquent Observer 与模型事件

学习如何在模型的保存、删除等生命周期事件上挂钩以追加处理。
最后修改于 2026年7月13日