Skip to content

Commit

Permalink
Merge pull request #33 from BowlOfSoup/ISSUE-25
Browse files Browse the repository at this point in the history
Issue 25 - Add support for Symfony translations
  • Loading branch information
BowlOfSoup authored May 11, 2020
2 parents 6c1f694 + 5f9a1a1 commit 3872ae0
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 39 deletions.
6 changes: 0 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 0 additions & 8 deletions src/Annotation/AbstractAnnotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Annotation/Normalize.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -87,4 +90,9 @@ public function getMaxDepth(): ?int
{
return $this->maxDepth;
}

public function getType(): string
{
return $this->type;
}
}
8 changes: 8 additions & 0 deletions src/Annotation/Serialize.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -33,4 +36,9 @@ public function getWrapElement(): string
{
return $this->wrapElement;
}

public function getType(): string
{
return $this->type;
}
}
44 changes: 44 additions & 0 deletions src/Annotation/Translate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace BowlOfSoup\NormalizerBundle\Annotation;

/**
* @Annotation
* @Target({"PROPERTY","METHOD"})
*/
class Translate extends AbstractAnnotation
{
/** @var array */
private $supportedProperties = [
'group' => ['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;
}
}
1 change: 1 addition & 0 deletions src/EventListener/RegisterAnnotationsListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
}
}
Expand Down
53 changes: 52 additions & 1 deletion src/Service/Normalize/AbstractNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -220,6 +227,50 @@ 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) {
// Don't try again if get with no group given to prevent
return (!$emptyGroup) ? $this->getTranslationAnnotation($translateAnnotations, true) : null;
}

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);
Expand Down
27 changes: 17 additions & 10 deletions src/Service/Normalize/MethodNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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;
}

Expand Down
27 changes: 17 additions & 10 deletions src/Service/Normalize/PropertyNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit 3872ae0

Please sign in to comment.