cmath10 / mapper
映射库
Requires
- php: >=7.4
- symfony/expression-language: ^4.4 || ^5.0
- symfony/property-access: ^4.4 || ^5.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.18
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-08 23:43:08 UTC
README
该库提供了一种通过可重用映射在对象之间(以及数组到对象)进行转换和传输数据的功能。
基本示例
class ArticleInput { public $title; public $text; public $author; } class Article { public $title; public $text; public $author; public function __construct(?string $title = null) { $this->title = $title; } } $input = new ArticleInput(); $input->title = 'title'; $input->text = 'text'; $article1 = new Article(); $mapper = new cmath10\Mapper\Mapper(); $mapper->create(ArticleInput::class, Article::class); $mapper->map($input, $article1); // ($article1->title === 'title') === true // ($article1->text === 'text') === true $article2 = $mapper->map($input, Article::class) // ($article2 instanceof Article) === true // ($article2->title === 'title') === true // ($article2->text === 'text') === true
工作原理
库的主要术语是映射。它是一个对象,包含有关如何从源中提取值并将其注入目标对象的信息。值在注入之前可以进行处理(以获得净化和嵌套映射的机会)。
映射包括
- 字段访问器——对象负责值提取;默认情况下,使用
symfony/property-access进行此目的,但也有其他选项,例如使用回调或symfony/expression-language和预设(如果提取的值为空),还有一个自定义访问器的接口; - 字段过滤器——对象负责值处理;默认情况下,没有对提取值进行过滤;您可以使用回调和映射过滤器进行嵌套映射。
默认情况下,映射基于类属性列表创建一组访问器,因此源字段将映射到具有相同名称的目标字段。如果需要,您可以更改特定字段的此行为。
在使用之前,所有映射都必须在映射器实例中注册。当您调用映射器方法map时,它将寻找给定源和目标正确的映射,然后使用访问器提取值,过滤器处理它们,然后注入值到目标。如果目标是类名,映射器将尝试创建给定类的实例,如果类构造函数是公开的并且所有可能的参数都是可选的。
安装
composer require cmath10/mapper
API
cmath10\Mapper\MapperInterface——映射器接口
create(string $sourceType, string $destinationType)——为两个类创建、注册并返回默认映射;register(MapInterface $map)——在映射器中注册自定义映射;map($source, $destination)——执行从源到目标对象或类的映射。
cmath10\mapper\MapInterface——映射接口
getSourceType()——返回源类型名称;用于找到正确的映射;getDestinationType()——返回目标类型名称;用于找到正确的映射;getFieldAccessors()——返回所有字段访问器;getFieldFilters()——返回所有字段过滤器;getOverwriteIfSet()——返回布尔标志;如果为true,即使其值为null,也会覆盖目标成员;getSkipNull()——返回布尔标志;如果为true,映射器将不会推送空源成员的值;route(string $destinationMember, string $sourceMember)——设置目标路由到源路由的映射;流畅;forMember(string $destinationMember, AccessorInterface $fieldMapper)——为字段设置自定义访问器;流畅;filter(string $destinationMember, FilterInterface $fieldFilter)——为值处理设置自定义过滤器;流畅;ignoreMember(string $destinationMember)——排除成员,以便它们不会被填充;流畅。
cmath10\mapper\FieldAccessor\AccessorInterface——访问器接口
getValue($source)——从源中提取值。
cmath10\mapper\FieldFilter\FitlerInterface——过滤器接口
filter($value)——处理值。
cmath10\Mapper\TypeFilterInterface — 类型过滤器接口用于处理类型名称。对于从 Doctrine 中运行时生成的类(如代理类)等提取类名非常有用。在默认映射器中使用,该映射器使用类型过滤器数组作为构造参数(用于实例化 cmath10\mapper\TypeGuesser,该类用于确定映射器 map 方法中提供的类型的正确映射)。
可用映射
AbstractMap
基本类用于创建自定义映射类,实现 MapperInterface;提供以下受保护方法
setupDefaults— 根据类属性列表创建一系列访问器。
使用示例
use cmath10\Mapper\AbstractMap; class ArticleOutputMap extends AbstractMap { public function __construct() { $this ->setupDefaults() // create default accessors set ->route('textNotMappedByDefault', 'text') // customize ; } public function getSourceType(): string { return Article::class; } public function getDestinationType(): string { return ArticleOutput::class; } }
DefaultMap
是 AbstractMap 的简单子类,只是在构造函数中调用 setupDefaults;映射器的 create 方法创建、注册并返回此类的实例。
可用访问器
ClosureAccessor
使用回调来提取值
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldAccessor\ClosureAccessor; $mapper = new Mapper(); $mapper ->create(Fixtures\Article::class, Fixtures\ArticleInput::class) ->forMember('author', new ClosureAccessor(fn (Fixtures\ArticleInput $a) => $a->author->name)) ;
ExpressionAccessor
使用 symfony/expression-language 来提取值
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldAccessor\ExpressionAccessor; $mapper = new Mapper(); $mapper ->create(Fixtures\MagazineWithPrivateProperties::class, Fixtures\MagazineOutput::class) ->forMember('articles', new ExpressionAccessor('getArticles()')) ;
PresetAccessor
不提取值,只提供
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldAccessor\PresetAccessor; $mapper = new Mapper(); $mapper ->create(Fixtures\MagazineWithPrivateProperties::class, Fixtures\MagazineOutput::class) ->forMember('articles', new PresetAccessor([])) ;
PropertyPathAccessor
使用 symfony/property-access 来提取值。此访问器是默认使用的。类似以下调用
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldAccessor\PropertyPathAccessor; $mapper = new Mapper(); $mapper ->create(Fixtures\MagazineWithPrivateProperties::class, Fixtures\MagazineOutput::class) ->forMember('articles', new PropertyPathAccessor('someFieldWithArticles')) ;
和
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldAccessor\PropertyPathAccessor; $mapper = new Mapper(); $mapper ->create(Fixtures\MagazineWithPrivateProperties::class, Fixtures\MagazineOutput::class) ->route('articles', 'someFieldWithArticles') ;
是等效的。另外,如果源和目标中的字段具有相同的名称,并且您使用 setupDefaults,则无需显式调用 route 或 forMember。
可用过滤器
ClosureFilter
使用回调来处理值
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldFilter\ClosureFilter; // For ArticleInput title='title' we will get Article title='[[title]]' $mapper = new Mapper(); $mapper ->create(Fixtures\ArticleInput::class, Fixtures\Article::class) ->filter('title', new ClosureFilter(static fn ($title) => '[[' . $title . ']]')) ;
IfNullFilter
如果值为空,则替换值
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldFilter\IfNullFilter; // For ArticleInput title=null we will get Article title='defaultTitle' $mapper = new Mapper(); $mapper ->create(Fixtures\ArticleInput::class, Fixtures\Article::class) ->filter('title', new IfNullFilter('defaultTitle')) ;
AbstractMappingFilter
可用于使用映射器的过滤器的基类。需要在构造函数中指定类名。用于嵌套映射。
ObjectMappingFilter
是 AbstractMappingFilter 的子类,使用类名和映射器从源成员创建对象
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldFilter\ObjectMappingFilter; $mapper = new Mapper(); $mapper->create(Fixtures\AuthorInput::class, Fixtures\Author::class); $mapper ->create(Fixtures\ArticleInput::class, Fixtures\Article::class) ->route('author', 'author') ->filter('author', new ObjectMappingFilter(Fixtures\Author::class)) ;
ObjectArrayMappingFilter
是 AbstractMappingFilter 的子类,使用类名和映射器从源成员创建对象数组,如果源成员的值为数组(如果源成员的值为数组,则返回空数组)
use cmath10\Mapper\Mapper; use cmath10\Mapper\FieldFilter\ObjectArrayMappingFilter; $mapper = new Mapper(); $mapper->create(Fixtures\Article::class, Fixtures\ArticleOutput::class); $mapper ->create(Fixtures\Magazine::class, Fixtures\MagazineOutput::class) ->route('articles', 'articles') ->filter('articles', new ObjectArrayMappingFilter(Fixtures\ArticleOutput::class)) ;