cmath10/mapper

1.0.2 2022-05-08 18:40 UTC

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,则无需显式调用 routeforMember

可用过滤器

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))
;