antoninmasek/simple-hydrator

简单的对象水化器

2.0.1 2024-09-04 12:22 UTC

This package is auto-updated.

Last update: 2024-09-04 12:22:49 UTC


README

Latest Version on Packagist Tests Total Downloads

此软件包旨在成为一个极其易于使用的对象水化器。您给它一个数组,它将尝试水化您的数据对象。我在不同的项目中多次重复了这个逻辑,所以我决定将其打包。我非常清楚,有更优越的替代方案,但我的目标是尽可能简单地进行实现。

此外,对我而言,主要目标是解决自己的问题。因此,尽管现在的实现有限,可能包含一些问题,但对我来说,它目前做得很好。如果您也在使用它并发现了一个问题,请随意提交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_namelast_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)。有关更多信息,请参阅 许可证文件