From b47d38209edbd4ca092897f17f20a48c77e67abb Mon Sep 17 00:00:00 2001 From: Vasek Purchart Date: Tue, 12 Sep 2017 22:44:32 +0200 Subject: [PATCH] cache which fields on entities are enums --- readme.md | 7 +- src/Enum/EnumPostLoadEntityListener.php | 84 ++++++++++++++----- .../Enum/LoadEnumToEntityIntegrationTest.php | 14 ++++ 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/readme.md b/readme.md index 2c8e0d8..23c760d 100644 --- a/readme.md +++ b/readme.md @@ -171,7 +171,7 @@ You need to register [`EnumPostLoadEntityListener`](/src/Enum/EnumPostLoadEntity 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) ); ``` diff --git a/src/Enum/EnumPostLoadEntityListener.php b/src/Enum/EnumPostLoadEntityListener.php index a481384..ad40640 100644 --- a/src/Enum/EnumPostLoadEntityListener.php +++ b/src/Enum/EnumPostLoadEntityListener.php @@ -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; @@ -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( diff --git a/tests/Enum/LoadEnumToEntityIntegrationTest.php b/tests/Enum/LoadEnumToEntityIntegrationTest.php index 5755e4a..ccaaf60 100644 --- a/tests/Enum/LoadEnumToEntityIntegrationTest.php +++ b/tests/Enum/LoadEnumToEntityIntegrationTest.php @@ -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 */