Skip to content

Commit

Permalink
Merge pull request #13 from interactive-solutions/hydration-using-clo…
Browse files Browse the repository at this point in the history
…sure

Hydration using closures
  • Loading branch information
Hotas2k authored Apr 7, 2017
2 parents 99672c3 + ffd82d3 commit 9bebd03
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 86 deletions.
22 changes: 6 additions & 16 deletions src/Context/EntityFixtureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

class EntityFixtureContext implements SnippetAcceptingContext, ServiceManagerAwareInterface
{
use EntityHydratorTrait;
use EntityHydrationTrait;

/**
* @var array
Expand Down Expand Up @@ -297,14 +297,12 @@ public function anExistingWithValues($type, TableNode $values)

$properties = $this->getDefaultEntityProperties($type);
$metadata = $this->entityManager->getClassMetadata($entityClass);
$hydrator = $this->createEntityHydrator($metadata);

foreach ($values->getRows() as list ($key, $value)) {
$properties[$key] = $value;
}

$entity = $hydrator->hydrate($properties, new $entityClass());

$entity = $this->hydrateEntity($metadata, new $entityClass(), $properties);
$this->ensureEntityTimestamps($entity);

$this->entityManager->persist($entity);
Expand Down Expand Up @@ -426,25 +424,21 @@ public function anExistingWithParent($type, $parentType, $parentId)
*/
public function anExistingWithParentAndValues($type, $parentType, $parentId, TableNode $values)
{
$options = $this->getEntityOptions($type);
$parentClass = $this->getEntityClass($parentType);
$targetClass = $this->getEntityClass($type);

$properties = $this->getDefaultEntityProperties($type);
$metadata = $this->entityManager->getClassMetadata($targetClass);

$hydrator = isset($options['hydrator']) ? new $options['hydrator'] : null;
$hydrator = $this->createEntityHydrator($metadata, $hydrator);

foreach ($values->getRows() as list ($key, $value)) {
$properties[$key] = $value;
}

$targetEntity = $hydrator->hydrate($properties, new $targetClass());
$parentEntity = $this->entityManager->find($parentClass, $parentId);

$targetEntity = $this->hydrateEntity($metadata, new $targetClass(), $properties);
$this->ensureEntityTimestamps($targetEntity);

$parentEntity = $this->entityManager->find($parentClass, $parentId);

$parentMetadata = $this->entityManager->getClassMetadata($parentClass);
$targetMetadata = $this->entityManager->getClassMetadata($targetClass);

Expand Down Expand Up @@ -499,20 +493,16 @@ public function anExistingOnAliasAsWithValues($type, $alias, $childAlias, TableN
*/
public function anExistingOnAliasWithValues($type, $alias, TableNode $values)
{
$options = $this->getEntityOptions($type);
$targetClass = $this->getEntityClass($type);

$properties = $this->getDefaultEntityProperties($type);
$metadata = $this->entityManager->getClassMetadata($targetClass);

$hydrator = isset($options['hydrator']) ? new $options['hydrator'] : null;
$hydrator = $this->createEntityHydrator($metadata, $hydrator);

foreach ($values->getRows() as list ($key, $value)) {
$properties[$key] = $value;
}

$targetEntity = $hydrator->hydrate($properties, new $targetClass());
$targetEntity = $this->hydrateEntity($metadata, new $targetClass(), $properties);
$this->ensureEntityTimestamps($targetEntity);

$parent = $this->getEntityFromAlias($alias);
Expand Down
155 changes: 155 additions & 0 deletions src/Context/EntityHydrationTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php
/**
* @author Erik Norgren <[email protected]>
* @copyright Interactive Solutions
*/

namespace InteractiveSolutions\ZfBehat\Context;

use Closure;
use DateTime;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use InvalidArgumentException;

trait EntityHydrationTrait
{
/**
* Hydrates an entity
*
* @param ClassMetadata $metadata
* @param mixed $entity
* @param $values
*
* @return mixed
*
* @throws InvalidArgumentException
*/
private function hydrateEntity(ClassMetadata $metadata, $entity, $values)
{
foreach ($metadata->getFieldNames() as $field) {
// Ignore whatever is not set in the properties array
if (!array_key_exists($field, $values)) {
continue;
}

switch ($metadata->getTypeOfField($field)) {
case Type::DATETIME:
$this->setDatetimeFieldOfObject($entity, $field, $values[$field]);
break;

case Type::SIMPLE_ARRAY:
$this->setSimpleArrayFieldOfObject($entity, $field, $values[$field]);
break;

case Type::BOOLEAN:
$this->setBoolFieldOfObject($entity, $field, $values[$field]);
break;

default:
$this->setFieldOfObject($entity, $field, $values[$field]);
}
}

return $entity;
}

/**
* Binds an anonymous function to an object, allowing us to access
* instance variables directly
*
* @param $object
* @param $field
* @param $newValue
*
* @return void
*/
private function setFieldOfObject($object, $field, $newValue)
{
$setValue = function ($object, $field, $newValue) {
$object->{$field} = $newValue;
};

$setValue = Closure::bind($setValue, null, $object);

$setValue($object, $field, $newValue);
}

/**
* Sets the datetime field of an object
*
* @param $object
* @param $field
* @param $newValue
*/
private function setDatetimeFieldOfObject($object, $field, $newValue)
{
$newValue = is_string($newValue) ? new DateTime($newValue) : $newValue;

$this->setFieldOfObject($object, $field, $newValue);
}

/**
* Sets the simple array field of an object
*
* @param $object
* @param $field
* @param $newValue
*/
private function setSimpleArrayFieldOfObject($object, $field, $newValue)
{
$newValue = is_string($newValue) ? explode(',', $newValue) : $newValue;

$this->setFieldOfObject($object, $field, $newValue);
}

/**
* Set bool field of an object
*
* @param $object
* @param $field
* @param $newValue
*
* @throws InvalidArgumentException
*
* @return void
*/
private function setBoolFieldOfObject($object, $field, $newValue)
{
if (is_bool($newValue) || is_numeric($newValue)) {
return $this->setFieldOfObject($object, $field, (bool) $newValue);
}

// from now on, we assume it's a string
switch (mb_strtolower($newValue)) {
case '1':
case 't':
case 'true':
return $this->setFieldOfObject($object, $field, true);

case '0':
case 'f':
case 'false':
return $this->setFieldOfObject($object, $field, false);

default:
throw new InvalidArgumentException('Failed to parse boolean value');
}
}

/**
* Ensure createdAt/updatedAt is not null after entity creation
*
* @param $entity
*/
private function ensureEntityTimestamps($entity)
{
if (property_exists($entity, 'createdAt') && $entity->getCreatedAt() === null) {
$this->setFieldOfObject($entity, 'createdAt', new DateTime());
}

if (property_exists($entity, 'updatedAt') && $entity->getCreatedAt() === null) {
$this->setFieldOfObject($entity, 'updatedAt', new DateTime());
}
}
}
62 changes: 0 additions & 62 deletions src/Context/EntityHydratorTrait.php

This file was deleted.

14 changes: 6 additions & 8 deletions src/Context/UserFixtureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

class UserFixtureContext implements SnippetAcceptingContext, ServiceManagerAwareInterface
{
use EntityHydratorTrait;
use EntityHydrationTrait;

/**
* @var ObjectManager
Expand Down Expand Up @@ -71,9 +71,9 @@ public function getRepository()
*/
public function generateDefaultUser($role = 'user', $stepIdentifierValue = null)
{
$userEntityClass = $this->options->getUserEntityClass();
$userProperties = $this->options->getDefaultUserProperties();
$stepIdentifier = $this->options->getUserStepIdentifier();
$userEntityClass = $this->options->getUserEntityClass();
$userProperties = $this->options->getDefaultUserProperties();
$stepIdentifier = $this->options->getUserStepIdentifier();

// If we have a password, bcrypt it
if (isset($userProperties['password'])) {
Expand All @@ -89,14 +89,12 @@ public function generateDefaultUser($role = 'user', $stepIdentifierValue = null)
/* @var TokenOwnerInterface $user */
$user = $this->getRepository()->findOneBy([$stepIdentifier => $userProperties[$stepIdentifier]]);

if (! $user) {
if (!$user) {
$user = new $userEntityClass();
}

$metadata = $this->objectManager->getClassMetadata($userEntityClass);
$hydrator = $this->createEntityHydrator($metadata);

$hydrator->hydrate($userProperties, $user);
$user = $this->hydrateEntity($metadata, $user, $userProperties);

return $user;
}
Expand Down

0 comments on commit 9bebd03

Please sign in to comment.