Skip to content

Commit

Permalink
cache which fields on entities are enums
Browse files Browse the repository at this point in the history
  • Loading branch information
VasekPurchart committed Sep 16, 2017
1 parent 9de2ae1 commit b47d382
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 24 deletions.
7 changes: 5 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,20 @@ You need to register [`EnumPostLoadEntityListener`](/src/Enum/EnumPostLoadEntity
<?php

use Consistence\Doctrine\Enum\EnumPostLoadEntityListener;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\Events;

/** @var \Doctrine\ORM\EntityManager $entityManager */
/** @var \Doctrine\ORM\Mapping\Driver\AnnotationDriver $annotationDriver */
$annotationDriver = $entityManager->getConfiguration()->getMetadataDriverImpl();
$annotationReader = $annotationDriver->getReader();

// make sure to use the most appropriate cache for given environment to get the best performance
$cache = new ArrayCache();

$entityManager->getEventManager()->addEventListener(
Events::postLoad,
new EnumPostLoadEntityListener($annotationReader)
new EnumPostLoadEntityListener($annotationReader, $cache)
);
```

Expand Down
84 changes: 62 additions & 22 deletions src/Enum/EnumPostLoadEntityListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Consistence\Enum\Enum;
use Doctrine\Common\Annotations\Reader;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\Cache;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
Expand All @@ -16,52 +18,90 @@ class EnumPostLoadEntityListener
/** @var \Doctrine\Common\Annotations\Reader */
private $annotationReader;

public function __construct(Reader $annotationReader)
/** @var \Doctrine\Common\Cache\Cache */
private $enumFieldsCache;

public function __construct(
Reader $annotationReader,
Cache $enumFieldsCache = null
)
{
$this->annotationReader = $annotationReader;
$this->enumFieldsCache = $enumFieldsCache !== null ? $enumFieldsCache : new ArrayCache();
}

public function postLoad(LifecycleEventArgs $event)
{
$entity = $event->getEntity();
$entityManager = $event->getEntityManager();
$metadata = $this->getClassMetadata($entityManager, get_class($entity));
foreach ($this->getEnumFields($entityManager, $entity) as $fieldName => $enumClassName) {
$this->processField($entityManager, $entity, $fieldName, $enumClassName);
}
}

/**
* @param \Doctrine\ORM\EntityManager $entityManager
* @param object $entity
* @return string[] format: enum field name (string) => enum class name for field (string)
*/
private function getEnumFields(
EntityManager $entityManager,
$entity
): array
{
$className = get_class($entity);
$enumFields = $this->enumFieldsCache->fetch($className);
if ($enumFields !== false) {
return $enumFields;
}

$enumFields = [];
$metadata = $this->getClassMetadata($entityManager, $className);
foreach ($metadata->getFieldNames() as $fieldName) {
$this->processField($entityManager, $entity, $fieldName);
$annotation = $this->annotationReader->getPropertyAnnotation(
$metadata->getReflectionProperty($fieldName),
EnumAnnotation::class
);
if ($annotation !== null) {
$enumClassName = $metadata->fullyQualifiedClassName($annotation->class);
if (!is_a($enumClassName, Enum::class, true)) {
throw new \Consistence\Doctrine\Enum\NotEnumException($enumClassName);
}
$enumFields[$fieldName] = $enumClassName;
}
}

$this->enumFieldsCache->save($className, $enumFields);

return $enumFields;
}

/**
* @param \Doctrine\ORM\EntityManager $entityManager
* @param object $entity
* @param string $fieldName
* @param string $enumClassName
*/
private function processField(
EntityManager $entityManager,
$entity,
string $fieldName
string $fieldName,
string $enumClassName
)
{
$metadata = $this->getClassMetadata($entityManager, get_class($entity));

$annotation = $this->annotationReader->getPropertyAnnotation($metadata->getReflectionProperty($fieldName), EnumAnnotation::class);

if ($annotation !== null) {
$scalarValue = $metadata->getFieldValue($entity, $fieldName);
if (is_scalar($scalarValue)) {
$enumClass = $metadata->fullyQualifiedClassName($annotation->class);
if (!is_a($enumClass, Enum::class, true)) {
throw new \Consistence\Doctrine\Enum\NotEnumException($enumClass);
}
$enum = $enumClass::get($scalarValue);
$metadata->setFieldValue($entity, $fieldName, $enum);
$entityManager->getUnitOfWork()->setOriginalEntityProperty(
spl_object_hash($entity),
$fieldName,
$enum
);
}
$scalarValue = $metadata->getFieldValue($entity, $fieldName);
if (!is_scalar($scalarValue)) {
return;
}

$enum = $enumClassName::get($scalarValue);
$metadata->setFieldValue($entity, $fieldName, $enum);
$entityManager->getUnitOfWork()->setOriginalEntityProperty(
spl_object_hash($entity),
$fieldName,
$enum
);
}

private function getClassMetadata(
Expand Down
14 changes: 14 additions & 0 deletions tests/Enum/LoadEnumToEntityIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ public function testLoadEnumNotEnumClass()
}
}

public function testLoadMultipleInstancesOfOneEntity()
{
$foo = new FooEntity();
$iAmFooToo = new FooEntity();

list($postLoadListener, $entityManager) = $this->getPostLoadListener();

$postLoadListener->postLoad(new LifecycleEventArgs($foo, $entityManager));
$postLoadListener->postLoad(new LifecycleEventArgs($iAmFooToo, $entityManager));

$this->assertSame(FooEnum::get(FooEnum::ONE), $foo->getEnum());
$this->assertSame(FooEnum::get(FooEnum::ONE), $iAmFooToo->getEnum());
}

/**
* @param object $entity
*/
Expand Down

0 comments on commit b47d382

Please sign in to comment.