stratadox / hydration-mapping
Requires
- php: >=7.2
- ext-json: *
- stratadox/deserializer-contracts: ^0.3
- stratadox/hydration-mapping-contracts: ^0.4
- stratadox/hydrator-contracts: ^0.8
- stratadox/proxy-contracts: ^0.3.0
- stratadox/specification-interfaces: ^1.0
Requires (Dev)
- phpstan/phpstan: ^0.12
- phpunit/phpunit: ^7.1
- stratadox/deserializer: ^0.2.1
- stratadox/proxy: ^0.4
- stratadox/specification: ^1.0
Suggests
- stratadox/hydrator: For hydrating the objects
- stratadox/proxy: For lazy loading relationships
- stratadox/specification: For advanced input validation
README
用于水解目的的映射。
将数组或类似数组的结构映射到对象属性,以便组装表示业务领域的对象。
安装
使用 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 类映射标量类型属性。以下标量映射可用
BooleanValueFloatValueIntegerValueStringValueNullValue
通过命名构造函数创建标量映射
inProperty- 用法:
IntegerValue::inProperty('amount') - 当属性名和数据键相同时使用。
- 用法:
inPropertyWithDifferentKey- 用法:
BooleanValue::inPropertyWithDifferentKey('isBlocked', 'is_blocked') - 当数据键与属性名不同时使用。
- 用法:
基本验证
当适当的时候,这些映射在生成值之前会验证输入。例如,IntegerValue 映射会检查
- 输入值格式为整数
- 值不超出整数范围
可以通过使用 Casted* 映射来跳过此过程。它们在降低完整性的同时提供轻微的速度优势。Casted* 映射如下所示
CastedFloatCastedInteger
要跳过整个类型转换过程,可以使用 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 中的数值通常那样),可以使用 CanBeInteger:CanBeInteger::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结构加载) - 作为
*代理(用于懒加载)
这归结为以下可能性
HasManyNestedHasManyProxiesHasOneEmbeddedHasOneNestedHasOneProxy
关系映射通过命名构造函数创建
inProperty- 用法:
HasOneNested::inProperty('name', $deserializer) - 当属性名和数据键相同时使用。
- 用法:
inPropertyWithDifferentKey- 用法:
HasOneNested::inPropertyWithDifferentKey('friends', 'contacts', $deserializer) - 当数据键与属性名不同时使用。
- 用法:
在此上下文中,术语 key 指的是从其中映射对象数据的关联数组的键,也称为 offset、index 或 position。
嵌套与嵌入式
对于 *嵌入式 类,没有 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 接口来生成自定义映射。