gfg/dto-context

负责根据创建的上下文创建对象,以处理数据完整性

1.0.11 2016-08-26 17:23 UTC

README

Scrutinizer Code Quality Code Coverage Build Status Latest Stable Version Total Downloads License Forks Stars

简介

在处理实体的数据表示时,我们经常会发现需要检查和验证一组参数,这些参数取决于将要执行的操作。这些参数会根据要执行的操作而变化。通常,这会导致大量代码,以确保该特定操作的所有信息实际上都存在。

概念

DTO Context(数据传输对象)的主要思想是有一个实体的单一数据表示,在这种情况下称为 DataWrapper,其中包含实体可以拥有的所有内容,以及一个称为 Context 的对象,该对象表示实体将能够执行的动作上下文。例如:一个名为 Person 的实体,具有一些属性,如姓名、年龄、身高和体重,以及执行创建、更新姓名、增加年龄、更改国籍等操作的几个动作。对于这些动作中的任何一个,实体本身都是相同的,但是在更新姓名的情况下,不需要确保体重或身高已被填写。更新姓名的动作上下文知道这一点,知道执行该操作所需的全部内容,以及为该操作进行操作所需的所有内容。

除了封装特定动作的数据之外,每个上下文还会自动添加一些元数据,这可能非常有用,例如上下文名称和上下文唯一哈希,该哈希在数据导出时生成。

结构

Context //The context object where the rules and parameters are stored
├─ Base.php //Contains all the basic methods for a Context to be implemented
└─ ContextInterface.php //Every context must implement this interface

DataWrapper //The data representation of the system entities
├─ Base.php  //Contains all the basic actions that are needed for a datawrapper to work properly
├─ BaseCollection.php //A container for a collection of datawrappers
└─ DataWrapperInterface.php //Every datawrapper should implement this interface

Factory //Responsible for creating the contexts properly
├─ Base.php //All the basic actions to perform context creation
├─ FactoryInterface.php //All context factories should implement this interface
├─ Hydrator.php //Class responsible to rebuild a context based on the exported version of it
└─ HydratorInterface.php //All Hydrators must implement this interface

Manager.php //A manager to intermediate the requests to control contexts, this allows flexibility, adding custom factories and hydrators to attend specific needs

使用示例

该库本身提供支持,以扩展并实现特定场景的上下文逻辑。假设上述提到的Person上下文示例。要使用DTO-Context实现它,请遵循以下步骤

  1. 创建上下文工厂;
  2. 创建数据包装器;
  3. 创建上下文。

建议的结构

建议的结构应用作可组合的库。

src\MyDTO
  └─ Context 
    ├─ Factory.php
    └─ Person
        ├─ CreatePerson.php 
        ├─ UpdateName.php 
        └─ ...

    DataWrapper 
    └─ Person.php 

1. ContextFactory

<?php

namespace MyDTO\Context;

use \GFG\DTOContext\Factory\Base;

class Factory extends Base
{   
    /**
     * we map the contexts that the we'll be using,
     * this can be very useful to identify in one place all the related
     * actions of an entity
     */
    const PERSON_CREATE             = 'person.create';
    const PERSON_UPDATE_NAME        = 'person.update.name';
    const PERSON_UPDATE_HEIGHT      = 'person.update.height';
    const PERSON_UPDATE_WEIGHT      = 'person.update.weight';
    const PERSON_INCREASE_AGE       = 'person.update.age';
    // ...

    /**
     * Points to which class this context will use
     */
    private $mappingList = [
        self::PERSON_CREATE             = 'MyDTO\Context\Person\CreatePerson',
        self::PERSON_UPDATE_NAME        = 'MyDto\Context\Person\UpdateName',
        self::PERSON_UPDATE_HEIGHT      = 'MyDto\Context\Person\UpdateHeight',
        self::PERSON_UPDATE_WEIGHT      = 'MyDto\Context\Person\UpdateWeight',
        self::PERSON_INCREASE_AGE       = 'MyDto\Context\Person\IncreaseAge',
        // ...
    ];

    public getMappingList()
    {
        return self::$mappingList;    
    }
}

2. DataWrapper

<?php

namespace MyDTO\DataWrapper;

use \GFG\DTOContext\DataWrapper\Base;

/**
 * @SuppressWarnings(PHPMD.UnusedPrivateField)
 * @method string getName()
 * @method integer getHeight()
 * @method integer getWeight()
 * @method integer getAge()
 * @method string getNacionality()
 * @method \MyDTO\DataWrapper\Person setName(string $name)
 * @method \MyDTO\DataWrapper\Person setHeight(integer $height)
 * @method \MyDTO\DataWrapper\Person setWeight(integer $weight)
 * @method \MyDTO\DataWrapper\Person setAge(integer $age)
 * @method \MyDTO\DataWrapper\Person setNacionality(string $nationality)
 */
class Person extends Base
{
    private $name;
    private $height;
    private $weight;
    private $age;
    private $nationality;
}

3. Contexts

创建Person上下文

<?php

namespace MyDTO\Context;

use \GFG\DTOContext\Context\ContextInterface;

class CreatePerson extends ContextInterface
{
    /**
     * In this method, we'll use only the data that is needed for
     * this action
     */
    public function exportContextData()
    {
        $dataWrapper = $this->getDataWrapper();

        return $this->prepareExport([
            'name'        => $dataWrapper->getName(),
            'height'      => $dataWrapper->getHeight(),
            'weight'      => $dataWrapper->getWeight(),
            'age'         => $dataWrapper->getAge(),
            'nationality' => $dataWrapper->getNacionality()
        ]);
    }
}

更新姓名

<?php

namespace MyDTO\Context;

use \GFG\DTOContext\Context\ContextInterface;

class UpdateName extends ContextInterface
{
    public function exportContextData()
    {
        $dataWrapper = $this->getDataWrapper();

        return $this->prepareExport([
            'name'  => $dataWrapper->getName()
        ]);
    }
}

实现

<?php

use \GFG\DTOContext\Context\Manager;
use \MyDTO\Context\Factory;
use \MyDto\DataWrapper\Person;

$manager = (new Manager())
    ->setFactory(new Factory);

$context = $manager->build(
    Factory::PERSON_CREATE, 
    new Person([
        'name'        => 'John Armless',
        'height'      => 180,
        'weight'      => 90,
        'age'         => 20,
        'nationality' => 'Brazilian'
    ])
);

// also, you can add some extra information to be sent with the metadata
// any set method will be used to store information, later captured by the
// corresponding get method
$context->setAccessCode('access code');

上下文的组成

如前所述,有一些元数据可以增加使用DTO-Context封装数据的优势。

<?php 
$exportedData = $context->exportContextData();

/*
$exportedData = Array(
    'name'         => 'mydto.context.person.createperson' //this name is
generated based on the namespace of the context
    'info'         => ['AccessCode' => 'access code'] //extra information
    'hash'         => 'ff17fea5e96ea2401372805b763fb182' //this hash is an
unique hash generated for each instance, specially useful for tracking purposes 
    'data_wrapper' => 'MyDTO\DataWrapper\Person' //which datawrapper this
context is using
    'data'         => Array(
        'name'        => 'John Armless',
        'height'      => 180,
        'weight'      => 90,
        'age'         => 20,
        'nationality' => 'Brazilian'
    ) //all the data that was exported
);
*/
?>

有了这个导出数据的数组,可以重建完全相同的上下文,这在许多方面都非常有用,例如在服务器之间记录请求,因为重建时的哈希将保持不变。

<?php
use \GFG\DTO-Context\Factory\Hydrator;
$rebuiltContext = $manager->rebuild($exportedData, new Hydrator);