sav/hydrator-creator

通过类构造函数进行对象注水

0.2.1 2023-08-27 06:02 UTC

This package is auto-updated.

Last update: 2024-09-27 08:25:22 UTC


README

Code Coverage type-coverage psalm-level

该库旨在通过类构造函数进行对象注水。此外,借助可扩展属性,您还可以添加自己的数据提取器、修改器和验证器。该属性可以应用于类或参数。

安装

可以通过composer安装此包

composer require sav/hydrator-creator

注意:当前版本的这个包仅支持PHP 8.1以上。

用法

use Sav\Hydrator\Hydrator;

final class User
{
    public readonly string $email;

    public function __construct(
        string $email,
        public readonly string $name
    ) {
        $this->email = strtolower($email);
    }
}

$data = [
    'email' => 'Test@mail.com',
    'name' => 'Sam',
];

$user = Hydrator::init()->hydrate(User::class, $data);
var_dump($user);

结果

object(User) {
  ["email"]=> string(13) "test@mail.com"
  ["name"]=> string(3) "Sam"
}

如果属性不是标量类型,而是允许其他对象的类,它也会自动转换。如果一个类在构造函数中使用了参数,它将自动传递给构造函数,无需指定确切名称。这允许将数据转换为简单的ValueObjects。

use Sav\Hydrator\Hydrator;

final class Item
{
    public function __construct(
        public readonly int $id,
        public readonly string $name,
    ) {
    }
}

final class PriceValueObject
{
    public function __construct(
        private readonly float $value,
    ) {
    }

    public function getValue(): float
    {
        return $this->value;
    }
}

final class OrderItem
{
    public function __construct(
        public readonly Item $product,
        public readonly PriceValueObject $cost,
    ) {
    }
}

$data = [
    'product' => ['id' => 1, 'name' => 'phone'],
    'cost' => 10012.23,
];

$orderItem = Hydrator::init()->hydrate(OrderItem::class, $data);
var_dump($orderItem);

结果

object(OrderItem) {
  ["product"]=> object(Item) {
    ["id"]=> int(1)
    ["name"]=> string(5) "phone"
  }
  ["cost"]=> object(PriceValueObject) {
    ["value":"PriceValueObject":private]=> float(10012.23)
  }
}

对象数组

如果您有一组特定类的对象数组,那么您必须指定ArrayOfObjects属性,将其传递给您需要的类以获取元素。

示例

use Sav\Hydrator\Hydrator;
use Sav\Hydrator\Attribute\ArrayMap\ArrayOfObjects;

class Product
{
    public function __construct(
        public readonly int $id,
        public readonly DateTimeImmutable $dateCreate,
    ) {
    }
}

class Products
{
    public function __construct(
        #[ArrayOfObjects(Product::class)]
        public readonly array $products
    ) {
    }
}

$data = [
    'products' => [
        ['id' => 1, 'dateCreate' => '2023-01-01'],
        ['id' => 2, 'dateCreate' => '2023-01-02'],
    ],
];
$products = Hydrator::init()->hydrate(Products::class, $data);
var_dump($products);

结果

object(Products) {
  ["products"]=> array(2) {
    [0]=> object(Product) {
      ["id"]=> int(1)
      ["dateCreate"]=> object(DateTimeImmutable) {
        ["date"]=> string(26) "2023-01-01 00:00:00.000000"
        ["timezone_type"]=> int(0)
        ["timezone"]=> string(3) "UTC"
      }
    }
    [1]=>object(Product) {
      ["id"]=> int(2)
      ["dateCreate"]=> object(DateTimeImmutable) {
        ["date"]=> string(26) "2023-01-02 00:00:00.000000"
        ["timezone_type"]=> int(0)
        ["timezone"]=> string(3) "UTC"
      }
    }
  }
}

字段别名

可以为属性设置各种可能的别名,这些别名也将搜索数据源。

use Sav\Hydrator\Hydrator;
use Sav\Hydrator\Attribute\ValueExtractor\Alias;

class User
{
    public function __construct(
        private readonly int $id,
        #[Alias('personalPhone')]
        private readonly string $phone,
    ) {
    }
}

默认值

使用DefaultValue属性,您可以设置参数的默认值。您也可以使用标准的PHP语法设置默认值。

use Sav\Hydrator\Hydrator;
use Sav\Hydrator\Attribute\ValueModifier\DefaultValue;

class User
{
    public function __construct(
        public readonly int $id,
        #[DefaultValue(new DateTimeImmutable('2023-01-03'))]
        public readonly DateTimeImmutable $dateCreate,
        #[DefaultValue(100, applyForEmpty: true)]
        public readonly int $balance,
        public readonly int $limit = 30,
    ) {
    }
}

$user = Hydrator::init()->hydrate(User::class, ['id' => 1, 'balance' => 0]);
var_dump($user);

结果

object(User) {
  ["id"]=> int(1)
  ["dateCreate"]=> object(DateTimeImmutable) {
    ["date"]=> string(26) "2023-01-03 00:00:00.000000"
    ["timezone_type"]=> int(0)
    ["timezone"]=> string(3) "UTC"
  }
  ["balance"]=> int(100)
  ["limit"]=> int(30)
}

参数的必填值

该属性指定输入数据中是否需要参数值。该属性可以应用于类或参数。

use Sav\Hydrator\Hydrator;
use Sav\Hydrator\Attribute\RequiredKeyValue;

#[RequiredKeyValue]
class User
{
    public function __construct(
        public readonly ?int $id,
        public readonly int $balance = 3,
    ) {
    }
}

try {
    $user = Hydrator::init()->hydrate(User::class, ['id' => 1]);
} catch(HydratorException $e) {
    echo $e->getMessage();
}

结果

balance: Value not exist.

非空验证器

NotEmpty属性允许检查参数的完整性。它只检查参数是否被分配了值,并且不是null。如果检查失败,将抛出异常。

use Sav\Hydrator\Hydrator;
use Sav\Hydrator\Attribute\ValueValidator\NotEmpty;

class User
{
    public function __construct(
        public readonly int $id,
        #[NotEmpty]
        public readonly int $balance,
    ) {
    }
}

try {
    $user = Hydrator::init()->hydrate(User::class, ['id' => 1, 'balance' => 0]);
} catch(HydratorException $e) {
    echo $e->getMessage();
}

结果

balance: Value is empty.

自定义属性

要扩展功能,您可以编写自己的属性,这些属性根据其目的实现特定的接口。

  • Sav\Hydrator\Attribute\ValueExtractor 用于更改在输入数据中搜索参数值的方式。别名属性是此类属性的示例。

  • \Sav\Hydrator\Attribute\ValueModifier 用于转换找到的值。DefaultValue属性可以是此类属性的一个例子。

  • \Sav\Hydrator\Attribute\ValueValidator 用于检查提取和修改后的数据。NotEmpty属性是验证空值的一个示例。

  • \Sav\Hydrator\Attribute\ArrayMap 用于定义具有特定键的值的解释类型。ArrayOfObjects属性是一个示例,它将数组中的每个元素解释为指定类的值。