Skip to content

Commit

Permalink
Merge pull request #4 from sunrise-php/v1.2.0
Browse files Browse the repository at this point in the history
v1.2.0
  • Loading branch information
fenric authored Jul 21, 2021
2 parents c5ac270 + ffb931c commit 5a77c75
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 6 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
## Installation

```bash
composer require 'sunrise/hydrator:^1.0'
composer require 'sunrise/hydrator:^1.2'
```

## How to use?
Expand All @@ -39,6 +39,7 @@ $payload = [
'value' => 'bar',
],
],
'enumerableValue' => 'FOO',
];
```

Expand All @@ -59,6 +60,7 @@ final class FooDto implements HydrableObjectInterface
public DateTimeImmutable $dateTime;
public BarDto $barDto;
public BarDtoCollection $barDtoCollection;
public TestEnumerableObject $enumerableValue;
}
```

Expand All @@ -80,6 +82,15 @@ final class BarDtoCollection extends HydrableObjectCollection
}
```

```php
use Sunrise\Hydrator\EnumerableObject;

final class TestEnumerableObject extends EnumerableObject
{
public const FOO = 'foo';
}
```

```php
use Sunrise\Hydrator\Hydrator;

Expand Down
44 changes: 44 additions & 0 deletions src/Hydrator/EnumerableObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

/**
* It's free open-source software released under the MIT License.
*
* @author Anatoly Fenric <anatoly@fenric.ru>
* @copyright Copyright (c) 2021, Anatoly Fenric
* @license https://github.com/sunrise-php/hydrator/blob/master/LICENSE
* @link https://github.com/sunrise-php/hydrator
*/

namespace Sunrise\Hydrator;

/**
* EnumerableObject
*/
abstract class EnumerableObject implements EnumerableObjectInterface
{

/**
* The enum value
*
* @var mixed
*/
protected $value;

/**
* Constructor of the class
*
* @param mixed $value
*/
public function __construct($value)
{
$this->value = $value;
}

/**
* {@inheritdoc}
*/
public function getValue()
{
return $this->value;
}
}
26 changes: 26 additions & 0 deletions src/Hydrator/EnumerableObjectInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types=1);

/**
* It's free open-source software released under the MIT License.
*
* @author Anatoly Fenric <anatoly@fenric.ru>
* @copyright Copyright (c) 2021, Anatoly Fenric
* @license https://github.com/sunrise-php/hydrator/blob/master/LICENSE
* @link https://github.com/sunrise-php/hydrator
*/

namespace Sunrise\Hydrator;

/**
* EnumerableObjectInterface
*/
interface EnumerableObjectInterface
{

/**
* Gets the enum value
*
* @return mixed
*/
public function getValue();
}
63 changes: 58 additions & 5 deletions src/Hydrator/Hydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
* Import functions
*/
use function array_key_exists;
use function class_exists;
use function constant;
use function defined;
use function in_array;
use function is_array;
use function is_scalar;
Expand All @@ -49,8 +52,10 @@ class Hydrator implements HydratorInterface
*/
public function __construct()
{
$this->annotationReader = /** @scrutinizer ignore-deprecated */ new SimpleAnnotationReader();
$this->annotationReader->addNamespace(Annotation::class);
if (class_exists(SimpleAnnotationReader::class)) {
$this->annotationReader = /** @scrutinizer ignore-deprecated */ new SimpleAnnotationReader();
$this->annotationReader->addNamespace(Annotation::class);
}
}

/**
Expand All @@ -77,13 +82,15 @@ public function hydrate(HydrableObjectInterface $object, array $data) : Hydrable
$class = new ReflectionClass($object);
$properties = $class->getProperties();
foreach ($properties as $property) {
$property->setAccessible(true);

if ($property->isStatic()) {
continue;
}

$key = $property->getName();

if (!array_key_exists($key, $data)) {
if (isset($this->annotationReader) && !array_key_exists($key, $data)) {
$alias = $this->annotationReader->getPropertyAnnotation($property, Annotation\Alias::class);
if ($alias instanceof Annotation\Alias) {
$key = $alias->value;
Expand All @@ -110,8 +117,6 @@ public function hydrate(HydrableObjectInterface $object, array $data) : Hydrable
));
}

$property->setAccessible(true);

$this->hydrateProperty($object, $class, $property, $property->getType(), $data[$key]);
}

Expand Down Expand Up @@ -162,6 +167,11 @@ private function hydrateProperty(
return;
}

if (is_subclass_of($type->getName(), EnumerableObjectInterface::class)) {
$this->hydratePropertyWithEnumerableValue($object, $class, $property, $type, $value);
return;
}

if (is_subclass_of($type->getName(), HydrableObjectInterface::class)) {
$this->hydratePropertyWithOneToOneAssociation($object, $class, $property, $type, $value);
return;
Expand Down Expand Up @@ -298,6 +308,49 @@ private function hydratePropertyWithArray(
$property->setValue($object, $array);
}

/**
* Hydrates the given property with the given enumerable value
*
* @param HydrableObjectInterface $object
* @param ReflectionClass $class
* @param ReflectionProperty $property
* @param ReflectionNamedType $type
* @param mixed $value
*
* @return void
*
* @throws Exception\InvalidValueException
* If the given value isn't valid.
*/
private function hydratePropertyWithEnumerableValue(
HydrableObjectInterface $object,
ReflectionClass $class,
ReflectionProperty $property,
ReflectionNamedType $type,
$value
) : void {
if (!is_string($value)) {
throw new Exception\InvalidValueException(sprintf(
'The <%s.%s> property only accepts a string.',
$class->getShortName(),
$property->getName(),
));
}

$enum = $type->getName();
$constant = sprintf('%s::%s', $enum, $value);
if (!defined($constant)) {
throw new Exception\InvalidValueException(sprintf(
'The <%s.%s> property only accepts one of the <%s> enum values.',
$class->getShortName(),
$property->getName(),
(new ReflectionClass($enum))->getShortName(),
));
}

$property->setValue($object, new $enum(constant($constant)));
}

/**
* Hydrates the given property with the given date-time value
*
Expand Down
18 changes: 18 additions & 0 deletions tests/Hydrator/Fixture/TestEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace Sunrise\Hydrator\Tests\Fixture;

/**
* Import classes
*/
use Sunrise\Hydrator\EnumerableObject;

/**
* TestEnum
*/
final class TestEnum extends EnumerableObject
{
public const A = 'A:value';
public const B = 'B:value';
public const C = 'C:value';
}
16 changes: 16 additions & 0 deletions tests/Hydrator/Fixture/TestEnumDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Sunrise\Hydrator\Tests\Fixture;

/**
* Import classes
*/
use Sunrise\Hydrator\HydrableObjectInterface;

/**
* TestEnumDto
*/
final class TestEnumDto implements HydrableObjectInterface
{
public TestEnum $foo;
}
28 changes: 28 additions & 0 deletions tests/Hydrator/HydratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,34 @@ public function testInvalidValueExceptionForOneToManyProperty($nonArrayValue) :
]);
}

public function testEnumerableValue() : void
{
$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 'A']);
$this->assertSame('A:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 'B']);
$this->assertSame('B:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 'C']);
$this->assertSame('C:value', $object->foo->getValue());
}

public function testUnknownEnumerableValue() : void
{
$this->expectException(Exception\InvalidValueException::class);
$this->expectExceptionMessage('The <TestEnumDto.foo> property only accepts one of the <TestEnum> enum values.');

(new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 'D']);
}

public function testInvalidEnumerableValue() : void
{
$this->expectException(Exception\InvalidValueException::class);
$this->expectExceptionMessage('The <TestEnumDto.foo> property only accepts a string.');

(new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => []]);
}

/**
* @return array<array>
*/
Expand Down

0 comments on commit 5a77c75

Please sign in to comment.