somnambulist/laravel-domain-input-mapper

此包已被弃用,不再维护。作者建议使用somnambulist/domain-input包。

这是一个用于创建域输入对象并将它们映射到Laravel项目中的实体的支持类集合。

0.5.2 2017-02-18 04:31 UTC

This package is auto-updated.

Last update: 2022-02-01 12:58:06 UTC


README

该项目已被:somnambulist/domain-input-mapper 替换 https://github.com/dave-redfern/somnambulist-domain-input

请迁移到替代包。

域输入映射库

该库提供请求对象(例如Http\Request)与域实体之间的抽象。不是直接传递请求,而是将其转换为包含要映射到域对象的信息的DomainInput对象。域输入包含只读的请求数据和文件的集合。

对于Http,使用Laravel Illuminate的UploadedFile和Request,但可以使用任何请求。

要求

  • PHP 5.6+
  • Illuminate/Http 5.2+
  • Illuminate/Support 5.2+
  • somnambulist/collection 1.0+

安装

使用composer安装,或从github.com检出/拉取文件。

  • composer require somnambulist/laravel-domain-input-mapper

域输入

第一个组件是DomainInput和相关工厂类:DomainInputFactory。工厂包含从HttpRequest或直接从传递的集合创建的方法。

DomainInput对象包含所有要映射到域中的实体或聚合路由的输入和文件数据。通过抽象请求类型,可以将域映射/处理保持得干净,并更易于在其他上下文中使用。

DomainInput由以下组成

  • 输入
  • 文件

两者都从标准集合转换为不可变集合。一旦创建,就不应修改它们,因为它们现在代表请求进入域。

DomainInput提供对输入(别名get())和文件的访问器。两者都支持Laravel点表示法来访问嵌套数组的数据,例如:object.type.file。

域响应

当从域返回数据时,最好将结果表示为单个单元,它包括

  • 转换后的域数据
  • 事务/域状态
  • 任何消息(例如错误/警告)

提供了一个基本接口和实现,实现了域响应。

此响应为只读,并使用不可变集合来存储域数据和任何消息。此外,原始的DomainInput与响应相关联。这确保在进一步处理域数据时,原始输入可用。

一个重要功能是,域处理结果提供在这个响应中。它不需要再次由视图/响应层“发现”。状态可以是应用程序所需的任何数据类型,尽管建议使用字符串或整数。

由于响应是只读的,并且通过构造函数构建,因此应该在集合中收集任何域数据,并将其传递给构造函数。

域映射器

最后一个组件是一个接口和基本的聚合实现,用于将DomainInput映射到您的实体/聚合。这是一个非常简单的接口,包含两个方法:

  • map
  • supports

map方法执行工作,接受DomainInput和预先创建的实体。需要注意的是,映射器并不打算创建主根实体/聚合。这应该在数据映射之前通过单独的工厂步骤提供。话虽如此,映射器可以创建将附加到根的所需子实体。

supports是一个简单的检查,以查看映射器是否支持传递的实体。这通常是对实体进行的instanceof类型检查。在聚合映射器中使用,以防止在不支持映射器的情况下使用实体。

映射器本身可以像需要的那么复杂或简单;需要注意的是,它应该只为单个实体或聚合的子集执行映射。例如:您有一个由订单及其子实体(如行项、客户、地址)组成的聚合。每个这些可能都需要单独的存储库或额外的支持逻辑。这可以封装在单个映射器中,并使用OrderMapper聚合一次映射整个输入。

示例

主订单映射器

use Somnambulist\Domain\Contracts\DataInputMapper as DataInputMapperContract;
class OrderMapper implements DataInputMapperContract
{

    /**
     * @param Input $input
     * @param Order $entity
     */
    public function map(Input $input, $entity)
    {
        $entity
            ->setProperty($input->get('order.property'))
            // ... do other mapping
        ;
    }

    /**
     * @return boolean
     */
    public function supports($entity)
    {
        return ($entity instanceof Order);
    }
}

订单项映射器

use Somnambulist\Domain\Contracts\DataInputMapper as DataInputMapperContract;
class OrderItemMapper implements DataInputMapperContract
{

    protected $factory;

    public function __construct(OrderFactory $factory)
    {
        $this->factory = $factory;
    }

    /**
     * @param Input $input
     * @param Order $entity
     */
    public function map(Input $input, $entity)
    {
        // look up existing items, or make new ones
        foreach ($input->get('order.item') as $item) {
            $orderItem = $this->factory->createOrderItem($entity);

            // item will be an array, convert to collection
            $item = new Immutable($item);
            $orderItem
                ->setSomeProperty($item->get('some_property))
                // ... do other mapping
            ;
        }
    }

    /**
     * @return boolean
     */
    public function supports($entity)
    {
        return ($entity instanceof Order);
    }
}

地址映射器

use Contracts\AddressableEntity;
use Somnambulist\Domain\Contracts\DataInputMapper as DataInputMapperContract;
class AddressMapper implements DataInputMapperContract
{

    /**
     * @param Input $input
     * @param Order $entity
     */
    public function map(Input $input, $entity)
    {
        $address = new Address();
        $address
            ->setAddressLine1($input->get('address.address_line_1')
            //... assign the rest
        ;

        // addresses should be value objects so we'll check if it is the same
        // these methods will all be defined in the AddressableEntity interface.
        // The address being a value object will have an isSameAs method.
        if (!$entity->hasAddress() || !$entity->getAddress()->isSameAs($address)) {
            $entity->setAddress($address);
        }
    }

    /**
     * @return boolean
     */
    public function supports($entity)
    {
        return ($entity instanceof AddressableEntity);
    }
}

将它们全部组合在一起

class OrderAggregateMapper extends AggregateMapper
{

}

// in an input handler / command (better defined in the DI container)
$mapper = new OrderAggregateMapper([
    new OrderMapper(),
    new OrderItemMapper(new OrderFactory()),
    new AddressMapper(),
]);

$input  = $inputFactory->createFromHttpRequest($request);
$entity = new Order();

$mapper->map($input, $entity);

这种方法的优点是各个部分的隔离和分离。这使得每个部分更容易测试、更容易管理,在某些情况下,映射器可以重用(例如,地址)。替代方案可能是单个处理程序,它必须接收实体管理器实例或许多存储库来完成相同的工作。如果您不想映射所有数据或您想在多个步骤中部分映射聚合根,每个都必须单独处理,而通过这种方法,只需在各个阶段添加所需的映射器即可。

最后:重要的是要记住,数据输入映射器是您领域的一部分,而不是控制器或上下文类型的一部分。它不应了解HTTP或CLI的具体情况。