cascademedia/cartographer

一个简单且可配置的库,允许在数组之间进行映射。

v0.1.0 2015-09-25 16:50 UTC

This package is not auto-updated.

Last update: 2024-10-02 10:22:25 UTC


README

Cartographer 是一个 PHP 库,提供了将数据从一种类型转换到另一种类型的能力。

Build Status Code Coverage Scrutinizer Code Quality

安装

要安装 Cartographer,只需执行以下 composer 命令来引入库。

$ composer require cascademedia/cartographer @stable

或者,您可以克隆/下载此存储库并手动安装包。

基本用法

库有三个基本部分:映射器、映射和上下文。

  • 上下文对象用于包含单个映射用例的规则,例如:将用户输入映射到数据对象或将实体映射到视图模型。
  • 映射对象实际上是一个规则,用于确定在给定上下文中如何将数据从字段映射到另一个字段。
  • 最后,映射器将所有这些整合在一起,并确保从源数据到目标数据的正确映射得到执行。

不再介绍,以下是一个简单的示例,它将输入数组映射到 User 类。有关更高级的用法,请查阅文档。

class User
{
    public $firstName;

    public $lastName;
}

class MyContext implements ContextInterface
{
    public function getMap()
    {
        return (new MapBuilder())
            ->from(MapBuilder::REF_ARRAY)
            ->to(MapBuilder::REF_CLASS_PROPERTIES)
            ->add('firstName', 'first_name')
            ->add('lastName', 'last_name')
            ->getMap()
        ;
    }
}

$source = [
    'first_name' => 'Test First',
    'last_name' => 'Test Last'
];

$destination = new User();

$mapper = new Mapper();
$context = new MyContext();

$destination = $mapper->map($destination, $source, $context);
var_dump($destination);
/*
class User#2 (2) {
  public $firstName =>
  string(10) "Test First"
  public $lastName =>
  string(9) "Test Last"
}
*/

上下文

上下文是包含用于单个 Mapper::map() 操作的所有规则的类。在使用映射器时,必须在上下文中包含映射规则。

要创建上下文,创建一个实现 ContextInterface 的类,并使用 getMap() 返回包含所有所需映射规则的映射。

class MyContext implements ContextInterface
{
    public function getMap()
    {
        return (new MapBuilder())
            ->from(MapBuilder::REF_ARRAY)
            ->to(MapBuilder::REF_ARRAY)
            ->add('first_name', 'fname')
            ->add('last_name', 'lname')
            ->getMap()
        ;
    }
}

$source = [
    'fname' => 'Test First',
    'lname' => 'Test Last'
];

$mapper = new Mapper();

$result = $mapper->map([], $source, new MyContext());
var_dump($result);
/*
array(2) {
  'first_name' =>
  string(10) "Test First"
  'last_name' =>
  string(9) "Test Last"
}
*/

参考

参考是映射器用于从对象或数组中检索或存储字段数据的类。映射器目前支持数组、对象修改器和对象属性引用。

除非您正在手动创建字段映射,否则通常不会直接使用参考类。

数组引用

ArrayReference 类告诉映射器您希望访问数组的顶层数据。

getValue() 方法允许用户从任何给定的数组中检索数据。

$reference = new ArrayReference('first_name');

$data = [
    'first_name' => 'Test First',
    'last_name' => 'Test Last'
];

var_dump($reference->getValue($data));
// string(10) "Test First"

setValue() 方法允许用户将数据放入任何给定的数组。请注意,此方法返回修改后的数组的一个副本,并且不会修改传入的原始数组。

var_dump($reference->setValue($data, 'Another Test First'));
/*
array(2) {
  'first_name' =>
  string(18) "Another Test First"
  'last_name' =>
  string(9) "Test Last"
}
*/

修改器引用

MutatorReference 类告诉映射器您希望访问类方法调用返回的数据。默认情况下,此引用将尝试使用命名字段的获取器和设置器。例如,引用名为 test 的字段将分别调用 getTest()setTest(),但可以根据需要配置调用其他方法。

请注意,只能访问公共方法。访问私有和受保护的方法将抛出 ReflectionException

getValue() 方法将调用为给定对象配置的获取器方法并返回其结果。

class User
{
    private $firstName;

    private $lastName;

    public function getFirstName()
    {
        return $this->firstName;
    }

    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;
        return $this;
    }

    public function getLastName()
    {
        return $this->lastName;
    }

    public function setLastName($lastName)
    {
        $this->lastName = $lastName;
        return $this;
    }
}

$reference = new MutatorReference('first_name');

$user = (new User())
    ->setFirstName('Test First')
    ->setLastName('Test Last')
;

var_dump($reference->getValue($user));
// string(10) "Test First"

setValue() 方法将调用为给定对象配置的设置器方法。

var_dump($reference->setValue($user, 'Another Test First'));
/*
class User#3 (2) {
  private $firstName =>
  string(18) "Another Test First"
  private $lastName =>
  string(9) "Test Last"
}
*/

如果默认的获取器和设置器不满意,您可以通过引用的构造函数更改要调用的方法。

class User
{
    private $firstName;

    public function retrieveFirstName()
    {
        return $this->firstName;
    }

    public function addFirstName($firstName)
    {
        $this->firstName = $firstName;
        return $this;
    }
}

$reference = new MutatorReference('first_name', 'retrieveFirstName', 'addFirstName');

$user = (new User())
    ->addFirstName('Test First')
;

// calls $user->addFirstName('Changed First')
$reference->setValue($user, 'Changed First');

// calls $user->retrieveFirstName()
$value = $reference->getValue($user);

var_dump($value);
// string(13) "Changed First"

属性引用

PropertyReference 类告诉映射器您希望访问类公共属性中的数据。请注意,只能访问公共属性。访问私有和受保护属性将抛出 ReflectionException

getValue() 方法将返回引用对象属性中的数据。

class User
{
    public $firstName;

    public $lastName;
}

$reference = new PropertyReference('firstName');

$user = new User();
$user->firstName = 'Test First';
$user->lastName = 'Test Last';

var_dump($reference->getValue($user));
// string(10) "Test First"

setValue() 方法将数据放入引用对象属性中。

var_dump($reference->setValue($user, 'Another Test First'));
/*
class User#3 (2) {
  public $firstName =>
  string(18) "Another Test First"
  public $lastName =>
  string(9) "Test Last"
}
*/

映射

映射类是映射库的工作马。它们是使用引用来映射单个数据片段的类,以确定数据来源和去向。

映射

Mapping 类是最直接的映射。它只需将源数据直接映射到配置的目的地。当构建 Mapping 类时,目的地引用在前,源引用在后。

$source = [
    'fname' => 'First',
    'lname' => 'Last'
];

$destination = [];

$mapping = new Mapping(new ArrayReference('first_name'), new ArrayReference('fname'));

$destination = $mapping->map($destination, $source);
var_dump($destination);
/*
array(1) {
  'first_name' =>
  string(5) "First"
}
*/

嵌入式映射

EmbeddedMapping 类允许将嵌入式数据结构映射到目的地。当构建 EmbeddedMapping 类时,源字段在前,随后是一个描述嵌入式结构如何映射到目的地的映射。

$source = [
    'name' => [
        'fname' => 'First',
        'lname' => 'Last'
    ]
];

$destination = [];

$mapping = new EmbeddedMapping(new ArrayReference('name'), (new MapBuilder())
    ->from(MapBuilder::REF_ARRAY)
    ->to(MapBuilder::REF_ARRAY)
    ->add('first_name', 'fname')
    ->add('last_name', 'lname')
    ->getMap()
);

$destination = $mapping->map($destination, $source);
var_dump($destination);
/*
array(2) {
  'first_name' =>
  string(5) "First"
  'last_name' =>
  string(4) "Last"
}
*/

解析器映射

ResolverMapping 类允许使用 值解析器 将数据映射到目的地。

class FullNameResolver implements ValueResolverInterface
{
    public function resolve($source, $destination)
    {
        return $source['fname'] . ' ' . $source['lname'];
    }
}

$source = [
    'fname' => 'First',
    'lname' => 'Last'
];

$destination = [];

$mapping = new ResolverMapping(new ArrayReference('name'), new FullNameResolver());

$destination = $mapping->map($destination, $source);
var_dump($destination);
/*
array(1) {
  'name' =>
  string(10) "First Last"
}
*/

值解析器

值解析器是一个对象,它将从整个源对象中获取一个单独的值。使用值解析器允许在映射值时使用任意逻辑。

class FullNameResolver implements ValueResolverInterface
{
    public function resolve($source, $destination)
    {
        $firstName = $destination['fname'];
    
        if (array_key_exists('fname', $source)) {
            $firstName = $source['fname'];
        }
    
        return $firstName . ' ' . $source['lname'];
    }
}

$destination = [
    'fname' => '1First'
];

$source = [
    'lname' => 'Last'
];

$resolver = new FullNameResolver();
$result = $resolver->resolve($source, $destination);
var_dump($result);
// string(11) "1First Last"

可调用值解析器

库自带灵活的 CallableValueResolver 类,允许在不需要定义解析器类的情况下定义值解析器。

$destination = [
    'fname' => '1First'
];

$source = [
    'lname' => 'Last'
];

$resolver = new CallableResolver(function ($source, $destination) {
    $firstName = $destination['fname'];

    if (array_key_exists('fname', $source)) {
        $firstName = $source['fname'];
    }

    return $firstName . ' ' . $source['lname'];
});

$result = $resolver->resolve($source, $destination);
var_dump($result);
// string(11) "1First Last"

映射构建器

为了简化在上下文中创建映射,库自带了一个 MapBuilder 类,它提供了一个简单、流畅的接口来构建映射。设计 MapBuilder 的目的是解决大多数使用案例,以便用户很少需要手动创建映射。

定义默认引用类型

MapBuilder 类允许您使用 from()to() 方法指定默认的 引用类型。这些方法分别接受 MapBuilder 常量 REF_ARRAYREF_CLASS_PROPERTIESREF_CLASS_MUTATORS。如果使用了除上述常量之外的其他值,则抛出 InvalidReferenceTypeException 异常。

如果未调用 from()to() 方法,则假设两个都是 REF_ARRAY 引用类型。

(new MapBuilder())
    ->from(MapBuilder::REF_ARRAY)
    ->to(Mapbuilder::REF_CLASS_MUTATORS)
;

添加映射

MapBuilder 类上有方法允许添加预构建的映射类型,以及添加自定义映射的能力。

add() 方法简单地使用确定的引用类型创建一个新的 映射

(new MapBuilder())
    ->add('first_name', 'fname')
;

// add() creates a mapping equivalent to:

new Mapping(new ArrayReference('first_name'), new ArrayReference('fname'))

addEmbedded() 方法使用确定的引用类型创建一个 嵌入式映射

(new MapBuilder())
    ->addEmbedded('name', (new MapBuilder())
        ->add('first_name', 'fname')
        ->add('last_name', 'lname')
        ->getMap()
    )
;

// addEmbedded() creates an embedded mapping equivalent to:

new EmbeddedMapping(new ArrayReference('name'), (new MapBuilder())
    ->add('first_name', 'fname')
    ->add('last_name', 'lname')
    ->getMap()
)

addResolver() 方法使用确定的引用类型创建一个 解析器映射

(new MapBuilder())
    ->addResolver('full_name', new FullNameResolver())
;

// addResolver() creates a mapping equivalent to:

new ResolverMapping(new ArrayReference('full_name'), new FullNameResolver())

最后,addMapping 方法简单地添加一个自定义用户定义的映射。

(new MapBuilder())
    ->addMapping(new Mapping(new ArrayReference('first_name'), new ArrayReference('fname')))
;

构建映射

一旦所有映射都已添加到 MapBuilder,就生成一个可以在上下文中使用的 MapgetMap() 方法为我们的使用生成一个新的 Map

(new MapBuilder())
    ->add('first_name', 'fname')
    ->add('last_name', 'lname')
    ->getMap()
;

// is equivalent to:

new Map([
    new Mapping(new ArrayReference('first_name'), new ArrayReference('fname')),
    new Mapping(new ArrayReference('last_name'), new ArrayReference('lname'))
]);

想要贡献吗?

如果您想为这个库做出贡献,可以通过以下几种方式实现:

  • 提交有关错误或改进项目的功能的issue。
  • 提交包含新功能的pull request。
  • 更新任何缺失的文档。

在提交pull request时,请确保功能由单元测试覆盖,并且所有测试都通过。

许可

该库遵循MIT许可证,意味着任何人都可以免费使用和修改。

The MIT License (MIT)

Copyright (c) 2015 Cascade Media, LLC.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.