承接 cmath10/mapper 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

cmath10/mapper

Composer 安装命令:

composer require cmath10/mapper

包简介

Mapping library

README 文档

README

The library provides a utility for converting objects to each other (and arrays to objects) and transferring data between them by reusable map.

Basic example:

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

How it works

The main term of the library is a map. It is an object which is containing information about how to extract values from the source and inject them into a destination object. Values can be processed before injection (to get an opportunity for sanitization and nested mapping).

Map includes:

  • field accessors — objects are responsible for value extraction; by default, it is used symfony/property-access for that purpose, but there are other options, like using callbacks or symfony/expression-language and presets (if the extracted value is null), also there is an interface for custom accessors;
  • field filters — objects are responsible for value processing; by default, there are no filters for extracted value; you can use callbacks and mapping filters for nested mapping.

By default, a map creates a set of accessors based on a class properties list, so a field from a source will be mapped on a field of a destination with the same name. You can change this behavior for specific fields if needed.

All maps must be registered in a mapper instance before using. When you call the mapper method map it seeks the correct map for a given source and destination, then it uses accessors to extract values, filters to process them, and, after that, injects values to a destination. If the destination is a class name, the mapper will try to create an instance of the given class, if the class constructor is public and all possible arguments are optional.

Installation

composer require cmath10/mapper

API

cmath10\Mapper\MapperInterface — interface for mappers:

  • create(string $sourceType, string $destinationType) — creates, registers and returns default map for two classes;
  • register(MapInterface $map) — registers a custom map in the mapper;
  • map($source, $destination) — performs mapping from a source to a destination object or class.

cmath10\mapper\MapInterface — interface for maps:

  • getSourceType() — returns name of source type; used to find the correct map;
  • getDestinationType() — returns name of destination type; used to find the correct map;
  • getFieldAccessors() — returns all fields accessors;
  • getFieldFilters() — returns all fields filters;
  • getOverwriteIfSet() — returns boolean flag; if true, destination member will be overwritten even if its value is not null;
  • getSkipNull() — returns boolean flag; if true, the mapper will not push null source member's value;
  • route(string $destinationMember, string $sourceMember) — sets mapping of destination route to source route; fluent;
  • forMember(string $destinationMember, AccessorInterface $fieldMapper) — sets custom accessor for field; fluent;
  • filter(string $destinationMember, FilterInterface $fieldFilter) — sets a custom filter for value processing; fluent;
  • ignoreMember(string $destinationMember) — excludes members from accounting, so they will not be filled; fluent.

cmath10\mapper\FieldAccessor\AccessorInterface — interface for accessors:

  • getValue($source) — extracts value from a source.

cmath10\mapper\FieldFilter\FitlerInterface — interface for filters:

  • filter($value) — processes the value.

cmath10\Mapper\TypeFilterInterface — interface for type filters is used to process type names. Useful for extracting class names from runtime generated classes like proxies in Doctrine. Used in default mapper which uses an array of type filters as a constructor parameter (to instantiate cmath10\mapper\TypeGuesser which is used to determine the correct map for supplied types in mapper's map method).

Available maps

AbstractMap

Basic class is used to create custom map classes, implements MapperInterface; provides following protected method:

  • setupDefaults — creates a set of accessors based on a class properties list.

Usage example:

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

Simple descendant of AbstractMap, just calls setupDefaults in its constructor; the mapper create method creates, registers and returns instance of this class.

Available accessors

ClosureAccessor

Uses callback to extract a value:

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

Uses symfony/expression-language to extract a value:

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

Does not extract a value, just provides:

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

Uses symfony/property-access to extract a value. This accessor is used by default. Calls like:

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

and

use cmath10\Mapper\Mapper;
use cmath10\Mapper\FieldAccessor\PropertyPathAccessor;

$mapper = new Mapper();
$mapper
    ->create(Fixtures\MagazineWithPrivateProperties::class, Fixtures\MagazineOutput::class)
    ->route('articles', 'someFieldWithArticles')
;

are equivalent. Also, if the fields in a source and destination have the same name, and you use setupDefaults, you don't need to call route or forMember explicitly.

Available filters

ClosureFilter

Uses callback to process value:

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

Replaces the value if it is null:

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

Basic class for filters that can use mapper. Requires class name in the constructor. Used for nested mapping.

ObjectMappingFilter

Descendant of AbstractMappingFilter, uses class name and mapper to make an object from a source member:

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

Descendant of AbstractMappingFilter, uses class name and mapper to make an array of objects from a source member, if the source member's value is array (returns an empty array instead):

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

cmath10/mapper 适用场景与选型建议

cmath10/mapper 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 15 次下载、GitHub Stars 达 0, 最近一次更新时间为 2021 年 04 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「mapper」 「automapper」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 cmath10/mapper 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 cmath10/mapper 我们能提供哪些服务?
定制开发 / 二次开发

基于 cmath10/mapper 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 15
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2021-04-16