morebec/domain-normalizer

此包已被弃用,不再维护。未建议替代包。

域对象规范器

0.4.0 2020-06-05 16:08 UTC

This package is auto-updated.

Last update: 2021-05-06 15:38:38 UTC


README

DomainNormalizer 是一个 Orkestra 组件,用于通过流畅的接口轻松规范域对象。它具有将序列化/持久化关注点从域类中移出的优点,同时提供了一种快速简便的方式来定义域类的原始映射信息。

它包含序列化和反序列化(通过规范化)的功能。

规范化

规范化是将域对象实例转换为原始值(int、string、float、bool、数组)数组表示的过程。这种数组表示称为规范化形式。

给定如下类结构

class Order
{
    /** @var string */
    private $id;

    /** @var int */
    private $createdAt;

    /** @var array<OrderLineItem> */
    private $lineItems;
}


class OrderLineItem
{
    /**
     * @var string
     */
    private $productId;
    /**
     * @var int
     */
    private $quantity;

    public function __construct(ProductId $productId, int $quantity)
    {
        $this->productId = $productId;
        $this->quantity = $quantity;
    }
}

class ProductId
{
    /**
     * @var string
     */
    private $id;

    public function __construct(string $id)
    {
        $this->id = $id;
    }

    public function __toString()
    {
        return $this->id;
    }
}

使用以下定义

use DateTime;
use Morebec\DomainNormalizer\Normalization\Configuration\NormalizerConfiguration;
use Morebec\DomainNormalizer\Normalization\Configuration\ObjectNormalizationDefinitionFactory as DefinitionFactory;
use Morebec\DomainNormalizer\Normalization\Configuration\ObjectNormalizationDefinition as Definition;
use Morebec\DomainNormalizer\Normalization\Normalizer;
use Morebec\DomainNormalizer\Normalization\NormalizationContext;

$config = new NormalizerConfiguration();


$config->registerDefinition(DefinitionFactory::forClass(
    Order::class,
    static function (Definition $d) {
        $d->property('id')
            ->renamedTo('ID')
            ->asString();

        $d->property('createdAt')->as(static function (NormalizationContext $context) {
            $value = $context->getValue();
            return (new DateTime("@$value"))->format('Y-m-d');
        });

        $d->property('lineItems')
            ->asArrayOfTransformed(OrderLineItem::class);

        $d->createProperty('nbLineItems')
            ->as(static function(NormalizationContext $context) {
                return count($context->getObject()->getLineItems());
            }
        );
    })
);


$config->registerDefinition(DefinitionFactory::forClass(
    OrderLineItem::class,
    static function (Definition $d) {
        $d->property('quantity');
        $d->property('productId')->asString();
    })
);

$normalizer = new Normalizer($config);
$obj = new Order();
$data = $normalizer->normalize($obj);

将返回以下规范化形式

[
  "id" => "id5e5716cf048284.16614551",
  "createdAt" => "2020-02-27",
  "lineItems" => [
    0 => [
      "quantity" => 5,
      "productId" => "5e5716cf0485b7.29093456",
    ],
    1 => [
      "quantity" => 5,
      "productId" => "5e5716cf048606.07838602"
    ]
  ]
];

也可以在类内部包含您的定义

use Morebec\DomainNormalizer\Normalization\Configuration\ObjectNormalizationDefinition;

class OrderDefinition extends ObjectNormalizationDefinition 
{
    public function __construct() 
    {
        parent::__construct(Order::class);
        
        $this->property('id')
            ->renamedTo('ID')
            ->asString();

        $this->property('createdAt')->as(static function (TransformationContext $context) {
            $value = $context->getValue();
            return (new DateTime("@$value"))->format('Y-m-d');
        });

        $this->property('lineItems')
            ->asArrayOfTransformed(OrderLineItem::class);

        $this->createProperty('nbLineItems')
            ->as(static function(TransformationContext $context) {
                return count($context->getObject()->getLineItems());
            }
        );
    }
}

规则很简单

  • 如果规范器无法找到给定对象的定义
    • 它将在其注册表中查找是否有可用的定义,用于对象的父类。
    • 否则:将抛出异常
  • 定义遵循显式声明方法
    • 如果实例上存在属性但不属于定义的一部分,则不会进行规范化。将被忽略。
    • 如果实例上不存在属性但属于定义的一部分
      • 除非它是“绑定”属性,否则规范器将抛出异常。
      • 在绑定属性的情况下,它们将被添加到结果规范化形式中。(这可以通过使用定义工厂时的 bound 定义或通过 createProperty 来定义)

反规范化

反规范化的过程是规范化的逆过程:将规范化形式转换为域对象实例。

在反规范化定义中,我们不是定义属性,而是定义键。

规则与规范化类似,但预设了一些细微的差异

  • 定义也遵循显式声明方法
    • 如果键存在于规范化形式中但不属于定义的一部分,则不会进行反规范化。
    • 每个嵌套的规范化形式都必须有一个相关的定义。
      • 如果没有找到,反规范器将抛出异常。
    • 如果存在键定义但关联的数据不存在
      • 将应用配置的缺失转换器,这通常是要么抛出错误,要么提供默认值。
        • 默认情况下是抛出错误
    • 如果存在键定义,但类上没有相应的属性
      • 抛出异常。
class OrderDenormalizationDefinition extends ObjectDenormalizationDefinition
{
    public function __construct()
    {
        parent::_construct(Order::class);
        $this->key('ID')
             ->renamedTo('id')
             ->as(static function (TransformationContext $context) {
                return new ProductId($context->getValue());  
            }
        );
        
        $this->key('createdAt')->as(static function (TransformationContext $context) {
            return strtotime($context->getValue());
        });
        $this->key('newKey')->defaultsTo('test');

        $this->key('lineItems')
            ->asArrayOfTransformed(OrderLineItem::class);

    }
}

自动反/规范化

尽管我们推荐使用显式定义,但也可以使用自动规范化和反规范化。
这在与简单的 DTO 进行规范化和反规范化处理时很有用。

new AutomaticNormalizationDefinition(Order::class);

new AutomaticDenormalizationDefinition(Order::class);