stratadox/hydration-mapping

v4.3 2020-11-22 22:27 UTC

This package is auto-updated.

Last update: 2024-09-24 03:23:32 UTC


README

Build Status codecov Infection Minimum PhpStan Level Scrutinizer Code Quality Maintainability Latest Stable Version License

Implements Latest Stable Version License

用于水解目的的映射。

将数组或类似数组的结构映射到对象属性,以便组装表示业务领域的对象。

安装

使用 composer 安装

composer require stratadox/hydration-mapping

目的

这些映射对象定义了对象属性与数据源之间的关系。

典型用法

通常,水解映射会提供给 MappedHydrator 实例。它们共同组成一个强大的团队,解决单一目的:将数据映射到对象图中。

例如

use Stratadox\Hydration\Mapping\Simple\Type\IntegerValue;
use Stratadox\Hydration\Mapping\Simple\Type\StringValue;
use Stratadox\Hydrator\MappedHydrator;
use Stratadox\Hydrator\ObjectHydrator;

$hydrator = MappedHydrator::using(
    ObjectHydrator::default(),
    StringValue::inProperty('title'),
    IntegerValue::inProperty('rating'),
    StringValue::inPropertyWithDifferentKey('isbn', 'id')
);

$book = new Book;
$hydrator->writeTo($book, [
    'title'  => 'This is a book.',
    'rating' => 3,
    'isbn'   => '0000000001'
]);

更常见的是,将映射水解器提供给 反序列化器

use Stratadox\Deserializer\ObjectDeserializer;
use Stratadox\Hydration\Mapping\Simple\Type\IntegerValue;
use Stratadox\Hydration\Mapping\Simple\Type\StringValue;
use Stratadox\Hydrator\MappedHydrator;
use Stratadox\Hydrator\ObjectHydrator;
use Stratadox\Instantiator\ObjectInstantiator;

$deserialize = ObjectDeserializer::using(
    ObjectInstantiator::forThe(Book::class),
    MappedHydrator::using(
        ObjectHydrator::default(),
        StringValue::inProperty('title'),
        IntegerValue::inProperty('rating'),
        StringValue::inPropertyWithDifferentKey('isbn', 'id')
    )
);

$book = $deserialize->from([
   'title'  => 'This is a book.',
   'rating' => 3,
   'isbn'   => '0000000001'
]);

映射

有三种类型的属性映射可用

  • 标量映射
  • 关系映射
  • 扩展点

标量映射

可以使用 *Value 类映射标量类型属性。以下标量映射可用

  • BooleanValue
  • FloatValue
  • IntegerValue
  • StringValue
  • NullValue

通过命名构造函数创建标量映射

  • inProperty
    • 用法:IntegerValue::inProperty('amount')
    • 当属性名和数据键相同时使用。
  • inPropertyWithDifferentKey
    • 用法:BooleanValue::inPropertyWithDifferentKey('isBlocked', 'is_blocked')
    • 当数据键与属性名不同时使用。

基本验证

当适当的时候,这些映射在生成值之前会验证输入。例如,IntegerValue 映射会检查

  • 输入值格式为整数
  • 值不超出整数范围

可以通过使用 Casted* 映射来跳过此过程。它们在降低完整性的同时提供轻微的速度优势。Casted* 映射如下所示

  • CastedFloat
  • CastedInteger

要跳过整个类型转换过程,可以使用 OriginalValue 映射。

BooleanValue 的输入必须是 0、1 或已经为布尔类型的。可以提供自定义的真/假值作为可选参数

use Stratadox\Hydration\Mapping\Simple\Type\BooleanValue;

$myProperty = BooleanValue::withCustomTruths('foo', ['yes', 'y'], ['no', 'n']);

可空和混合值

可以通过将映射包装在 CanBeNull 中将上述所有映射转换为可空。

例如,而不是 IntegerValue::inProperty('foo'),可以使用以下方式将 foo 属性设置为可空:CanBeNull::or(IntegerValue::inProperty('foo'))

以同样的方式,可以配置混合值类型。要映射可能为整数或浮点数的值(如 PHP 中的数值通常那样),可以使用 CanBeIntegerCanBeInteger::or(FloatValue::inProperty('foo')))。此映射将首先检查值是否可以安全地转换为整数,然后回退到浮点值。非数值值将导致异常,指示输入数据无法映射的原因和位置。

这些混合映射可以组合(如 装饰器 习惯用法),例如,首先尝试将值映射到布尔值,否则映射为整数,如果无法进行类型转换,则映射为浮点值,如果所有其他方法都失败,则将其转换为字符串

use Stratadox\Hydration\Mapping\Simple\Type\CanBeBoolean;
use Stratadox\Hydration\Mapping\Simple\Type\CanBeInteger;
use Stratadox\Hydration\Mapping\Simple\Type\CanBeFloat;
use Stratadox\Hydration\Mapping\Simple\Type\StringValue;

$theProperty = CanBeBoolean::orCustom(
    CanBeInteger::or(
        CanBeFloat::or(
            StringValue::inProperty('bar')
        )
    ), ['TRUE'], ['FALSE']
);

关系映射

可以使用一夫一妻制 HasOne* 或多夫一妻制 HasMany* 映射来映射关系。

这些中的每一个都与输入数据以三种方式之一相连

  • 作为 *嵌入式 值(用于从表格数据加载)
  • 作为 *嵌套 数据结构(用于从JSON结构加载)
  • 作为 *代理(用于懒加载)

这归结为以下可能性

  • HasManyNested
  • HasManyProxies
  • HasOneEmbedded
  • HasOneNested
  • HasOneProxy

关系映射通过命名构造函数创建

  • inProperty
    • 用法:HasOneNested::inProperty('name', $deserializer)
    • 当属性名和数据键相同时使用。
  • inPropertyWithDifferentKey
    • 用法:HasOneNested::inPropertyWithDifferentKey('friends', 'contacts', $deserializer)
    • 当数据键与属性名不同时使用。

在此上下文中,术语 key 指的是从其中映射对象数据的关联数组的键,也称为 offsetindexposition

嵌套与嵌入式

对于 *嵌入式 类,没有 inPropertyWithDifferentKey。它们不是依赖于键中的嵌入式数组,而是从原始输入数组中获取原始输入数组,并从其一个或多个值中组合它们的属性。

单个

HasOne* 类型的关系为每个关系分配一个 Deserializes 相关实例的对象。

HasOneNested 接收在给定 key 的原始输入中找到的值。此值必须是一个数组,可能是一个关联数组。

HasOneEmbedded 映射采取不同的方法:它们从原始输入数组中的数据生成一个新的对象。这种方法在映射例如 嵌入式值 时很有用。

多对多

HasMany* 关系需要一个 Deserializes 集合的对象和一个 Deserializes 项的对象。

这种方法允许在映射集合的方式上有很多自由度。可用的 反序列化器 可以将集合映射为普通数组或自定义集合对象。

这些反序列化器可以进一步使用映射的填充器实例。这种组合
能够映射各种形状和类型的整个对象结构。

代理

代理 用于允许懒加载。与反序列化器不同,它们接受一个工厂来创建对象,这些对象在需要时加载“真实”对象而不是代理。

可以使用 HasOneProxy 映射将懒加载的单个关系映射。懒加载的多对多关系可以选择正常懒加载或额外懒加载。对于额外懒加载关系,使用 HasManyProxies 映射。对于“常规”懒加载关系,它映射为 HasOneProxy,其中“one”指的是一个集合。

后一种方法仅当集合包含在集合对象中时才有效。在对象包含在数组中时应懒加载的情况下,应使用 HasManyProxies 映射,其中每个代理在调用时配置为加载整个数组。

使用此机制,通过任何类型的集合(无论是数组还是集合对象)都支持懒加载和额外懒加载。

双向

可以使用 HasBackReference 映射映射双向 一对多一对一 关系。

此映射充当拥有方填充器的观察者,将“拥有者”对象的引用分配给给定属性。

高级验证

可以使用 ConstrainedMapping 应用高级输入验证。如果 规范 与之满足,则 ConstrainedMapping 将生成映射的值,否则抛出异常。

例如,检查评分是否介于1和5之间可能如下所示

use Stratadox\Hydration\Mapping\Composite\ConstrainedMapping;
use Stratadox\Hydration\Mapping\Simple\Type\IntegerValue;
use Your\Constraint\IsNotLess;
use Your\Constraint\IsNotMore;

ConstrainedMapping::checkThatIt(
    IsNotLess::than(1)->and(IsNotMore::than(5)),
    IntegerValue::inProperty('rating')
);

约束本身实现了(最小化)接口 Satisfiable,该接口只强制使用方法 isSatisfiedBy($input)

实现自定义约束的推荐方式是通过扩展抽象类 Specification

use Stratadox\Specification\Specification;

class IsNotLess extends Specification
{
    private $minimum;

    private function __construct(int $minimum)
    {
        $this->minimum = $minimum;
    }

    public static function than(int $minimum): self
    {
        return new self($minimum);
    }

    public function isSatisfiedBy($number): bool
    {
        return $number >= $this->minimum;
    }
}

或者使用 Specifying 特性

use Stratadox\Specification\Contract\Specifies;
use Stratadox\Specification\Specifying;

class IsNotMore implements Specifies
{
    use Specifying;

    private $maximum;

    private function __construct(int $maximum)
    {
        $this->maximum = $maximum;
    }

    public static function than(int $maximum): self
    {
        return new self($maximum);
    }

    public function isSatisfiedBy($number): bool
    {
        return $number <= $this->maximum;
    }
}

默认值

为了遵循PHP的精神,有一个类可以加载默认值而不是传播异常: Defaults::to(-1, IntegerValue::inProperty('foo'))

扩展

ClosureMapping 提供了一个易于扩展的点。它接受一个匿名函数作为构造函数参数。这个函数使用输入数据被调用以生成映射结果。

为了获得额外的扩展能力,可以通过实现 Mapping 接口来生成自定义映射。