gfg/dto-context

Responsible to create objects to handle data integrity based on the context which they are created

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

INTRODUCTION

When dealing with data representation of an entity, frequently we find ourselves checking and verifying a set of parameters that are needed to perform a certain action. These parameters are changed depending on the action that will be executed. Often ending up with a lot of code in order to ensure that all information for that specific action is actually there.

CONCEPT

The main idea of DTO Context (Data Transfer Object) is to have one data representation of an entity, in this case called DataWrapper where everything that the entity is capable of holding is there, and an object called Context that represents the action context that the entity will be able to perform. As an example: an entity called Person with some properties, such as name, age, height and weight, with several actions to perform as creation, update name, increase age, change nationality. For any of these actions the entity itself will be the same, but for updating the name ther is no need to ensure that the weight or the height is populated. The Context to update name action is aware of that, knows what is needed to perform the action, and everything that is required for that action to proceed.

Besides from encapsulating the data for a specific action, there is some metadata automatically added to each context, that can be extremely useful, such as context name and a context unique hash that is generated when the data is exported.

STRUCTURE

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

EXAMPLES OF USE

The library itself offers support to extend and implement context logic to a specific scenario. Assuming the example of the Person contexts as mentioned above. In order to implement it using the DTO-Context, follow these steps:

  1. Create the ContextFactory;
  2. Create the DataWrapper;
  3. Create the Contexts.

Proposed structure

The proposed structure is to be used as a composable library.

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

CreatePerson context

<?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()
        ]);
    }
}

UpdateName

<?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()
        ]);
    }
}

Implementation

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

Anatomy of a Context

As previously stated, there is some metadata that increases the benefits of using DTO-Context to encapsulate data.

<?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
);
*/
?>

With this array of the exported data, it can rebuild the exact same context, which is very usefull for many things, such as logging a request between servers, since the hash will continue the same when rebuilt.

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