nylle/php-automapper

dev-master 2018-06-14 07:17 UTC

This package is not auto-updated.

Last update: 2024-09-20 19:14:42 UTC


README

PHP 版本的著名 .NET AutoMapper,自动将一个对象的属性映射到另一个对象。

PHP 有所不同

PHP 不是类型安全的。虽然支持数组、类和接口的函数参数类型提示,但无法指定返回类型。

为了确保安全映射,PHP AutoMapper 需要遵守一些约定。

约定

  1. 只有公共属性会被映射。受保护的或私有的属性以及方法、获取器和设置器将被忽略。
  2. 目标类的属性需要具有适当的 PHPDoc 元素,形式如下: /** @var type */
  3. 任何标量类型(int、integer、bool、boolean、float、double、real、string)都会被复制,而不会进行进一步的检查(请参阅 PHP 的 类型转换)。
  4. 类型 /** @var array */ 被认为是标量类型的简单数组,因此它们会被复制而不会进行进一步的检查,同时保留键。
  5. 对象类型需要包含完整的命名空间和类名,形式如下: /** @var Full\Name\Space\ClassName */
  6. 对于对象数组,类型需要是包括命名空间在内的完整类名,后面跟着空方括号: /** @var NameSpace\ClassName[] */。键将不会保留。

使用方法

默认情况下,所有具有相同名称的属性都会从源对象映射到目标对象。标量将被复制,对象将被递归映射。

映射具有不同名称的属性

在创建映射时,目标和源属性需要是包含类名(包括命名空间)以及属性名(用作用域解析运算符 :: 分隔)的完全限定字符串。这样,只有指定类的属性会被映射,但嵌套对象中具有相同名称的属性将被故意忽略。因此,对于每个期望的类,每个具有不同名称的目标/源属性对都需要进行映射。

$mapper = new \Adminomatic\AutoMapper\Mapper();
$mapper->CreateMap('NameSpace\DestinationClass::MyProperty', 'NameSpace\SourceClass::DifferentlyNamedProperty');
$mapper->CreateMap('NameSpace\SubClass::MyProperty', 'NameSpace\SourceSubClass::DifferentlyNamedProperty');
$myDestinationObject = $mapper->Map(new \NameSpace\DestinationClass(), $sourceObject);

指定自定义类型转换器

只能为映射属性对指定 ITypeConverter。否则,转换器将用于所有属性。

$mapper = new \Adminomatic\AutoMapper\Mapper();
$mapper->CreateMapUsingConverter('DestinationClass::MyProperty', 
                                 'SourceClass::DifferentlyNamedProperty', 
                                 new \Adminomatic\AutoMapper\ImplodeConverter());
$myDestinationObject = $mapper->Map(new DestinationClass(), $sourceObject);

要为多个属性使用相同的转换器,需要为每种情况创建一个映射。

$myImplodeConverter = new \Adminomatic\AutoMapper\ImplodeConverter();
 
$mapper = new \Adminomatic\AutoMapper\Mapper();
$mapper->CreateMapUsingConverter('DestinationClass::MyProperty', 'SourceClass::DifferentlyNamedProperty', $myImplodeConverter);
$mapper->CreateMapUsingConverter('DestinationClass::OtherProperty', 'SourceClass::AnotherProperty', $myImplodeConverter);
$myDestinationObject = $mapper->Map(new DestinationClass(), $sourceObject);

记住:每个属性都必须通过包括命名空间和类名,然后是作用域解析运算符和属性名来完全限定。

从多个源值解析目标属性值

有时,目标属性期望的值依赖于源对象的多个属性。想象你有一个具有属性 PersonDTO->FirstNamePersonDTO->LastName 的源类 PersonDTO,但你希望将这些组合成目标属性 PersonModel->FullName。指定你的 IValueResolver 实现来处理这些情况。值解析器将始终获取父对象以使用属性,因此不能指定源属性。

$mapper = new \Adminomatic\AutoMapper\Mapper();
$mapper->CreateMapUsingResolver('NameSpace\DestinationClass::MyProperty', new \CustomResolver());
$myDestinationObject = $mapper->Map(new \NameSpace\DestinationClass(), $sourceObject);

API

PHP AutoMapper 包含用于创建自己的类型转换器和值解析器的接口。

ITypeConverter

ITypeConverter::Convert() 方法期望源属性值并返回目标属性转换后的值。

namespace Adminomatic\AutoMapper {
    interface ITypeConverter {
        function Convert($source);
    }
}

包含的类型转换器

PHP AutoMapper 包含两个简单易用的类型转换器。

CountConverter

namespace Adminomatic\AutoMapper {
    class CountConverter implements ITypeConverter {
        public function Convert($source) {
            return \count($source);
        }
    }
}

CountConverter 期望输入为一个数组,并返回元素的数量。

一个可能的例子是,一个包含评论数组的博客帖子 DTO,你想要将评论数组转换成博客帖子模型中的整数值,简单地显示该帖子的评论数量。

ImplodeConverter

namespace Adminomatic\AutoMapper {
    class ImplodeConverter implements ITypeConverter {
        private $delimiter;
 
        public function __construct($delimiter='') {
            $this->delimiter = $delimiter;
        }
 
        public function Convert($source) {
            return \implode($this->delimiter, $source);
        }
    }
}

ImplodeConverter 期望输入为一个标量数组,并返回一个包含所有元素的连接字符串,如果指定了分隔符,则由分隔符分隔。

一个可能的例子是博客帖子中的类别或标签数组,应映射为一个单字符串,显示在帖子上方或下方。

IValueResolver

IValueResolver::Resolve() 方法期望源对象并返回目标属性解析后的值。

namespace Adminomatic\AutoMapper {
    interface IValueResolver {
        function Resolve($source);
    }
}

值解析器示例(不包括在内)

有时目标属性的期望值取决于源对象的多个属性。你可以通过实现 IValueResolver 接口来创建任何需要的值解析器。

namespace MyNameSpace {
    class FullNameResolver implements \Adminomatic\AutoMapper\IValueResolver {
        function Resolve($source) {
            if(!\is_object($source) || \get_class($source) != 'MyNameSpace\MyExpectedClass') {
                return null;
            }
            return $source->FirstName . ' ' . $source->LastName;
        }
    }
}