From fbe61ff3f68affdd0d5535f013486c9009c81fcc Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Fri, 8 May 2020 13:06:34 +0200 Subject: [PATCH 1/9] Add classmap. --- composer.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 16f4169..c0bf334 100644 --- a/composer.json +++ b/composer.json @@ -18,17 +18,20 @@ "doctrine/annotations": "1.*", "doctrine/cache": "~1.0", "php": ">=7.2", - "symfony/framework-bundle": "~3.4|~4.4|~5.0", - "php-coveralls/php-coveralls": "^2.0" + "symfony/framework-bundle": "~3.4|~4.4|~5.0" }, "require-dev": { "doctrine/common": "~2.12", "doctrine/collections": "1.*", + "php-coveralls/php-coveralls": "^2.0", "phpunit/phpunit": "^8.5", "friendsofphp/php-cs-fixer": "^2.10" }, "autoload": { - "psr-4": { "BowlOfSoup\\NormalizerBundle\\": "src/" } + "psr-4": { "BowlOfSoup\\NormalizerBundle\\": "src/" }, + "classmap": [ + "tests/NormalizerTestTrait.php" + ] }, "autoload-dev": { "psr-4": { "BowlOfSoup\\NormalizerBundle\\Tests\\": "tests/" } From 7bf8da2443e417ca6725ff932a037fddc6e06a50 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 9 May 2020 00:32:42 +0200 Subject: [PATCH 2/9] ISSUE-25: Enable support for translations. --- composer.json | 4 +- src/Annotation/AbstractAnnotation.php | 8 --- src/Annotation/Normalize.php | 8 +++ src/Annotation/Serialize.php | 8 +++ src/Annotation/Translate.php | 44 ++++++++++++++++ .../RegisterAnnotationsListener.php | 1 + src/Service/Normalize/AbstractNormalizer.php | 52 ++++++++++++++++++- src/Service/Normalize/MethodNormalizer.php | 27 ++++++---- src/Service/Normalize/PropertyNormalizer.php | 27 ++++++---- tests/DummyTranslator.php | 15 ++++++ tests/NormalizerTestTrait.php | 8 ++- tests/Service/NormalizerTest.php | 7 +++ tests/assets/Person.php | 9 ++++ 13 files changed, 186 insertions(+), 32 deletions(-) create mode 100644 src/Annotation/Translate.php create mode 100644 tests/DummyTranslator.php diff --git a/composer.json b/composer.json index c0bf334..bfef5aa 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "doctrine/annotations": "1.*", "doctrine/cache": "~1.0", "php": ">=7.2", - "symfony/framework-bundle": "~3.4|~4.4|~5.0" + "symfony/framework-bundle": "~4.4|~5.0", + "symfony/translation": "~4.4|~5.0" }, "require-dev": { "doctrine/common": "~2.12", @@ -30,6 +31,7 @@ "autoload": { "psr-4": { "BowlOfSoup\\NormalizerBundle\\": "src/" }, "classmap": [ + "tests/DummyTranslator.php", "tests/NormalizerTestTrait.php" ] }, diff --git a/src/Annotation/AbstractAnnotation.php b/src/Annotation/AbstractAnnotation.php index 1a2ce9c..f655c52 100644 --- a/src/Annotation/AbstractAnnotation.php +++ b/src/Annotation/AbstractAnnotation.php @@ -16,19 +16,11 @@ abstract class AbstractAnnotation /** @var array */ protected $group = []; - /** @var string */ - protected $type; - public function getGroup(): array { return $this->group; } - public function getType(): string - { - return $this->type; - } - /** * Check if annotation property 'group' matches up with requested group. */ diff --git a/src/Annotation/Normalize.php b/src/Annotation/Normalize.php index 899325a..7679e95 100644 --- a/src/Annotation/Normalize.php +++ b/src/Annotation/Normalize.php @@ -40,6 +40,9 @@ class Normalize extends AbstractAnnotation /** @var int */ private $maxDepth; + /** @var string */ + protected $type; + public function __construct(array $properties) { foreach ($this->supportedProperties as $supportedPropertyKey => $supportedPropertyOptions) { @@ -87,4 +90,9 @@ public function getMaxDepth(): ?int { return $this->maxDepth; } + + public function getType(): string + { + return $this->type; + } } diff --git a/src/Annotation/Serialize.php b/src/Annotation/Serialize.php index 81fcd76..2e28a62 100644 --- a/src/Annotation/Serialize.php +++ b/src/Annotation/Serialize.php @@ -20,6 +20,9 @@ class Serialize extends AbstractAnnotation /** @var string */ private $wrapElement; + /** @var string */ + protected $type; + public function __construct(array $properties) { foreach ($this->supportedProperties as $supportedPropertyKey => $supportedPropertyOptions) { @@ -33,4 +36,9 @@ public function getWrapElement(): string { return $this->wrapElement; } + + public function getType(): string + { + return $this->type; + } } diff --git a/src/Annotation/Translate.php b/src/Annotation/Translate.php new file mode 100644 index 0000000..32bab58 --- /dev/null +++ b/src/Annotation/Translate.php @@ -0,0 +1,44 @@ + ['type' => 'array'], + 'domain' => ['type' => 'string'], + 'locale' => ['type' => 'string'], + ]; + + /** @var string|null */ + private $domain; + + /** @var string|null */ + private $locale; + + public function __construct(array $properties) + { + foreach ($this->supportedProperties as $supportedPropertyKey => $supportedPropertyOptions) { + if ($this->validateProperties($properties, $supportedPropertyKey, $supportedPropertyOptions, __CLASS__)) { + $this->$supportedPropertyKey = $properties[$supportedPropertyKey]; + } + } + } + + public function getDomain(): ?string + { + return $this->domain; + } + + public function getLocale(): ?string + { + return $this->locale; + } +} diff --git a/src/EventListener/RegisterAnnotationsListener.php b/src/EventListener/RegisterAnnotationsListener.php index f4f23ba..8a74bac 100644 --- a/src/EventListener/RegisterAnnotationsListener.php +++ b/src/EventListener/RegisterAnnotationsListener.php @@ -36,6 +36,7 @@ public function registerAnnotations(KernelEvent $event): void if ($this->parameterRegisterAnnotations) { AnnotationRegistry::registerFile(__DIR__ . '/../Annotation/Normalize.php'); AnnotationRegistry::registerFile(__DIR__ . '/../Annotation/Serialize.php'); + AnnotationRegistry::registerFile(__DIR__ . '/../Annotation/Translate.php'); AnnotationRegistry::registerAutoloadNamespace('BowlOfSoup\NormalizerBundle\Annotation', __DIR__); } } diff --git a/src/Service/Normalize/AbstractNormalizer.php b/src/Service/Normalize/AbstractNormalizer.php index e89152c..a156072 100644 --- a/src/Service/Normalize/AbstractNormalizer.php +++ b/src/Service/Normalize/AbstractNormalizer.php @@ -5,10 +5,12 @@ namespace BowlOfSoup\NormalizerBundle\Service\Normalize; use BowlOfSoup\NormalizerBundle\Annotation\Normalize; +use BowlOfSoup\NormalizerBundle\Annotation\Translate; use BowlOfSoup\NormalizerBundle\Exception\BosNormalizerException; use BowlOfSoup\NormalizerBundle\Model\ObjectCache; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; use Doctrine\Common\Collections\Collection; +use Symfony\Contracts\Translation\TranslatorInterface; abstract class AbstractNormalizer { @@ -21,6 +23,9 @@ abstract class AbstractNormalizer /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor */ protected $classExtractor; + /** @var \Symfony\Contracts\Translation\TranslatorInterface */ + protected $translator; + /** @var string */ protected $group; @@ -34,9 +39,11 @@ abstract class AbstractNormalizer public $processedDepthObjects = []; public function __construct( - ClassExtractor $classExtractor + ClassExtractor $classExtractor, + TranslatorInterface $translator ) { $this->classExtractor = $classExtractor; + $this->translator = $translator; } public function cleanUp(): void @@ -220,6 +227,49 @@ protected function handleCallbackResult($propertyValue, Normalize $propertyAnnot return $propertyValue; } + /** + * @param \BowlOfSoup\NormalizerBundle\Annotation\Translate[]|array + */ + protected function getTranslationAnnotation(array $translateAnnotations, $emptyGroup = false): ?Translate + { + if (empty($translateAnnotations)) { + return null; + } + + $group = ($emptyGroup) ? null : $this->group; + + $translationAnnotation = null; + /** @var \BowlOfSoup\NormalizerBundle\Annotation\Translate $translateAnnotation */ + foreach ($translateAnnotations as $translateAnnotation) { + if (!$translateAnnotation->isGroupValidForConstruct($group)) { + continue; + } + + // By overwriting the return variable, the last valid annotation on the property/method is taken. + $translationAnnotation = $translateAnnotation; + } + // Annotation found, but no explicit group. Try again with no group. + if (null === $translationAnnotation) { + return $this->getTranslationAnnotation($translateAnnotations, true); + } + + return $translationAnnotation; + } + + /** + * @param mixed $value + * + * @return mixed + */ + protected function translateValue($value, Translate $translationAnnotation) + { + if (!is_string($value)) { + return $value; + } + + return $this->translator->trans($value, [], $translationAnnotation->getDomain(), $translationAnnotation->getLocale()); + } + private function isCircularReference(object $object, string $objectName): bool { $objectIdentifier = $this->classExtractor->getId($object); diff --git a/src/Service/Normalize/MethodNormalizer.php b/src/Service/Normalize/MethodNormalizer.php index 9d38fad..af1e06b 100644 --- a/src/Service/Normalize/MethodNormalizer.php +++ b/src/Service/Normalize/MethodNormalizer.php @@ -5,10 +5,12 @@ namespace BowlOfSoup\NormalizerBundle\Service\Normalize; use BowlOfSoup\NormalizerBundle\Annotation\Normalize; +use BowlOfSoup\NormalizerBundle\Annotation\Translate; use BowlOfSoup\NormalizerBundle\Exception\BosNormalizerException; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; use BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor; use BowlOfSoup\NormalizerBundle\Service\Normalizer; +use Symfony\Contracts\Translation\TranslatorInterface; class MethodNormalizer extends AbstractNormalizer { @@ -17,9 +19,10 @@ class MethodNormalizer extends AbstractNormalizer public function __construct( ClassExtractor $classExtractor, + TranslatorInterface $translator, MethodExtractor $methodExtractor ) { - parent::__construct($classExtractor); + parent::__construct($classExtractor, $translator); $this->methodExtractor = $methodExtractor; } @@ -40,7 +43,7 @@ public function normalize( $classMethods = $this->methodExtractor->getMethods($object); $normalizedMethods = []; foreach ($classMethods as $classMethod) { - $methodAnnotations = $this->getMethodAnnotations($objectName, $classMethod); + $methodAnnotations = $this->getMethodAnnotations($objectName, $classMethod, Normalize::class); if (empty($methodAnnotations)) { continue; } @@ -58,18 +61,15 @@ public function normalize( return $normalizedMethods; } - private function getMethodAnnotations(string $objectName, \ReflectionMethod $classMethod): array + private function getMethodAnnotations(string $objectName, \ReflectionMethod $classMethod, string $annotationClass): array { $methodName = $classMethod->getName(); - if (isset($this->annotationCache[MethodExtractor::TYPE][$objectName][$methodName])) { - $methodAnnotations = $this->annotationCache[MethodExtractor::TYPE][$objectName][$methodName]; + if (isset($this->annotationCache[$annotationClass][MethodExtractor::TYPE][$objectName][$methodName])) { + $methodAnnotations = $this->annotationCache[$annotationClass][MethodExtractor::TYPE][$objectName][$methodName]; } else { - $methodAnnotations = $this->methodExtractor->extractMethodAnnotations( - $classMethod, - Normalize::class - ); - $this->annotationCache[MethodExtractor::TYPE][$objectName][$methodName] = $methodAnnotations; + $methodAnnotations = $this->methodExtractor->extractMethodAnnotations($classMethod, $annotationClass); + $this->annotationCache[$annotationClass][MethodExtractor::TYPE][$objectName][$methodName] = $methodAnnotations; } return $methodAnnotations; @@ -93,6 +93,9 @@ private function normalizeMethod( continue; } + $translateAnnotations = $this->getMethodAnnotations(get_class($object), $method, Translate::class); + $translationAnnotation = $this->getTranslationAnnotation($translateAnnotations); + $methodName = $method->getName(); $methodValue = $method->invoke($object); @@ -122,6 +125,10 @@ private function normalizeMethod( } $methodValue = (is_array($methodValue) && empty($methodValue) ? null : $methodValue); + if (null !== $translationAnnotation) { + $methodValue = $this->translateValue($methodValue, $translationAnnotation); + } + $normalizedProperties[$methodName] = $methodValue; } diff --git a/src/Service/Normalize/PropertyNormalizer.php b/src/Service/Normalize/PropertyNormalizer.php index 8a681ad..ef6411b 100644 --- a/src/Service/Normalize/PropertyNormalizer.php +++ b/src/Service/Normalize/PropertyNormalizer.php @@ -5,9 +5,11 @@ namespace BowlOfSoup\NormalizerBundle\Service\Normalize; use BowlOfSoup\NormalizerBundle\Annotation\Normalize; +use BowlOfSoup\NormalizerBundle\Annotation\Translate; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; use BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor; use BowlOfSoup\NormalizerBundle\Service\Normalizer; +use Symfony\Contracts\Translation\TranslatorInterface; class PropertyNormalizer extends AbstractNormalizer { @@ -16,9 +18,10 @@ class PropertyNormalizer extends AbstractNormalizer public function __construct( ClassExtractor $classExtractor, + TranslatorInterface $translator, PropertyExtractor $propertyExtractor ) { - parent::__construct($classExtractor); + parent::__construct($classExtractor, $translator); $this->propertyExtractor = $propertyExtractor; } @@ -41,7 +44,7 @@ public function normalize( $classProperties = $this->propertyExtractor->getProperties($object); $normalizedProperties = []; foreach ($classProperties as $classProperty) { - $propertyAnnotations = $this->getPropertyAnnotations($objectName, $classProperty); + $propertyAnnotations = $this->getPropertyAnnotations($objectName, $classProperty, Normalize::class); if (empty($propertyAnnotations)) { continue; } @@ -59,18 +62,15 @@ public function normalize( return $normalizedProperties; } - private function getPropertyAnnotations(string $objectName, \ReflectionProperty $classProperty): array + private function getPropertyAnnotations(string $objectName, \ReflectionProperty $classProperty, string $annotationClass): array { $propertyName = $classProperty->getName(); - if (isset($this->annotationCache[PropertyExtractor::TYPE][$objectName][$propertyName])) { - $propertyAnnotations = $this->annotationCache[PropertyExtractor::TYPE][$objectName][$propertyName]; + if (isset($this->annotationCache[$annotationClass][PropertyExtractor::TYPE][$objectName][$propertyName])) { + $propertyAnnotations = $this->annotationCache[$annotationClass][PropertyExtractor::TYPE][$objectName][$propertyName]; } else { - $propertyAnnotations = $this->propertyExtractor->extractPropertyAnnotations( - $classProperty, - new Normalize([]) - ); - $this->annotationCache[PropertyExtractor::TYPE][$objectName][$propertyName] = $propertyAnnotations; + $propertyAnnotations = $this->propertyExtractor->extractPropertyAnnotations($classProperty, $annotationClass); + $this->annotationCache[$annotationClass][PropertyExtractor::TYPE][$objectName][$propertyName] = $propertyAnnotations; } return $propertyAnnotations; @@ -96,6 +96,9 @@ private function normalizeProperty( continue; } + $translateAnnotations = $this->getPropertyAnnotations(get_class($object), $property, Translate::class); + $translationAnnotation = $this->getTranslationAnnotation($translateAnnotations); + $propertyName = $property->getName(); $propertyValue = $this->propertyExtractor->getPropertyValue($object, $property); @@ -128,6 +131,10 @@ private function normalizeProperty( } $propertyValue = (is_array($propertyValue) && empty($propertyValue) ? null : $propertyValue); + if (null !== $translationAnnotation) { + $propertyValue = $this->translateValue($propertyValue, $translationAnnotation); + } + $normalizedProperties[$propertyName] = $propertyValue; } diff --git a/tests/DummyTranslator.php b/tests/DummyTranslator.php new file mode 100644 index 0000000..92b6605 --- /dev/null +++ b/tests/DummyTranslator.php @@ -0,0 +1,15 @@ +propertyExtractor ?? new PropertyExtractor(new AnnotationReader()); $methodExtractor = $this->methodExtractor ?? new MethodExtractor(new AnnotationReader()); $classExtractor = $this->classExtractor ?? new ClassExtractor(new AnnotationReader()); + $this->translator = $this->translator ?? new DummyTranslator(); - $propertyNormalizer = $this->propertyNormalizer ?? new PropertyNormalizer($classExtractor, $propertyExtractor); - $methodNormalizer = $this->methodNormalizer ?? new MethodNormalizer($classExtractor, $methodExtractor); + $propertyNormalizer = $this->propertyNormalizer ?? new PropertyNormalizer($classExtractor, $this->translator, $propertyExtractor); + $methodNormalizer = $this->methodNormalizer ?? new MethodNormalizer($classExtractor, $this->translator, $methodExtractor); return new Normalizer($classExtractor, $propertyNormalizer, $methodNormalizer); } diff --git a/tests/Service/NormalizerTest.php b/tests/Service/NormalizerTest.php index 5ca19c7..d7c7159 100644 --- a/tests/Service/NormalizerTest.php +++ b/tests/Service/NormalizerTest.php @@ -454,6 +454,13 @@ public function testNormalizeMethodWithObjectAndAnEmptyValue(): void ); } + public function testNormalizeMethodWithTranslation(): void + { + $person = $this->getDummyDataSet(); + + $this->normalizer->normalize($person, 'translation'); + } + private function getDummyDataSet(): Person { $groupCollection = new ArrayCollection(); diff --git a/tests/assets/Person.php b/tests/assets/Person.php index 888bc1a..b0a2ef7 100644 --- a/tests/assets/Person.php +++ b/tests/assets/Person.php @@ -487,4 +487,13 @@ private function thisHoldsNoValue(): string { return ''; } + + /** + * @Bos\Normalize(group={"translation"}) + * @Bos\Translate() + */ + protected function translateMeThis(): string + { + return 'some value'; + } } From 9303c0fe7f3d4877c4e0dc1fcc31756bb03e3c2a Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 9 May 2020 08:33:41 +0200 Subject: [PATCH 3/9] ISSUE-25: Mock translator interface. --- tests/DummyTranslator.php | 15 --------------- tests/NormalizerTestTrait.php | 13 ++++++++++++- 2 files changed, 12 insertions(+), 16 deletions(-) delete mode 100644 tests/DummyTranslator.php diff --git a/tests/DummyTranslator.php b/tests/DummyTranslator.php deleted file mode 100644 index 92b6605..0000000 --- a/tests/DummyTranslator.php +++ /dev/null @@ -1,15 +0,0 @@ -propertyExtractor ?? new PropertyExtractor(new AnnotationReader()); $methodExtractor = $this->methodExtractor ?? new MethodExtractor(new AnnotationReader()); $classExtractor = $this->classExtractor ?? new ClassExtractor(new AnnotationReader()); - $this->translator = $this->translator ?? new DummyTranslator(); + + /** @var \PHPUnit\Framework\MockObject\MockBuilder $translationMockBuilder */ + $translationMockBuilder = $this->getMockBuilder(TranslatorInterface::class); + $translationMockBuilder->disableOriginalConstructor(); + + $this->translator = $translationMockBuilder + ->onlyMethods(['trans']) + ->getMock(); + $this->translator + ->method('trans') + ->willReturn('translatedValue'); $propertyNormalizer = $this->propertyNormalizer ?? new PropertyNormalizer($classExtractor, $this->translator, $propertyExtractor); $methodNormalizer = $this->methodNormalizer ?? new MethodNormalizer($classExtractor, $this->translator, $methodExtractor); From 0b6e195fc2faf3ae11224a0f2ba827cdc3d22ca1 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 9 May 2020 08:35:39 +0200 Subject: [PATCH 4/9] ISSUE-25: restore 3.4. --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index bfef5aa..1947371 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ "doctrine/annotations": "1.*", "doctrine/cache": "~1.0", "php": ">=7.2", - "symfony/framework-bundle": "~4.4|~5.0", - "symfony/translation": "~4.4|~5.0" + "symfony/framework-bundle": "~3.4|~4.4|~5.0", + "symfony/translation": "~3.4|~4.4|~5.0" }, "require-dev": { "doctrine/common": "~2.12", From 1600544bc342abe6e10826db9058ea1e24045686 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 9 May 2020 08:47:47 +0200 Subject: [PATCH 5/9] ISSUE-25: Remove dummy translator from classmap. --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 1947371..13a4593 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,6 @@ "autoload": { "psr-4": { "BowlOfSoup\\NormalizerBundle\\": "src/" }, "classmap": [ - "tests/DummyTranslator.php", "tests/NormalizerTestTrait.php" ] }, From 9735743193965240da45392fee2fe37c88acd69a Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 9 May 2020 08:52:25 +0200 Subject: [PATCH 6/9] ISSUE-25: Because of TranslatorInterface, only support 4.4 and up. --- .travis.yml | 6 ------ composer.json | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b75277..8a4ef22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,6 @@ cache: matrix: include: - - php: 7.2 - env: SYMFONY_VERSION=3.4.* - - php: 7.3 - env: SYMFONY_VERSION=3.4.* - - php: 7.4 - env: SYMFONY_VERSION=3.4.* - php: 7.2 env: SYMFONY_VERSION=4.4.* - php: 7.3 diff --git a/composer.json b/composer.json index 13a4593..6b33475 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ "doctrine/annotations": "1.*", "doctrine/cache": "~1.0", "php": ">=7.2", - "symfony/framework-bundle": "~3.4|~4.4|~5.0", - "symfony/translation": "~3.4|~4.4|~5.0" + "symfony/framework-bundle": "~4.4|~5.0", + "symfony/translation": "~4.4|~5.0" }, "require-dev": { "doctrine/common": "~2.12", From 214479c52ac7003a9eb47b3a45fa62e429f091b3 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Mon, 11 May 2020 14:21:32 +0200 Subject: [PATCH 7/9] Bundle installation + exception on normalizing wrong data types. --- README.md | 15 +++++++++------ src/Service/Normalizer.php | 5 ++++- tests/Service/NormalizerTest.php | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 94896ea..3e8b4b4 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,21 @@ Installation ------------ composer require bowlofsoup/normalizer-bundle +Add the bundle to your `config/bundles.php` file + + BowlOfSoup\NormalizerBundle\BowlOfSoupNormalizerBundle::class => ['all' => true], + Bowl Of Soup Normalizer ======================= - Normalizes properties and methods (public, protected, private) - Serialized normalized content -- Works with Symfony and Doctrine as its ORM. Can handle Doctrine proxies. -- Circular reference check: Handles circular reference by detecting it and returning content of the objects getId() method. -- Object caching: If a getId() method is implemented for an object it will cache the normalized object per normalize command. +- Works with Symfony and Doctrine as its ORM. Can handle Doctrine proxies +- Circular reference check: Handles circular reference by detecting it and returning content of the objects getId() method +- Object caching: If a getId() method is implemented for an object it will cache the normalized object per normalize command - Annotation caching, this means speed! - - The annotations for an object are cached. This means not parsing annotations multiple times for the same object. per flow (per normalize command). - - In Symfony prod mode, annotations are cached completely (after first run). + - The annotations for an object are cached. This means not parsing annotations multiple times for the same object. per flow (per normalize command) + - In Symfony prod mode, annotations are cached completely (after first run) The main features are described in the corresponding annotations. @@ -32,7 +36,6 @@ normalization and encoding normalized data. You can call each step separately (normalize, encode) or directly serialize an object. - # Serializer Annotations in your model diff --git a/src/Service/Normalizer.php b/src/Service/Normalizer.php index 483352c..ea7d995 100644 --- a/src/Service/Normalizer.php +++ b/src/Service/Normalizer.php @@ -2,6 +2,7 @@ namespace BowlOfSoup\NormalizerBundle\Service; +use BowlOfSoup\NormalizerBundle\Exception\BosNormalizerException; use BowlOfSoup\NormalizerBundle\Model\ObjectCache; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; use BowlOfSoup\NormalizerBundle\Service\Normalize\MethodNormalizer; @@ -49,8 +50,10 @@ public function normalize($data, string $group = null): array foreach ($data as $item) { $normalizedData[] = $this->normalize($item, $group); } - } else { + } else if (is_object($data)) { $normalizedData = $this->normalizeObject($data, $group); + } else { + throw new BosNormalizerException('Can only normalize an object or an array of objects. Input contains: ' . gettype($data)); } $this->cleanUp(); diff --git a/tests/Service/NormalizerTest.php b/tests/Service/NormalizerTest.php index d7c7159..7feea21 100644 --- a/tests/Service/NormalizerTest.php +++ b/tests/Service/NormalizerTest.php @@ -48,6 +48,24 @@ public function testNormalizeSuccess(): void ArraySubset::assert($result, $expectedResult); } + /** + * @testdox Normalize an integer, but only objects or an array of objects are allowed. + */ + public function testNormalizeInvalidDataType() + { + $this->expectException(BosNormalizerException::class); + $this->expectExceptionMessage('Can only normalize an object or an array of objects. Input contains: integer'); + + $this->normalizer->normalize([ + [ + 'value' => 123, + ], + [ + 'foo' => 'bar', + ], + ]); + } + /** * @testdox Normalize array of objects, full happy path no type property, still callback */ From 0ba1408665384f06ba60b949430d4c9794950267 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Mon, 11 May 2020 14:22:32 +0200 Subject: [PATCH 8/9] ISSUE-25: Translation in the README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3e8b4b4..2953bde 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ Bowl Of Soup Normalizer - Annotation caching, this means speed! - The annotations for an object are cached. This means not parsing annotations multiple times for the same object. per flow (per normalize command) - In Symfony prod mode, annotations are cached completely (after first run) +- Symfony translations + - Indicate domain (translation filename) and locale in annotations + - Does not support formatting with ICU MessageFormat (yet), so no parameters The main features are described in the corresponding annotations. From 44b6afd224dbf9deb4a6b3d52b2fdf143ba05d9f Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Mon, 11 May 2020 16:06:40 +0200 Subject: [PATCH 9/9] ISSUE-25: Prevent infinite loop on translating + improved coverage. --- src/Service/Normalize/AbstractNormalizer.php | 3 ++- tests/Service/NormalizerTest.php | 11 +++++++++-- tests/assets/Person.php | 5 ++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Service/Normalize/AbstractNormalizer.php b/src/Service/Normalize/AbstractNormalizer.php index a156072..8397a1f 100644 --- a/src/Service/Normalize/AbstractNormalizer.php +++ b/src/Service/Normalize/AbstractNormalizer.php @@ -250,7 +250,8 @@ protected function getTranslationAnnotation(array $translateAnnotations, $emptyG } // Annotation found, but no explicit group. Try again with no group. if (null === $translationAnnotation) { - return $this->getTranslationAnnotation($translateAnnotations, true); + // Don't try again if get with no group given to prevent + return (!$emptyGroup) ? $this->getTranslationAnnotation($translateAnnotations, true) : null; } return $translationAnnotation; diff --git a/tests/Service/NormalizerTest.php b/tests/Service/NormalizerTest.php index 7feea21..10d542d 100644 --- a/tests/Service/NormalizerTest.php +++ b/tests/Service/NormalizerTest.php @@ -472,11 +472,18 @@ public function testNormalizeMethodWithObjectAndAnEmptyValue(): void ); } - public function testNormalizeMethodWithTranslation(): void + public function testNormalizeWithTranslation(): void { $person = $this->getDummyDataSet(); + $person->setGender('male'); + + $result = $this->normalizer->normalize($person, 'translation'); - $this->normalizer->normalize($person, 'translation'); + $this->assertSame([ + 'id' => 123, + 'gender' => 'translatedValue', + 'translateMeThis' => 'translatedValue', + ], $result); } private function getDummyDataSet(): Person diff --git a/tests/assets/Person.php b/tests/assets/Person.php index b0a2ef7..34bd40b 100644 --- a/tests/assets/Person.php +++ b/tests/assets/Person.php @@ -21,7 +21,8 @@ class Person /** * @var int * - * @Bos\Normalize(group={"default"}) + * @Bos\Normalize(group={"default", "translation"}) + * @Bos\Translate(group={"translation"}) */ private $id; @@ -50,6 +51,8 @@ class Person * @var string * * @Bos\Normalize() + * @Bos\Normalize(group={"translation"}) + * @Bos\Translate(group={"translation"}) */ private $gender;