如何在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
相关产品推荐
相关产品推荐

