Skip to content

Commit

Permalink
Add support for enum in symfony property accessor hydrator
Browse files Browse the repository at this point in the history
  • Loading branch information
akalineskou committed Oct 1, 2022
1 parent bb4ed62 commit 6ceb6b8
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 1 deletion.
31 changes: 31 additions & 0 deletions fixtures/Entity/DummyWithEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/*
* This file is part of the Alice package.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Nelmio\Alice\Entity;

use Nelmio\Alice\Entity\Enum\DummyEnum;

class DummyWithEnum
{
private DummyEnum $dummyEnum;

public function setDummyEnum(DummyEnum $dummyEnum): void
{
$this->dummyEnum = $dummyEnum;
}

public function getDummyEnum(): DummyEnum
{
return $this->dummyEnum;
}
}
20 changes: 20 additions & 0 deletions fixtures/Entity/Enum/DummyEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the Alice package.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Nelmio\Alice\Entity\Enum;

enum DummyEnum: string
{
case Case1 = 'case1';
case Case2 = 'case2';
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Nelmio\Alice\Generator\Hydrator\Property;

use function enum_exists;
use Nelmio\Alice\Definition\Object\SimpleObject;
use Nelmio\Alice\Definition\Property;
use Nelmio\Alice\Generator\GenerationContext;
Expand All @@ -24,6 +25,10 @@
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\InaccessiblePropertyException;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\InvalidArgumentException;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\NoSuchPropertyException;
use ReflectionEnum;
use ReflectionException;
use ReflectionProperty;
use ReflectionType;
use Symfony\Component\PropertyAccess\Exception\AccessException as SymfonyAccessException;
use Symfony\Component\PropertyAccess\Exception\ExceptionInterface as SymfonyPropertyAccessException;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException as SymfonyInvalidArgumentException;
Expand Down Expand Up @@ -62,6 +67,14 @@ public function hydrate(ObjectInterface $object, Property $property, GenerationC
} catch (SymfonyAccessException $exception) {
throw HydrationExceptionFactory::createForInaccessibleProperty($object, $property, 0, $exception);
} catch (SymfonyInvalidArgumentException $exception) {
// as a fallback check if the property might be an enum
if (
null !== ($enumType = self::getEnumType($instance, $property))
&& null !== $newProperty = self::castValueToEnum($enumType, $property)
) {
return $this->hydrate($object, $newProperty, $context);
}

throw HydrationExceptionFactory::createForInvalidProperty($object, $property, 0, $exception);
} catch (SymfonyPropertyAccessException $exception) {
throw HydrationExceptionFactory::create($object, $property, 0, $exception);
Expand All @@ -71,4 +84,37 @@ public function hydrate(ObjectInterface $object, Property $property, GenerationC

return new SimpleObject($object->getId(), $instance);
}

private static function getEnumType($instance, Property $property): ?ReflectionType
{
try {
$enumType = (new ReflectionProperty($instance, $property->getName()))->getType();
} catch (ReflectionException) {
// property might not exist
return null;
}

if (null === $enumType) {
// might not have a type
return null;
}

if (!enum_exists($enumType->getName())) {
// might not be an enum
return null;
}

return $enumType;
}

private static function castValueToEnum(ReflectionType $enumType, Property $property): ?Property
{
foreach ((new ReflectionEnum($enumType->getName()))->getCases() as $reflectionCase) {
if ($property->getValue() === $reflectionCase->getValue()->value ?? $reflectionCase->getValue()->name) {
return $property->withValue($reflectionCase->getValue());
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Nelmio\Alice\Definition\Property;
use Nelmio\Alice\Dummy as NelmioDummy;
use Nelmio\Alice\Entity\DummyWithDate;
use Nelmio\Alice\Entity\DummyWithEnum;
use Nelmio\Alice\Entity\Enum\DummyEnum;
use Nelmio\Alice\Entity\Hydrator\Dummy;
use Nelmio\Alice\Generator\GenerationContext;
use Nelmio\Alice\Generator\Hydrator\PropertyHydratorInterface;
Expand Down Expand Up @@ -49,7 +51,7 @@ class SymfonyPropertyAccessorHydratorTest extends TestCase
* @var PropertyAccessorInterface
*/
private $propertyAccessor;

protected function setUp(): void
{
$this->propertyAccessor = new PropertyAccessor();
Expand Down Expand Up @@ -148,6 +150,35 @@ public function testThrowsInvalidArgumentExceptionIfInvalidTypeIsGiven(): void
}
}

public function testReturnsHydratedObjectWithEnum(): void
{
$object = new SimpleObject('dummy', new DummyWithEnum());
$property = new Property('dummyEnum', 'case1');

$result = $this->hydrator->hydrate($object, $property, new GenerationContext());

static::assertEquals(DummyEnum::Case1, $result->getInstance()->getDummyEnum());
}

public function testThrowsInvalidArgumentExceptionIfEnumCaseIsNotFound(): void
{
try {
$object = new SimpleObject('dummy', new DummyWithEnum());
$property = new Property('dummyEnum', 'case3');

$this->hydrator->hydrate($object, $property, new GenerationContext());

static::fail('Expected exception to be thrown.');
} catch (InvalidArgumentException $exception) {
static::assertEquals(
'Invalid value given for the property "dummyEnum" of the object "dummy" (class: Nelmio\Alice\Entity\DummyWithEnum).',
$exception->getMessage()
);
static::assertEquals(0, $exception->getCode());
static::assertNotNull($exception->getPrevious());
}
}

public function testCatchesAnySymfonyPropertyAccessorToThrowAnHydratorException(): void
{
try {
Expand Down

0 comments on commit 6ceb6b8

Please sign in to comment.