You need to enable JavaScript to run this app. 如何在Spatie\DataTransferObject的DTO类中对指定属性做类型转换
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spatie\DataTransferObject的DTO类中对指定属性做类型转换

问题根因

你错误的核心是在自定义Caster中主动实例化了空的DTO对象,这完全不符合spatie/data-transfer-object的Caster设计逻辑:

  • Caster接口的cast方法接收的入参就是当前待转换字段的原始值,仅需要针对单个值做转换即可,不需要依赖DTO的其他属性,也不需要关联整个DTO实例
  • 你手动new出来的空DTO实例和Eloquent读取数据时生成的实际DTO实例没有任何关联,自然会出现其他字段全为null的问题
正确实现方案

步骤1:重写自定义Caster逻辑

移除Caster内的DTO实例化代码,直接处理传入的字段值:

use Spatie\DataTransferObject\Caster;
use Carbon\Carbon;

class ProductDateCaster implements Caster
{
    public function cast(mixed $value): string
    {
        // 直接转换原始值即可,此处逻辑和你原有parseDate保持一致即可
        // 示例为原始值是时间戳的场景
        return Carbon::createFromTimestamp((int)$value)->format('d/m/Y');
    }
}

如果你需要复用HasDates trait中的parseDate方法,可以将该方法改为公共静态方法,无需实例化即可调用:

// HasDates trait中调整parseDate为静态方法
trait HasDates
{
    public static function parseDate(int $timestamp): \DateTimeInterface
    {
        // 你的原有日期解析逻辑
    }
}

// Caster内直接调用静态方法
public function cast(mixed $value): string
{
    return \App\DTO\ProductDetails::parseDate((int)$value)->format('d/m/Y');
}

步骤2:修正DTO字段类型声明

你转换后返回的是字符串格式的日期,需要将对应字段的类型声明从int改为string,否则会出现类型错误:

use Spatie\DataTransferObject\CastableDataTransferObject;
use Spatie\DataTransferObject\Attributes\CastWith;

class ProductDetails extends CastableDataTransferObject
{
    use HasDates;

    public int $number;

    #[CastWith(ProductDateCaster::class)]
    public string $start_date;

    #[CastWith(ProductDateCaster::class)]
    public string $end_date;
}

修改完成后,Eloquent读取数据时会自动将details字段转换为ProductDetails DTO实例,两个日期字段也会自动通过自定义Caster转换为d/m/Y格式的字符串,不会出现其他字段为null的问题。

内容的提问来源于stack exchange,提问作者Savvy Osive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 06:36:03