gfg / dto-context
Responsible to create objects to handle data integrity based on the context which they are created
Installs: 9 103
Dependents: 3
Suggesters: 0
Security: 0
Stars: 4
Watchers: 9
Forks: 0
Open Issues: 0
Requires (Dev)
- pdepend/pdepend: @stable
- phploc/phploc: @stable
- phpmd/phpmd: @stable
- phpunit/php-invoker: @stable
- phpunit/phpunit: @stable
- sebastian/phpcpd: @stable
- squizlabs/php_codesniffer: @stable
- sstalle/php7cc: @stable
README
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:
- Create the ContextFactory;
- Create the DataWrapper;
- 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);