antoninmasek / simple-hydrator
简单的对象水化器
Requires
- php: ^8.2
- phpunit/phpunit: ^9.5 || ^10.0 || ^11.0
Requires (Dev)
- laravel/pint: ^1.5
- spatie/ray: ^1.32.2
README
此软件包旨在成为一个极其易于使用的对象水化器。您给它一个数组,它将尝试水化您的数据对象。我在不同的项目中多次重复了这个逻辑,所以我决定将其打包。我非常清楚,有更优越的替代方案,但我的目标是尽可能简单地进行实现。
此外,对我而言,主要目标是解决自己的问题。因此,尽管现在的实现有限,可能包含一些问题,但对我来说,它目前做得很好。如果您也在使用它并发现了一个问题,请随意提交PR :)
简而言之
此软件包可以帮助您从数组创建对象,例如
$data = ['name' => 'John', 'age' => 42]; class Human extends \AntoninMasek\SimpleHydrator\DataObject { public string $name; public int $age; } Human::fromArray($data);
安装
您可以通过composer安装此软件包
composer require antoninmasek/simple-hydrator
使用
要水化您的数据对象,您有两种选择
Hydrator::hydrate
您可以使用以下方式使用Hydrator
$human = Hydrator::hydrate(Human::class, $data);
这种方法的优点是,您的数据对象不需要扩展任何东西。缺点是,没有PHP Doc将没有自动完成。
YourDataObject::fromArray
这种方式,您可以扩展您的数据对象,使用DataObject抽象类,这将使您能够直接在数据对象上调用fromArray方法。
$human = Human::fromArray($data);
主要优点是自动完成以及更好的可读性。缺点是,您必须扩展数据对象。至少是父类。嵌套对象不需要扩展任何东西。
不同的键
在正常情况下,该软件包将对象属性名称与输入数组键一一映射。这不一定总是最优的。输入数组可能包含对PHP属性无效的字符,或者您只想分配自己的名称。让我们用一个例子来演示这一点。假设我们想要获取图像尺寸,输入数组具有以下格式
$imageData = [ "ExifImageWidth" => 4032, "ExifImageHeight" => 3024, ]
但是,我们希望我们的DTO看起来像这样
class ImageData { public int $width; public int $height; }
解决这个问题的方法是使用Key属性并指定源数组中的实际键值
use AntoninMasek\SimpleHydrator\Attributes\Key; class ImageData { #[Key('ExifImageWidth')] public int $width; #[Key('ExifImageHeight')] public int $height; }
就是这样!
集合
如果您发现自己处于这样的场景,比如说有一个拥有许多Key对象的Car对象
$car = [ 'brand' => 'Chevrolet', 'type' => 'Camaro', 'keys' => [ [ 'name' => 'main', 'is_active' => true, ], [ 'name' => 'secondary', 'is_active' => false, ], ], ];
并且您需要将每个keys转换为Key对象,您可以在数据对象定义中添加#[Collection(Key::class)]属性,如下所示
use AntoninMasek\SimpleHydrator\Attributes\Collection; class Car { public string $type; public string $brand; public ?ClassThatNeedsCustomCaster $customCaster; #[Collection(Key::class)] public ?array $keys; }
这确保了正确的转换,您最终将得到一个Key对象的数组。
您的根数组是对象列表
如果您的源数组是对象列表,并且您只想转换它,那么您可以使用collectionFromArray而不是fromArray方法
DTO制作
对于您的DTO的每个属性,您可以使用camelCase或snake_case方法来设置它们的值,无论哪种方法更适合您的偏好。在下面的示例中,我们在这里设置了first_name和last_name属性。
$person = Human::make() ->firstName('John') ->lastName('Doe') ->kids(3);
如果您更喜欢保留自动完成,您也可以使用set方法,其中第一个参数是属性名称,第二个参数是值。因此,要复制上面的示例
$person = Human::make() ->set('firstName', 'John') ->set('lastName', 'Doe') ->set('kids', 3);
转换器
对于属性类型不是PHP内置,或者需要比仅按名称填充属性更多的关注的情况,可以编写一个转换器。
转换器类
定义转换器的一种方法是创建一个扩展 AntoninMasek\SimpleHydrator\Casters\Caster 的类。您只需要实现 cast 方法,该方法接收一个包含从输入数组中获取的原始数据的 $value 参数,这些数据将用于填充此类。
以下是一个简单的 DateTime 转换器示例
class DateTimeCaster extends Caster { public function cast(mixed $value): ?DateTime { if (is_null($value)) { return null; } return new DateTime($value); } }
它期望 $value 是一个有效日期格式的字符串。例如 1969-07-20,并返回一个包含此日期的 DateTime 对象。
匿名转换器
如果您不想创建转换器类,可以通过提供一个闭包来创建匿名转换器,而不是转换器类。
Caster::registerCaster(DateTime::class, function ($value) { if (is_null($value)) { return null; } return new DateTime($value); });
注册转换器
您可以通过两种方式注册转换器。首先,指定所有类及其相应转换器之间的映射
Caster::setCasters([ YourObject::class => YourObjectCaster::class, YourSecondObject::class => AnotherCaster::class, ]);
或者一次只指定一个转换器
Caster::registerCaster(YourObject::class, YourObjectCaster::class);
要清除所有转换器,可以使用
Caster::clearCasters();
覆盖默认转换器
如果包中的任何默认转换器都不符合您的需求,您可以轻松地覆盖它。您只需为特定类注册您的转换器即可。已注册的转换器具有更高的优先级,并且如果未提供特定类的映射,则使用包中的默认转换器。
注意
- 请注意,从版本
1.0.1开始,数组键中的任何无效字符都将被忽略。这意味着对于以下数组$data = ['service (Appointments)' => ['2022-06-01']],service (Appointments)键将被设置为serviceAppointments对象属性
有效字符是那些可以传递以下正则表达式的字符:
[^a-zA-Z0-9_]
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志
致谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅 许可证文件