From df3e80c4188bc2563ec721430d05f7509790fb25 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 29 Jul 2024 18:11:17 +0500 Subject: [PATCH 01/27] Add support for collections --- docs/guide/en/typecasting.md | 3 +- src/Attribute/Parameter/Collection.php | 24 +++++++ .../Parameter/CollectionResolver.php | 33 ++++++++++ .../ParameterAttributeResolveContext.php | 7 +++ .../ParameterAttributesHandler.php | 4 +- src/Hydrator.php | 2 +- tests/Attribute/Parameter/CollectionTest.php | 63 +++++++++++++++++++ tests/Support/Classes/Chart/Chart.php | 28 +++++++++ tests/Support/Classes/Chart/ChartSet.php | 22 +++++++ tests/Support/Classes/Chart/Coordinates.php | 25 ++++++++ tests/Support/Classes/Chart/Point.php | 25 ++++++++ 11 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 src/Attribute/Parameter/Collection.php create mode 100644 src/Attribute/Parameter/CollectionResolver.php create mode 100644 tests/Attribute/Parameter/CollectionTest.php create mode 100644 tests/Support/Classes/Chart/Chart.php create mode 100644 tests/Support/Classes/Chart/ChartSet.php create mode 100644 tests/Support/Classes/Chart/Coordinates.php create mode 100644 tests/Support/Classes/Chart/Point.php diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index 8f39370..86207e2 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -20,9 +20,10 @@ $lock = $hydrator->create(Lock::class, ['name' => 'The lock', 'isLocked' => 1]); You can adjust type-casting by passing a type-caster to the hydrator: ```php +use Yiisoft\Hydrator\Hydrator; use Yiisoft\Hydrator\TypeCaster\CompositeTypeCaster; use Yiisoft\Hydrator\TypeCaster\PhpNativeTypeCaster; -use Yiisoft\Hydrator\TypeCaster\HydratorTypeCaster +use Yiisoft\Hydrator\TypeCaster\HydratorTypeCaster; $typeCaster = new CompositeTypeCaster( new PhpNativeTypeCaster(), diff --git a/src/Attribute/Parameter/Collection.php b/src/Attribute/Parameter/Collection.php new file mode 100644 index 0000000..c2eceba --- /dev/null +++ b/src/Attribute/Parameter/Collection.php @@ -0,0 +1,24 @@ +isResolved()) { + return Result::fail(); + } + + $collection = []; + foreach ($context->getResolvedValue() as $item) { + $collection[] = $context->getHydrator()->create($attribute->className, $item); + } + + return Result::success($collection); + } +} diff --git a/src/AttributeHandling/ParameterAttributeResolveContext.php b/src/AttributeHandling/ParameterAttributeResolveContext.php index 3567b67..7681051 100644 --- a/src/AttributeHandling/ParameterAttributeResolveContext.php +++ b/src/AttributeHandling/ParameterAttributeResolveContext.php @@ -7,6 +7,7 @@ use ReflectionParameter; use ReflectionProperty; use Yiisoft\Hydrator\DataInterface; +use Yiisoft\Hydrator\HydratorInterface; use Yiisoft\Hydrator\Result; /** @@ -23,6 +24,7 @@ public function __construct( private ReflectionParameter|ReflectionProperty $parameter, private Result $resolveResult, private DataInterface $data, + private HydratorInterface $hydrator, ) { } @@ -66,4 +68,9 @@ public function getData(): DataInterface { return $this->data; } + + public function getHydrator(): HydratorInterface + { + return $this->hydrator; + } } diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 10158ed..5520261 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -13,6 +13,7 @@ use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeResolverInterface; use Yiisoft\Hydrator\ArrayData; use Yiisoft\Hydrator\DataInterface; +use Yiisoft\Hydrator\HydratorInterface; use Yiisoft\Hydrator\Result; /** @@ -22,6 +23,7 @@ final class ParameterAttributesHandler { public function __construct( private AttributeResolverFactoryInterface $attributeResolverFactory, + private HydratorInterface $hydrator, ) { } @@ -60,7 +62,7 @@ public function handle( ); } - $context = new ParameterAttributeResolveContext($parameter, $resolveResult, $data); + $context = new ParameterAttributeResolveContext($parameter, $resolveResult, $data, $this->hydrator); $tryResolveResult = $resolver->getParameterValue($attribute, $context); if ($tryResolveResult->isResolved()) { diff --git a/src/Hydrator.php b/src/Hydrator.php index 49c7041..ce73c78 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -56,7 +56,7 @@ public function __construct( $attributeResolverFactory ??= new ReflectionAttributeResolverFactory(); $this->dataAttributesHandler = new DataAttributesHandler($attributeResolverFactory); - $this->parameterAttributesHandler = new ParameterAttributesHandler($attributeResolverFactory); + $this->parameterAttributesHandler = new ParameterAttributesHandler($attributeResolverFactory, $this); $this->objectFactory = $objectFactory ?? new ReflectionObjectFactory(); diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php new file mode 100644 index 0000000..38ecd09 --- /dev/null +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -0,0 +1,63 @@ +create( + ChartSet::class, + [ + 'charts' => [ + [ + 'points' => [ + ['coordinates' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0]], + ['coordinates' => ['x' => -12, 'y' => 12], 'rgb' => [0, -2, 257]], + ], + ], + [ + 'points' => [ + ['coordinates' => ['x' => -1, 'y' => 1], 'rgb' => [0, 0, 0]], + ['coordinates' => ['x' => -2, 'y' => 2], 'rgb' => [255, 255, 255]], + ], + ], + [ + 'points' => [ + ['coordinates' => ['x' => -13, 'y' => 13], 'rgb' => [-3, 258, 0]], + ['coordinates' => ['x' => -14, 'y' => 14], 'rgb' => [0, -4, 259]], + ], + ], + ], + ], + ); + + $this->assertEquals( + new ChartSet([ + new Chart([ + new Point(new Coordinates(-11, 11), [-1, 256, 0]), + new Point(new Coordinates(-12, 12), [0, -2, 257]), + ]), + new Chart([ + new Point(new Coordinates(-1, 1), [0, 0, 0]), + new Point(new Coordinates(-2, 2), [255, 255, 255]), + ]), + new Chart([ + new Point(new Coordinates(-13, 13), [-3, 258, 0]), + new Point(new Coordinates(-14, 14), [0, -4, 259]), + ]), + ]), + $object, + ); + } +} diff --git a/tests/Support/Classes/Chart/Chart.php b/tests/Support/Classes/Chart/Chart.php new file mode 100644 index 0000000..b81b187 --- /dev/null +++ b/tests/Support/Classes/Chart/Chart.php @@ -0,0 +1,28 @@ +points; + } + +// public function getPoint(): ?Point +// { +// return $this->point; +// } +} diff --git a/tests/Support/Classes/Chart/ChartSet.php b/tests/Support/Classes/Chart/ChartSet.php new file mode 100644 index 0000000..a94abc2 --- /dev/null +++ b/tests/Support/Classes/Chart/ChartSet.php @@ -0,0 +1,22 @@ +charts; + } +} diff --git a/tests/Support/Classes/Chart/Coordinates.php b/tests/Support/Classes/Chart/Coordinates.php new file mode 100644 index 0000000..4d51bef --- /dev/null +++ b/tests/Support/Classes/Chart/Coordinates.php @@ -0,0 +1,25 @@ +x; + } + + public function getY(): int + { + return $this->y; + } +} diff --git a/tests/Support/Classes/Chart/Point.php b/tests/Support/Classes/Chart/Point.php new file mode 100644 index 0000000..3bcb298 --- /dev/null +++ b/tests/Support/Classes/Chart/Point.php @@ -0,0 +1,25 @@ +coordinates; + } + + public function getRgb(): array + { + return $this->rgb; + } +} From 07d33fbe7100e211c13fad0c3f7ce08478c345f1 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 29 Jul 2024 13:11:30 +0000 Subject: [PATCH 02/27] Apply fixes from StyleCI --- src/Attribute/Parameter/CollectionResolver.php | 3 +-- tests/Support/Classes/Chart/Chart.php | 3 +-- tests/Support/Classes/Chart/ChartSet.php | 3 +-- tests/Support/Classes/Chart/Coordinates.php | 3 +-- tests/Support/Classes/Chart/Point.php | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index cc1d37f..7669a47 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -13,8 +13,7 @@ final class CollectionResolver implements ParameterAttributeResolverInterface public function getParameterValue( ParameterAttributeInterface $attribute, ParameterAttributeResolveContext $context, - ): Result - { + ): Result { if (!$attribute instanceof Collection) { throw new UnexpectedAttributeException(Collection::class, $attribute); } diff --git a/tests/Support/Classes/Chart/Chart.php b/tests/Support/Classes/Chart/Chart.php index b81b187..05466d9 100644 --- a/tests/Support/Classes/Chart/Chart.php +++ b/tests/Support/Classes/Chart/Chart.php @@ -12,8 +12,7 @@ public function __construct( #[Collection(Point::class)] private array $points = [], // private ?Point $point = null, - ) - { + ) { } public function getPoints(): array diff --git a/tests/Support/Classes/Chart/ChartSet.php b/tests/Support/Classes/Chart/ChartSet.php index a94abc2..d329fdf 100644 --- a/tests/Support/Classes/Chart/ChartSet.php +++ b/tests/Support/Classes/Chart/ChartSet.php @@ -11,8 +11,7 @@ final class ChartSet public function __construct( #[Collection(Chart::class)] private array $charts = [], - ) - { + ) { } public function getCharts(): array diff --git a/tests/Support/Classes/Chart/Coordinates.php b/tests/Support/Classes/Chart/Coordinates.php index 4d51bef..6745518 100644 --- a/tests/Support/Classes/Chart/Coordinates.php +++ b/tests/Support/Classes/Chart/Coordinates.php @@ -9,8 +9,7 @@ final class Coordinates public function __construct( private int $x, private int $y, - ) - { + ) { } public function getX(): int diff --git a/tests/Support/Classes/Chart/Point.php b/tests/Support/Classes/Chart/Point.php index 3bcb298..d7be1e6 100644 --- a/tests/Support/Classes/Chart/Point.php +++ b/tests/Support/Classes/Chart/Point.php @@ -9,8 +9,7 @@ final class Point public function __construct( private Coordinates $coordinates, private array $rgb, - ) - { + ) { } public function getCoordinates(): Coordinates From bc542394850169cd21382c3d72f638aa2b5d3f45 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 31 Jul 2024 12:59:02 +0500 Subject: [PATCH 03/27] Fix unit tests --- .../ParameterAttributesHandler.php | 17 ++++++++++++++++- tests/Attribute/Parameter/LeftTrimTest.php | 1 + tests/Attribute/Parameter/RightTrimTest.php | 1 + tests/Attribute/Parameter/ToDateTimeTest.php | 3 +++ tests/Attribute/Parameter/ToStringTest.php | 1 + tests/Attribute/Parameter/TrimTest.php | 1 + .../ParameterAttributeResolveContextTest.php | 3 ++- .../ParameterAttributesHandlerTest.php | 8 +++++--- 8 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 5520261..018b543 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -23,7 +23,7 @@ final class ParameterAttributesHandler { public function __construct( private AttributeResolverFactoryInterface $attributeResolverFactory, - private HydratorInterface $hydrator, + private ?HydratorInterface $hydrator = null, ) { } @@ -62,6 +62,7 @@ public function handle( ); } + $this->requireHydrator(); $context = new ParameterAttributeResolveContext($parameter, $resolveResult, $data, $this->hydrator); $tryResolveResult = $resolver->getParameterValue($attribute, $context); @@ -72,4 +73,18 @@ public function handle( return $resolveResult; } + + /** + * Ensure that validator is set in paramater attributes handler. + * + * @psalm-assert HydratorInterface $this->hydrator + * + * @throws RuntimeException If hydrator is not set in parameter attributes handler. + */ + private function requireHydrator(): void + { + if ($this->hydrator === null) { + throw new RuntimeException('Hydrator is not set in parameter attributes handler.'); + } + } } diff --git a/tests/Attribute/Parameter/LeftTrimTest.php b/tests/Attribute/Parameter/LeftTrimTest.php index ac832f0..ea5dc99 100644 --- a/tests/Attribute/Parameter/LeftTrimTest.php +++ b/tests/Attribute/Parameter/LeftTrimTest.php @@ -38,6 +38,7 @@ public function testBase(string $expected, LeftTrim $attribute, mixed $value): v TestHelper::getFirstParameter(static fn(?string $a) => null), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue($attribute, $context); diff --git a/tests/Attribute/Parameter/RightTrimTest.php b/tests/Attribute/Parameter/RightTrimTest.php index dbad4b3..8ea738c 100644 --- a/tests/Attribute/Parameter/RightTrimTest.php +++ b/tests/Attribute/Parameter/RightTrimTest.php @@ -38,6 +38,7 @@ public function testBase(string $expected, RightTrim $attribute, mixed $value): TestHelper::getFirstParameter(static fn(?string $a) => null), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue($attribute, $context); diff --git a/tests/Attribute/Parameter/ToDateTimeTest.php b/tests/Attribute/Parameter/ToDateTimeTest.php index ad7ebf6..19e5c64 100644 --- a/tests/Attribute/Parameter/ToDateTimeTest.php +++ b/tests/Attribute/Parameter/ToDateTimeTest.php @@ -75,6 +75,7 @@ public function testBase(DateTimeImmutable $expected, ToDateTime $attribute, mix TestHelper::getFirstParameter(static fn(?DateTimeImmutable $a) => null), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue($attribute, $context); @@ -346,6 +347,7 @@ public function testResultType(string $expected, Closure $closure, mixed $value) TestHelper::getFirstParameter($closure), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue(new ToDateTime(format: 'php:d.m.Y'), $context); @@ -362,6 +364,7 @@ public function testResultTypeWithIntlFormat(string $expected, Closure $closure, TestHelper::getFirstParameter($closure), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue(new ToDateTime(format: 'dd.MM.yyyy'), $context); diff --git a/tests/Attribute/Parameter/ToStringTest.php b/tests/Attribute/Parameter/ToStringTest.php index fc433b4..395623d 100644 --- a/tests/Attribute/Parameter/ToStringTest.php +++ b/tests/Attribute/Parameter/ToStringTest.php @@ -43,6 +43,7 @@ public function testBase(mixed $expected, mixed $value): void TestHelper::getFirstParameter(static fn(string $a) => null), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $attribute->getParameterValue($attribute, $context); diff --git a/tests/Attribute/Parameter/TrimTest.php b/tests/Attribute/Parameter/TrimTest.php index 1fc776c..1809536 100644 --- a/tests/Attribute/Parameter/TrimTest.php +++ b/tests/Attribute/Parameter/TrimTest.php @@ -38,6 +38,7 @@ public function testBase(string $expected, Trim $attribute, mixed $value): void TestHelper::getFirstParameter(static fn(?string $a) => null), Result::success($value), new ArrayData(), + new Hydrator(), ); $result = $resolver->getParameterValue($attribute, $context); diff --git a/tests/AttributeHandling/ParameterAttributeResolveContextTest.php b/tests/AttributeHandling/ParameterAttributeResolveContextTest.php index 9a9ba91..ddd9bb0 100644 --- a/tests/AttributeHandling/ParameterAttributeResolveContextTest.php +++ b/tests/AttributeHandling/ParameterAttributeResolveContextTest.php @@ -8,6 +8,7 @@ use ReflectionClass; use Yiisoft\Hydrator\ArrayData; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; +use Yiisoft\Hydrator\Hydrator; use Yiisoft\Hydrator\Result; final class ParameterAttributeResolveContextTest extends TestCase @@ -21,7 +22,7 @@ public function testBase(): void ))->getProperties()[0]; $data = new ArrayData(['a' => 5, 'b' => 6]); - $context = new ParameterAttributeResolveContext($parameter, Result::success(7), $data); + $context = new ParameterAttributeResolveContext($parameter, Result::success(7), $data, new Hydrator()); $this->assertSame($parameter, $context->getParameter()); $this->assertTrue($context->isResolved()); diff --git a/tests/AttributeHandling/ParameterAttributesHandlerTest.php b/tests/AttributeHandling/ParameterAttributesHandlerTest.php index 9c663ae..2d2c815 100644 --- a/tests/AttributeHandling/ParameterAttributesHandlerTest.php +++ b/tests/AttributeHandling/ParameterAttributesHandlerTest.php @@ -9,6 +9,7 @@ use Yiisoft\Hydrator\AttributeHandling\ParameterAttributesHandler; use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ContainerAttributeResolverFactory; use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ReflectionAttributeResolverFactory; +use Yiisoft\Hydrator\Hydrator; use Yiisoft\Hydrator\Tests\Support\Attribute\ContextViewer; use Yiisoft\Hydrator\Tests\Support\Attribute\ContextViewerResolver; use Yiisoft\Hydrator\Tests\Support\Attribute\CustomValue; @@ -26,7 +27,8 @@ public function testDefaultsHandleParameters(): void new SimpleContainer([ ContextViewerResolver::class => $contextViewerResolver, ]) - ) + ), + new Hydrator(), ); $parameter = TestHelper::getFirstParameter(static fn(#[ContextViewer] int $a) => null); @@ -41,7 +43,7 @@ public function testDefaultsHandleParameters(): void public function testBase(): void { - $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); + $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory(), new Hydrator()); $parameter = TestHelper::getFirstParameter(static fn(#[CustomValue('42')] int $a) => null); @@ -52,7 +54,7 @@ public function testBase(): void public function testNotResolvedAttributeAfterResolved(): void { - $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); + $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory(), new Hydrator()); $parameter = TestHelper::getFirstParameter( static function( From 909a539309a157ecca758f8228ea919d98dc6d26 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 31 Jul 2024 13:11:17 +0500 Subject: [PATCH 04/27] Fix Psalm --- src/Attribute/Parameter/CollectionResolver.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index 7669a47..e3e5745 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -6,6 +6,7 @@ use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; +use Yiisoft\Hydrator\DataInterface; use Yiisoft\Hydrator\Result; final class CollectionResolver implements ParameterAttributeResolverInterface @@ -22,8 +23,17 @@ public function getParameterValue( return Result::fail(); } + $resolvedValue = $context->getResolvedValue(); + if (!is_iterable($resolvedValue)) { + return Result::fail(); + } + $collection = []; foreach ($context->getResolvedValue() as $item) { + if (!is_array($item) && !$item instanceof DataInterface) { + return Result::fail(); + } + $collection[] = $context->getHydrator()->create($attribute->className, $item); } From f04c35705eb0abbc52d5f418208b9388ec0aafd0 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 1 Aug 2024 19:13:41 +0500 Subject: [PATCH 05/27] WIP --- .../ParameterAttributesHandler.php | 2 +- tests/Attribute/Parameter/CollectionTest.php | 109 ++++++++++++++++++ .../ParameterAttributesHandlerTest.php | 11 ++ tests/Support/Classes/Chart/Chart.php | 8 +- tests/Support/Classes/Chart/ChartSet.php | 5 - tests/Support/Classes/Chart/Coordinates.php | 10 -- tests/Support/Classes/Chart/Point.php | 10 -- tests/Support/Classes/Post.php | 14 +++ 8 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 tests/Support/Classes/Post.php diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 018b543..5c94033 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -75,7 +75,7 @@ public function handle( } /** - * Ensure that validator is set in paramater attributes handler. + * Ensure that validator is set in parameter attributes handler. * * @psalm-assert HydratorInterface $this->hydrator * diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 38ecd09..c890fbe 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -4,15 +4,124 @@ namespace Yiisoft\Hydrator\Tests\Attribute\Parameter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use stdClass; +use Yiisoft\Hydrator\ArrayData; +use Yiisoft\Hydrator\Attribute\Parameter\Collection; +use Yiisoft\Hydrator\Attribute\Parameter\CollectionResolver; +use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException; +use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; +use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ContainerAttributeResolverFactory; use Yiisoft\Hydrator\Hydrator; +use Yiisoft\Hydrator\Result; +use Yiisoft\Hydrator\Tests\Support\Attribute\Counter; +use Yiisoft\Hydrator\Tests\Support\Attribute\CounterResolver; use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Chart; use Yiisoft\Hydrator\Tests\Support\Classes\Chart\ChartSet; use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Coordinates; use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Point; +use Yiisoft\Hydrator\Tests\Support\Classes\CounterClass; +use Yiisoft\Hydrator\Tests\Support\Classes\Post; +use Yiisoft\Hydrator\Tests\Support\TestHelper; +use Yiisoft\Test\Support\Container\SimpleContainer; final class CollectionTest extends TestCase { + public function testUnexpectedAttributeException(): void + { + $hydrator = new Hydrator( + attributeResolverFactory: new ContainerAttributeResolverFactory( + new SimpleContainer([ + CounterResolver::class => new CollectionResolver(), + ]), + ), + ); + $object = new CounterClass(); + + $this->expectException(UnexpectedAttributeException::class); + $this->expectExceptionMessage( + 'Expected "' . Collection::class . '", but "' . Counter::class . '" given.' + ); + $hydrator->hydrate($object); + } + + public function testNotResolvedValue(): void + { + $hydrator = new Hydrator(); + $object = new class () { + #[Collection(Chart::class)] + public array $charts = []; + }; + + $hydrator->hydrate($object, ['chart' => []]); + $this->assertEmpty($object->charts); + } + + public function testInvalidValue(): void + { + $hydrator = new Hydrator(); + $object = new class () { + #[Collection(Chart::class)] + public array $charts = []; + }; + + $hydrator->hydrate($object, ['charts' => new stdClass()]); + $this->assertEmpty($object->charts); + } + + public function testInvalidValueItem(): void + { + $hydrator = new Hydrator(); + $object = new class () { + #[Collection(Chart::class)] + public array $charts = []; + }; + + $hydrator->hydrate( + $object, + [ + 'charts' => [ + new stdClass(), + ], + ], + ); + $this->assertEquals($object->charts, [new stdClass()]); + } + + public static function dataBase(): array + { + return [ + [ + new Collection(Post::class), + [ + ['name' => 'Post 1'], + ['name' => 'Post 2', 'description' => 'Description for post 2'], + ], + [ + new Post(name: 'Post 1'), + new Post(name: 'Post 2', description: 'Description for post 2'), + ], + ], + ]; + } + + #[DataProvider('dataBase')] + public function testBase(Collection $attribute, array $value, array $expectedValue): void + { + $resolver = new CollectionResolver(); + $context = new ParameterAttributeResolveContext( + TestHelper::getFirstParameter(static fn(?string $a) => null), + Result::success($value), + new ArrayData(), + new Hydrator(), + ); + $result = $resolver->getParameterValue($attribute, $context); + + $this->assertTrue($result->isResolved()); + $this->assertEquals($expectedValue, $result->getValue()); + } + public function testWithHydrator(): void { $hydrator = new Hydrator(); diff --git a/tests/AttributeHandling/ParameterAttributesHandlerTest.php b/tests/AttributeHandling/ParameterAttributesHandlerTest.php index 2d2c815..32c710c 100644 --- a/tests/AttributeHandling/ParameterAttributesHandlerTest.php +++ b/tests/AttributeHandling/ParameterAttributesHandlerTest.php @@ -5,6 +5,7 @@ namespace Yiisoft\Hydrator\Tests\AttributeHandling; use PHPUnit\Framework\TestCase; +use RuntimeException; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributesHandler; use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ContainerAttributeResolverFactory; @@ -70,4 +71,14 @@ static function( $this->assertSame('42', $result->getValue()); } + + public function testHydratorNotSet(): void + { + $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); + $parameter = TestHelper::getFirstParameter(static fn(#[CustomValue('42')] int $a) => null); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Hydrator is not set in parameter attributes handler.'); + $handler->handle($parameter); + } } diff --git a/tests/Support/Classes/Chart/Chart.php b/tests/Support/Classes/Chart/Chart.php index 05466d9..cc0db5b 100644 --- a/tests/Support/Classes/Chart/Chart.php +++ b/tests/Support/Classes/Chart/Chart.php @@ -10,8 +10,7 @@ final class Chart { public function __construct( #[Collection(Point::class)] - private array $points = [], - // private ?Point $point = null, + private array $points, ) { } @@ -19,9 +18,4 @@ public function getPoints(): array { return $this->points; } - -// public function getPoint(): ?Point -// { -// return $this->point; -// } } diff --git a/tests/Support/Classes/Chart/ChartSet.php b/tests/Support/Classes/Chart/ChartSet.php index d329fdf..1e956d3 100644 --- a/tests/Support/Classes/Chart/ChartSet.php +++ b/tests/Support/Classes/Chart/ChartSet.php @@ -13,9 +13,4 @@ public function __construct( private array $charts = [], ) { } - - public function getCharts(): array - { - return $this->charts; - } } diff --git a/tests/Support/Classes/Chart/Coordinates.php b/tests/Support/Classes/Chart/Coordinates.php index 6745518..1f5cae9 100644 --- a/tests/Support/Classes/Chart/Coordinates.php +++ b/tests/Support/Classes/Chart/Coordinates.php @@ -11,14 +11,4 @@ public function __construct( private int $y, ) { } - - public function getX(): int - { - return $this->x; - } - - public function getY(): int - { - return $this->y; - } } diff --git a/tests/Support/Classes/Chart/Point.php b/tests/Support/Classes/Chart/Point.php index d7be1e6..9933801 100644 --- a/tests/Support/Classes/Chart/Point.php +++ b/tests/Support/Classes/Chart/Point.php @@ -11,14 +11,4 @@ public function __construct( private array $rgb, ) { } - - public function getCoordinates(): Coordinates - { - return $this->coordinates; - } - - public function getRgb(): array - { - return $this->rgb; - } } diff --git a/tests/Support/Classes/Post.php b/tests/Support/Classes/Post.php new file mode 100644 index 0000000..53dd553 --- /dev/null +++ b/tests/Support/Classes/Post.php @@ -0,0 +1,14 @@ + Date: Fri, 2 Aug 2024 13:02:55 +0500 Subject: [PATCH 06/27] Add PHPDoc [skip ci] --- src/Attribute/Parameter/Collection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Attribute/Parameter/Collection.php b/src/Attribute/Parameter/Collection.php index c2eceba..7100ffe 100644 --- a/src/Attribute/Parameter/Collection.php +++ b/src/Attribute/Parameter/Collection.php @@ -6,6 +6,10 @@ use Attribute; +/** + * Converts the resolved value to array of instances of the class specified in {@see Collection::$className}. + * Non-resolved and invalid values are skipped. + */ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)] final class Collection implements ParameterAttributeInterface { From 4eba0b3029274e083429ca525805ed0755dc3aab Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 2 Aug 2024 14:20:45 +0500 Subject: [PATCH 07/27] More tests --- tests/Attribute/Parameter/CollectionTest.php | 89 +++++++++++++++----- tests/Support/Classes/PostCategory.php | 21 +++++ 2 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 tests/Support/Classes/PostCategory.php diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index c890fbe..3736ffe 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -13,6 +13,7 @@ use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ContainerAttributeResolverFactory; +use Yiisoft\Hydrator\DataInterface; use Yiisoft\Hydrator\Hydrator; use Yiisoft\Hydrator\Result; use Yiisoft\Hydrator\Tests\Support\Attribute\Counter; @@ -23,6 +24,7 @@ use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Point; use Yiisoft\Hydrator\Tests\Support\Classes\CounterClass; use Yiisoft\Hydrator\Tests\Support\Classes\Post; +use Yiisoft\Hydrator\Tests\Support\Classes\PostCategory; use Yiisoft\Hydrator\Tests\Support\TestHelper; use Yiisoft\Test\Support\Container\SimpleContainer; @@ -49,50 +51,41 @@ public function testUnexpectedAttributeException(): void public function testNotResolvedValue(): void { $hydrator = new Hydrator(); - $object = new class () { - #[Collection(Chart::class)] - public array $charts = []; - }; + $object = new PostCategory(); - $hydrator->hydrate($object, ['chart' => []]); - $this->assertEmpty($object->charts); + $hydrator->hydrate($object, ['post' => []]); + $this->assertEmpty($object->getPosts()); } public function testInvalidValue(): void { $hydrator = new Hydrator(); - $object = new class () { - #[Collection(Chart::class)] - public array $charts = []; - }; + $object = new PostCategory(); - $hydrator->hydrate($object, ['charts' => new stdClass()]); - $this->assertEmpty($object->charts); + $hydrator->hydrate($object, ['posts' => new stdClass()]); + $this->assertEmpty($object->getPosts()); } public function testInvalidValueItem(): void { $hydrator = new Hydrator(); - $object = new class () { - #[Collection(Chart::class)] - public array $charts = []; - }; + $object = new PostCategory(); $hydrator->hydrate( $object, [ - 'charts' => [ + 'posts' => [ new stdClass(), ], ], ); - $this->assertEquals($object->charts, [new stdClass()]); + $this->assertEquals($object->getPosts(), [new stdClass()]); } public static function dataBase(): array { return [ - [ + 'basic' => [ new Collection(Post::class), [ ['name' => 'Post 1'], @@ -103,11 +96,67 @@ public static function dataBase(): array new Post(name: 'Post 2', description: 'Description for post 2'), ], ], + 'nested, one to one and one to many relations' => [ + new Collection(Chart::class), + [ + [ + 'points' => [ + ['coordinates' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0]], + ['coordinates' => ['x' => -12, 'y' => 12], 'rgb' => [0, -2, 257]], + ], + ], + [ + 'points' => [ + ['coordinates' => ['x' => -1, 'y' => 1], 'rgb' => [0, 0, 0]], + ['coordinates' => ['x' => -2, 'y' => 2], 'rgb' => [255, 255, 255]], + ], + ], + [ + 'points' => [ + ['coordinates' => ['x' => -13, 'y' => 13], 'rgb' => [-3, 258, 0]], + ['coordinates' => ['x' => -14, 'y' => 14], 'rgb' => [0, -4, 259]], + ], + ], + ], + [ + new Chart([ + new Point(new Coordinates(-11, 11), [-1, 256, 0]), + new Point(new Coordinates(-12, 12), [0, -2, 257]), + ]), + new Chart([ + new Point(new Coordinates(-1, 1), [0, 0, 0]), + new Point(new Coordinates(-2, 2), [255, 255, 255]), + ]), + new Chart([ + new Point(new Coordinates(-13, 13), [-3, 258, 0]), + new Point(new Coordinates(-14, 14), [0, -4, 259]), + ]), + ], + ], + 'value item provided by class' => [ + new Collection(Post::class), + [ + ['name' => 'Post 1'], + new class () implements DataInterface + { + public function getValue(string $name): Result + { + $value = $name === 'name' ? 'Post 2' : 'Description for post 2'; + + return Result::success($value); + } + } + ], + [ + new Post(name: 'Post 1'), + new Post(name: 'Post 2', description: 'Description for post 2'), + ], + ], ]; } #[DataProvider('dataBase')] - public function testBase(Collection $attribute, array $value, array $expectedValue): void + public function testBase(Collection $attribute, array $value, mixed $expectedValue): void { $resolver = new CollectionResolver(); $context = new ParameterAttributeResolveContext( diff --git a/tests/Support/Classes/PostCategory.php b/tests/Support/Classes/PostCategory.php new file mode 100644 index 0000000..49dfa10 --- /dev/null +++ b/tests/Support/Classes/PostCategory.php @@ -0,0 +1,21 @@ +posts; + } +} From 27ae88800b3012e57b5e9a2dad0b79aecc3c4507 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 2 Aug 2024 09:20:57 +0000 Subject: [PATCH 08/27] Apply fixes from StyleCI --- tests/Attribute/Parameter/CollectionTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 3736ffe..7835008 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -137,15 +137,14 @@ public static function dataBase(): array new Collection(Post::class), [ ['name' => 'Post 1'], - new class () implements DataInterface - { + new class () implements DataInterface { public function getValue(string $name): Result { $value = $name === 'name' ? 'Post 2' : 'Description for post 2'; return Result::success($value); } - } + }, ], [ new Post(name: 'Post 1'), From 8f7162c121b41548be8f80eb76241636168254b8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 2 Aug 2024 14:26:34 +0500 Subject: [PATCH 09/27] Update CHANGELOG [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ed6fc2..e5ca24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 1.3.0 under development +- New #49: Add support for collections (@arogachev) - Enh #85: Allow to hydrate non-initialized readonly properties (@vjik) ## 1.2.0 April 03, 2024 From 71a9feea569d4a4ea300031f82111695f62e21d6 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 2 Aug 2024 17:10:31 +0500 Subject: [PATCH 10/27] Update guide --- docs/guide/en/typecasting.md | 114 ++++++++++++++++++++++++-- tests/Support/Classes/Chart/Chart.php | 5 -- 2 files changed, 105 insertions(+), 14 deletions(-) diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index 86207e2..dbfa9a7 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -120,6 +120,8 @@ echo $post->getAuthor()->getNickName(); ## Using attributes +### Strings + To cast a value to string explicitly, you can use `ToString` attribute: ```php @@ -140,6 +142,26 @@ $money = $hydrator->create(Money::class, [ ]); ``` +To strip whitespace (or other characters) from the beginning and/or end of a resolved string value, you can use `Trim`, +`LeftTrim` or `RightTrim` attributes: + +```php +use DateTimeImmutable; +use Yiisoft\Hydrator\Attribute\Parameter\Trim; + +class Person +{ + public function __construct( + #[Trim] // ' John ' → 'John' + private ?string $name = null, + ) {} +} + +$person = $hydrator->create(Person::class, ['name' => ' John ']); +``` + +### Datetime + To cast a value to `DateTimeImmutable` or `DateTime` object explicitly, you can use `ToDateTime` attribute: ```php @@ -157,20 +179,94 @@ class Person $person = $hydrator->create(Person::class, ['birthday' => '27.01.1986']); ``` -To strip whitespace (or other characters) from the beginning and/or end of a resolved string value, you can use `Trim`, -`LeftTrim` or `RightTrim` attributes: +### Collections + +Hydrator supports collections via `Collection` attribute. The class name of related collection must be specified: ```php -use DateTimeImmutable; -use Yiisoft\Hydrator\Attribute\Parameter\Trim; +final class PostCategory +{ + public function __construct( + #[Collection(Post::class)] + private array $posts = [], + ) { + } +} -class Person +final class Post { public function __construct( - #[Trim] // ' John ' → 'John' - private ?string $name = null, - ) {} + private string $name, + private string $description = '', + ) { + } } -$person = $hydrator->create(Person::class, ['name' => ' John ']); +$category = $hydrator->create( + PostCategory::class, + [ + ['name' => 'Post 1'], + ['name' => 'Post 2', 'description' => 'Description for post 2'], + ], +); +``` + +One-to-many relations can be nested and combined with one-to-one relations: + +```php +final class ChartSet +{ + public function __construct( + #[Collection(Chart::class)] + private array $charts = [], + ) { + } +} + +final class Chart +{ + public function __construct( + #[Collection(Point::class)] + private array $points, + ) { + } +} + +final class Point +{ + public function __construct( + private Coordinates $coordinates, + private array $rgb, + ) { + } +} + +final class Coordinates +{ + public function __construct( + private int $x, + private int $y, + ) { + } +} + +$object = $hydrator->create( + ChartSet::class, + [ + 'charts' => [ + [ + 'points' => [ + ['coordinates' => ['x' => 1, 'y' => 1], 'rgb' => [0, 0, 0]], + ['coordinates' => ['x' => 2, 'y' => 2], 'rgb' => [0, 0, 0]], + ], + ], + [ + 'points' => [ + ['coordinates' => ['x' => 3, 'y' => 3], 'rgb' => [0, 0, 0]], + ['coordinates' => ['x' => 4, 'y' => 4], 'rgb' => [0, 0, 0]], + ], + ], + ], + ], +); ``` diff --git a/tests/Support/Classes/Chart/Chart.php b/tests/Support/Classes/Chart/Chart.php index cc0db5b..b90cba1 100644 --- a/tests/Support/Classes/Chart/Chart.php +++ b/tests/Support/Classes/Chart/Chart.php @@ -13,9 +13,4 @@ public function __construct( private array $points, ) { } - - public function getPoints(): array - { - return $this->points; - } } From be18d6f22be6b163fd29c7e37348fabffa266d45 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 2 Aug 2024 17:23:44 +0500 Subject: [PATCH 11/27] Make data valid in tests --- tests/Attribute/Parameter/CollectionTest.php | 48 ++++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 7835008..5a0b5fd 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -101,35 +101,35 @@ public static function dataBase(): array [ [ 'points' => [ - ['coordinates' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0]], - ['coordinates' => ['x' => -12, 'y' => 12], 'rgb' => [0, -2, 257]], + ['coordinates' => ['x' => 1, 'y' => 1], 'rgb' => [255, 0, 0]], + ['coordinates' => ['x' => 2, 'y' => 2], 'rgb' => [255, 0, 0]], ], ], [ 'points' => [ - ['coordinates' => ['x' => -1, 'y' => 1], 'rgb' => [0, 0, 0]], - ['coordinates' => ['x' => -2, 'y' => 2], 'rgb' => [255, 255, 255]], + ['coordinates' => ['x' => 3, 'y' => 3], 'rgb' => [0, 255, 0]], + ['coordinates' => ['x' => 4, 'y' => 4], 'rgb' => [0, 255, 0]], ], ], [ 'points' => [ - ['coordinates' => ['x' => -13, 'y' => 13], 'rgb' => [-3, 258, 0]], - ['coordinates' => ['x' => -14, 'y' => 14], 'rgb' => [0, -4, 259]], + ['coordinates' => ['x' => 5, 'y' => 5], 'rgb' => [0, 0, 255]], + ['coordinates' => ['x' => 6, 'y' => 6], 'rgb' => [0, 0, 255]], ], ], ], [ new Chart([ - new Point(new Coordinates(-11, 11), [-1, 256, 0]), - new Point(new Coordinates(-12, 12), [0, -2, 257]), + new Point(new Coordinates(1, 1), [255, 0, 0]), + new Point(new Coordinates(2, 2), [255, 0, 0]), ]), new Chart([ - new Point(new Coordinates(-1, 1), [0, 0, 0]), - new Point(new Coordinates(-2, 2), [255, 255, 255]), + new Point(new Coordinates(3, 3), [0, 255, 0]), + new Point(new Coordinates(4, 4), [0, 255, 0]), ]), new Chart([ - new Point(new Coordinates(-13, 13), [-3, 258, 0]), - new Point(new Coordinates(-14, 14), [0, -4, 259]), + new Point(new Coordinates(5, 5), [0, 0, 255]), + new Point(new Coordinates(6, 6), [0, 0, 255]), ]), ], ], @@ -179,20 +179,20 @@ public function testWithHydrator(): void 'charts' => [ [ 'points' => [ - ['coordinates' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0]], - ['coordinates' => ['x' => -12, 'y' => 12], 'rgb' => [0, -2, 257]], + ['coordinates' => ['x' => 1, 'y' => 1], 'rgb' => [255, 0, 0]], + ['coordinates' => ['x' => 2, 'y' => 2], 'rgb' => [255, 0, 0]], ], ], [ 'points' => [ - ['coordinates' => ['x' => -1, 'y' => 1], 'rgb' => [0, 0, 0]], - ['coordinates' => ['x' => -2, 'y' => 2], 'rgb' => [255, 255, 255]], + ['coordinates' => ['x' => 3, 'y' => 3], 'rgb' => [0, 255, 0]], + ['coordinates' => ['x' => 4, 'y' => 4], 'rgb' => [0, 255, 0]], ], ], [ 'points' => [ - ['coordinates' => ['x' => -13, 'y' => 13], 'rgb' => [-3, 258, 0]], - ['coordinates' => ['x' => -14, 'y' => 14], 'rgb' => [0, -4, 259]], + ['coordinates' => ['x' => 5, 'y' => 5], 'rgb' => [0, 0, 255]], + ['coordinates' => ['x' => 6, 'y' => 6], 'rgb' => [0, 0, 255]], ], ], ], @@ -202,16 +202,16 @@ public function testWithHydrator(): void $this->assertEquals( new ChartSet([ new Chart([ - new Point(new Coordinates(-11, 11), [-1, 256, 0]), - new Point(new Coordinates(-12, 12), [0, -2, 257]), + new Point(new Coordinates(1, 1), [255, 0, 0]), + new Point(new Coordinates(2, 2), [255, 0, 0]), ]), new Chart([ - new Point(new Coordinates(-1, 1), [0, 0, 0]), - new Point(new Coordinates(-2, 2), [255, 255, 255]), + new Point(new Coordinates(3, 3), [0, 255, 0]), + new Point(new Coordinates(4, 4), [0, 255, 0]), ]), new Chart([ - new Point(new Coordinates(-13, 13), [-3, 258, 0]), - new Point(new Coordinates(-14, 14), [0, -4, 259]), + new Point(new Coordinates(5, 5), [0, 0, 255]), + new Point(new Coordinates(6, 6), [0, 0, 255]), ]), ]), $object, From b38c4148d50b75839829fbb7b2b8ac4d08f296bf Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 11:39:29 +0500 Subject: [PATCH 12/27] Update src/Attribute/Parameter/CollectionResolver.php Co-authored-by: Sergei Predvoditelev --- src/Attribute/Parameter/CollectionResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index e3e5745..4ff7686 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -29,7 +29,7 @@ public function getParameterValue( } $collection = []; - foreach ($context->getResolvedValue() as $item) { + foreach ($resolvedValue as $item) { if (!is_array($item) && !$item instanceof DataInterface) { return Result::fail(); } From 4174c745636e64d96ed056316bf8ffeb910498c4 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 12:29:30 +0500 Subject: [PATCH 13/27] Prevent BC break, use LogicException --- .../ParameterAttributeResolveContext.php | 20 ++++++++++++++++++- .../ParameterAttributesHandler.php | 5 +++-- .../ParameterAttributeResolveContextTest.php | 15 ++++++++++++++ .../ParameterAttributesHandlerTest.php | 4 ++-- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/AttributeHandling/ParameterAttributeResolveContext.php b/src/AttributeHandling/ParameterAttributeResolveContext.php index 7681051..f809ee7 100644 --- a/src/AttributeHandling/ParameterAttributeResolveContext.php +++ b/src/AttributeHandling/ParameterAttributeResolveContext.php @@ -4,6 +4,7 @@ namespace Yiisoft\Hydrator\AttributeHandling; +use LogicException; use ReflectionParameter; use ReflectionProperty; use Yiisoft\Hydrator\DataInterface; @@ -19,12 +20,13 @@ final class ParameterAttributeResolveContext * @param ReflectionParameter|ReflectionProperty $parameter Resolved parameter or property reflection. * @param Result $resolveResult The resolved value object. * @param DataInterface $data Data to be used for resolving. + * @param ?HydratorInterface Hydrator instance. */ public function __construct( private ReflectionParameter|ReflectionProperty $parameter, private Result $resolveResult, private DataInterface $data, - private HydratorInterface $hydrator, + private ?HydratorInterface $hydrator = null, ) { } @@ -71,6 +73,22 @@ public function getData(): DataInterface public function getHydrator(): HydratorInterface { + $this->requireHydrator(); + return $this->hydrator; } + + /** + * Ensure that validator is set in parameter attribute resolve context. + * + * @psalm-assert HydratorInterface $this->hydrator + * + * @throws LogicException If hydrator is not set in parameter attribute resolve context. + */ + private function requireHydrator(): void + { + if ($this->hydrator === null) { + throw new LogicException('Hydrator is not set in parameter attribute resolve context.'); + } + } } diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 5c94033..5926508 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -4,6 +4,7 @@ namespace Yiisoft\Hydrator\AttributeHandling; +use LogicException; use ReflectionAttribute; use ReflectionParameter; use ReflectionProperty; @@ -79,12 +80,12 @@ public function handle( * * @psalm-assert HydratorInterface $this->hydrator * - * @throws RuntimeException If hydrator is not set in parameter attributes handler. + * @throws LogicException If hydrator is not set in parameter attributes handler. */ private function requireHydrator(): void { if ($this->hydrator === null) { - throw new RuntimeException('Hydrator is not set in parameter attributes handler.'); + throw new LogicException('Hydrator is not set in parameter attributes handler.'); } } } diff --git a/tests/AttributeHandling/ParameterAttributeResolveContextTest.php b/tests/AttributeHandling/ParameterAttributeResolveContextTest.php index ddd9bb0..0987add 100644 --- a/tests/AttributeHandling/ParameterAttributeResolveContextTest.php +++ b/tests/AttributeHandling/ParameterAttributeResolveContextTest.php @@ -4,12 +4,14 @@ namespace Yiisoft\Hydrator\Tests\AttributeHandling; +use LogicException; use PHPUnit\Framework\TestCase; use ReflectionClass; use Yiisoft\Hydrator\ArrayData; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; use Yiisoft\Hydrator\Hydrator; use Yiisoft\Hydrator\Result; +use Yiisoft\Hydrator\Tests\Support\TestHelper; final class ParameterAttributeResolveContextTest extends TestCase { @@ -29,4 +31,17 @@ public function testBase(): void $this->assertSame(7, $context->getResolvedValue()); $this->assertSame($data, $context->getData()); } + + public function testGetHydratorNull(): void + { + $context = new ParameterAttributeResolveContext( + TestHelper::getFirstParameter(static fn(?string $a) => null), + Result::success(1), + new ArrayData(), + ); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Hydrator is not set in parameter attribute resolve context.'); + $context->getHydrator(); + } } diff --git a/tests/AttributeHandling/ParameterAttributesHandlerTest.php b/tests/AttributeHandling/ParameterAttributesHandlerTest.php index 32c710c..dc3bf64 100644 --- a/tests/AttributeHandling/ParameterAttributesHandlerTest.php +++ b/tests/AttributeHandling/ParameterAttributesHandlerTest.php @@ -4,8 +4,8 @@ namespace Yiisoft\Hydrator\Tests\AttributeHandling; +use LogicException; use PHPUnit\Framework\TestCase; -use RuntimeException; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributesHandler; use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\ContainerAttributeResolverFactory; @@ -77,7 +77,7 @@ public function testHydratorNotSet(): void $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); $parameter = TestHelper::getFirstParameter(static fn(#[CustomValue('42')] int $a) => null); - $this->expectException(RuntimeException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Hydrator is not set in parameter attributes handler.'); $handler->handle($parameter); } From 690a652b4d134393243a2b42e13bdaca630ca702 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 12:42:44 +0500 Subject: [PATCH 14/27] Microoptimization --- src/AttributeHandling/ParameterAttributesHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 5926508..79684bf 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -43,6 +43,7 @@ public function handle( ?Result $resolveResult = null, ?DataInterface $data = null ): Result { + $this->requireHydrator(); $resolveResult ??= Result::fail(); $data ??= new ArrayData(); @@ -63,7 +64,6 @@ public function handle( ); } - $this->requireHydrator(); $context = new ParameterAttributeResolveContext($parameter, $resolveResult, $data, $this->hydrator); $tryResolveResult = $resolver->getParameterValue($attribute, $context); From 9c7679985168831432c197f68b71c0e6cb77e515 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 12:52:09 +0500 Subject: [PATCH 15/27] Do not use separate method --- .../ParameterAttributeResolveContext.php | 16 ++-------------- .../ParameterAttributesHandler.php | 19 ++++--------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/AttributeHandling/ParameterAttributeResolveContext.php b/src/AttributeHandling/ParameterAttributeResolveContext.php index f809ee7..ed2e12d 100644 --- a/src/AttributeHandling/ParameterAttributeResolveContext.php +++ b/src/AttributeHandling/ParameterAttributeResolveContext.php @@ -72,23 +72,11 @@ public function getData(): DataInterface } public function getHydrator(): HydratorInterface - { - $this->requireHydrator(); - - return $this->hydrator; - } - - /** - * Ensure that validator is set in parameter attribute resolve context. - * - * @psalm-assert HydratorInterface $this->hydrator - * - * @throws LogicException If hydrator is not set in parameter attribute resolve context. - */ - private function requireHydrator(): void { if ($this->hydrator === null) { throw new LogicException('Hydrator is not set in parameter attribute resolve context.'); } + + return $this->hydrator; } } diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 79684bf..acb231e 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -43,7 +43,10 @@ public function handle( ?Result $resolveResult = null, ?DataInterface $data = null ): Result { - $this->requireHydrator(); + if ($this->hydrator === null) { + throw new LogicException('Hydrator is not set in parameter attributes handler.'); + } + $resolveResult ??= Result::fail(); $data ??= new ArrayData(); @@ -74,18 +77,4 @@ public function handle( return $resolveResult; } - - /** - * Ensure that validator is set in parameter attributes handler. - * - * @psalm-assert HydratorInterface $this->hydrator - * - * @throws LogicException If hydrator is not set in parameter attributes handler. - */ - private function requireHydrator(): void - { - if ($this->hydrator === null) { - throw new LogicException('Hydrator is not set in parameter attributes handler.'); - } - } } From 4b839e4d9368e4b92bfab5d7e6c3d965be7d6e0d Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 13:13:15 +0500 Subject: [PATCH 16/27] Add method `withHydrator()` --- .../ParameterAttributesHandler.php | 7 +++++++ .../ParameterAttributesHandlerTest.php | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index acb231e..17c0125 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -77,4 +77,11 @@ public function handle( return $resolveResult; } + + public function withHydrator(HydratorInterface $hydrator): static + { + $new = clone $this; + $new->hydrator = $hydrator; + return $new; + } } diff --git a/tests/AttributeHandling/ParameterAttributesHandlerTest.php b/tests/AttributeHandling/ParameterAttributesHandlerTest.php index dc3bf64..32d7292 100644 --- a/tests/AttributeHandling/ParameterAttributesHandlerTest.php +++ b/tests/AttributeHandling/ParameterAttributesHandlerTest.php @@ -72,6 +72,19 @@ static function( $this->assertSame('42', $result->getValue()); } + public function testWithHydrator(): void + { + $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); + $hydrator = new Hydrator(); + + $newHandler = $handler->withHydrator($hydrator); + $this->assertNotSame($handler, $newHandler); + + $parameter = TestHelper::getFirstParameter(static fn(#[CustomValue('42')] int $a) => null); + $result = $newHandler->handle($parameter); + $this->assertSame('42', $result->getValue()); + } + public function testHydratorNotSet(): void { $handler = new ParameterAttributesHandler(new ReflectionAttributeResolverFactory()); From 163dbab04fac53e7dfcee09999cdcbbf7808d079 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 13:16:43 +0500 Subject: [PATCH 17/27] Review fix [skip ci] --- docs/guide/en/typecasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index dbfa9a7..1c87bde 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -211,7 +211,7 @@ $category = $hydrator->create( ); ``` -One-to-many relations can be nested and combined with one-to-one relations: +Collections can be nested and combined one-to-one relations: ```php final class ChartSet From 4e37470eb1b3a03dc32cdac371fa11fa4a289446 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 13:18:55 +0500 Subject: [PATCH 18/27] Headers [skip ci] --- docs/guide/en/typecasting.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index 1c87bde..287e881 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -120,7 +120,7 @@ echo $post->getAuthor()->getNickName(); ## Using attributes -### Strings +### `ToString` To cast a value to string explicitly, you can use `ToString` attribute: @@ -142,6 +142,8 @@ $money = $hydrator->create(Money::class, [ ]); ``` +### `Trim` / `LeftTrim` / `RightTrim` + To strip whitespace (or other characters) from the beginning and/or end of a resolved string value, you can use `Trim`, `LeftTrim` or `RightTrim` attributes: @@ -160,7 +162,7 @@ class Person $person = $hydrator->create(Person::class, ['name' => ' John ']); ``` -### Datetime +### `ToDatetime` To cast a value to `DateTimeImmutable` or `DateTime` object explicitly, you can use `ToDateTime` attribute: @@ -179,7 +181,7 @@ class Person $person = $hydrator->create(Person::class, ['birthday' => '27.01.1986']); ``` -### Collections +### `Collection` Hydrator supports collections via `Collection` attribute. The class name of related collection must be specified: From 5095923d66a74285469ce4a7d4de6c87c3d3f190 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 13:34:59 +0500 Subject: [PATCH 19/27] Review fix --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ca24f..2955542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## 1.3.0 under development -- New #49: Add support for collections (@arogachev) +- New #49: Add support for collections: + - Add `Collection` attribute with handler for using with parameters; + - Add hydrator dependency to `ParameterAttributeResolveContext` and `ParameterAttributesHandler`. + (@arogachev) - Enh #85: Allow to hydrate non-initialized readonly properties (@vjik) ## 1.2.0 April 03, 2024 From 5a4f4923c208b5e69f9b1977f8b5f11aeb100536 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 16:35:55 +0500 Subject: [PATCH 20/27] self --- src/AttributeHandling/ParameterAttributesHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AttributeHandling/ParameterAttributesHandler.php b/src/AttributeHandling/ParameterAttributesHandler.php index 17c0125..2e187aa 100644 --- a/src/AttributeHandling/ParameterAttributesHandler.php +++ b/src/AttributeHandling/ParameterAttributesHandler.php @@ -78,7 +78,7 @@ public function handle( return $resolveResult; } - public function withHydrator(HydratorInterface $hydrator): static + public function withHydrator(HydratorInterface $hydrator): self { $new = clone $this; $new->hydrator = $hydrator; From b9fbdd8e83e4d6d5c24aa1d4ee136f502870529f Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 16:36:16 +0500 Subject: [PATCH 21/27] Fix typo [skip ci] --- docs/guide/en/typecasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index 287e881..5d6a6ae 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -213,7 +213,7 @@ $category = $hydrator->create( ); ``` -Collections can be nested and combined one-to-one relations: +Collections can be nested and combined with one-to-one relations: ```php final class ChartSet From 61875bfe6c296335105a6a5c8117ed54ea0468c1 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 16:39:12 +0500 Subject: [PATCH 22/27] Update CHANGELOG.md Co-authored-by: Sergei Predvoditelev --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2955542..0e106ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,9 @@ ## 1.3.0 under development -- New #49: Add support for collections: - - Add `Collection` attribute with handler for using with parameters; - - Add hydrator dependency to `ParameterAttributeResolveContext` and `ParameterAttributesHandler`. - (@arogachev) +- New #49: Add `Collection` PHP attribute (@arogachev) +- New #49: Add hydrator dependency and `withHydrator()` method to `ParameterAttributesHandler` (@arogachev) +- New #49: Add hydrator dependency and `getHydrator()` method to `ParameterAttributeResolveContext` (@arogachev) - Enh #85: Allow to hydrate non-initialized readonly properties (@vjik) ## 1.2.0 April 03, 2024 From e7bb65ba3e107dea8358fc503b22071217a21b88 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 16:42:45 +0500 Subject: [PATCH 23/27] Review fix [skip ci] --- docs/guide/en/typecasting.md | 60 ------------------------------------ 1 file changed, 60 deletions(-) diff --git a/docs/guide/en/typecasting.md b/docs/guide/en/typecasting.md index 5d6a6ae..ccda587 100644 --- a/docs/guide/en/typecasting.md +++ b/docs/guide/en/typecasting.md @@ -212,63 +212,3 @@ $category = $hydrator->create( ], ); ``` - -Collections can be nested and combined with one-to-one relations: - -```php -final class ChartSet -{ - public function __construct( - #[Collection(Chart::class)] - private array $charts = [], - ) { - } -} - -final class Chart -{ - public function __construct( - #[Collection(Point::class)] - private array $points, - ) { - } -} - -final class Point -{ - public function __construct( - private Coordinates $coordinates, - private array $rgb, - ) { - } -} - -final class Coordinates -{ - public function __construct( - private int $x, - private int $y, - ) { - } -} - -$object = $hydrator->create( - ChartSet::class, - [ - 'charts' => [ - [ - 'points' => [ - ['coordinates' => ['x' => 1, 'y' => 1], 'rgb' => [0, 0, 0]], - ['coordinates' => ['x' => 2, 'y' => 2], 'rgb' => [0, 0, 0]], - ], - ], - [ - 'points' => [ - ['coordinates' => ['x' => 3, 'y' => 3], 'rgb' => [0, 0, 0]], - ['coordinates' => ['x' => 4, 'y' => 4], 'rgb' => [0, 0, 0]], - ], - ], - ], - ], -); -``` From fc0f3af72b726613f335f7af3bda46dc21dd8ca9 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 16:48:45 +0500 Subject: [PATCH 24/27] Logic fix --- src/Attribute/Parameter/CollectionResolver.php | 2 +- tests/Attribute/Parameter/CollectionTest.php | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index 4ff7686..52ea3db 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -31,7 +31,7 @@ public function getParameterValue( $collection = []; foreach ($resolvedValue as $item) { if (!is_array($item) && !$item instanceof DataInterface) { - return Result::fail(); + continue; } $collection[] = $context->getHydrator()->create($attribute->className, $item); diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 5a0b5fd..63406e7 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -75,11 +75,19 @@ public function testInvalidValueItem(): void $object, [ 'posts' => [ + ['name' => 'Post 1'], new stdClass(), + ['name' => 'Post 2', 'description' => 'Description for post 2'], ], ], ); - $this->assertEquals($object->getPosts(), [new stdClass()]); + $this->assertEquals( + [ + new Post(name: 'Post 1'), + new Post(name: 'Post 2', description: 'Description for post 2'), + ], + $object->getPosts(), + ); } public static function dataBase(): array From 4ef73feb157cb74926f2b4ba96f85b96c8cb4c36 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 17:16:40 +0500 Subject: [PATCH 25/27] Handle noninstatiable exception --- .../Parameter/CollectionResolver.php | 7 +++++- tests/Attribute/Parameter/CollectionTest.php | 22 +++++++++++++++++-- tests/Support/Classes/{ => Post}/Post.php | 2 +- .../Classes/{ => Post}/PostCategory.php | 2 +- .../PostCategoryWithNonExistingPostClass.php | 21 ++++++++++++++++++ 5 files changed, 49 insertions(+), 5 deletions(-) rename tests/Support/Classes/{ => Post}/Post.php (75%) rename tests/Support/Classes/{ => Post}/PostCategory.php (85%) create mode 100644 tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index 52ea3db..903323e 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -7,6 +7,7 @@ use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException; use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext; use Yiisoft\Hydrator\DataInterface; +use Yiisoft\Hydrator\Exception\NonInstantiableException; use Yiisoft\Hydrator\Result; final class CollectionResolver implements ParameterAttributeResolverInterface @@ -34,7 +35,11 @@ public function getParameterValue( continue; } - $collection[] = $context->getHydrator()->create($attribute->className, $item); + try { + $collection[] = $context->getHydrator()->create($attribute->className, $item); + } catch (NonInstantiableException) { + continue; + } } return Result::success($collection); diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 63406e7..179fa6d 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -23,8 +23,9 @@ use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Coordinates; use Yiisoft\Hydrator\Tests\Support\Classes\Chart\Point; use Yiisoft\Hydrator\Tests\Support\Classes\CounterClass; -use Yiisoft\Hydrator\Tests\Support\Classes\Post; -use Yiisoft\Hydrator\Tests\Support\Classes\PostCategory; +use Yiisoft\Hydrator\Tests\Support\Classes\Post\Post; +use Yiisoft\Hydrator\Tests\Support\Classes\Post\PostCategory; +use Yiisoft\Hydrator\Tests\Support\Classes\Post\PostCategoryWithNonExistingPostClass; use Yiisoft\Hydrator\Tests\Support\TestHelper; use Yiisoft\Test\Support\Container\SimpleContainer; @@ -90,6 +91,23 @@ public function testInvalidValueItem(): void ); } + public function testNonInstantiableValueItem(): void + { + $hydrator = new Hydrator(); + $object = new PostCategoryWithNonExistingPostClass(); + + $hydrator->hydrate( + $object, + [ + 'posts' => [ + ['name' => 'Post 1'], + ['name' => 'Post 2', 'description' => 'Description for post 2'], + ], + ], + ); + $this->assertEmpty($object->getPosts()); + } + public static function dataBase(): array { return [ diff --git a/tests/Support/Classes/Post.php b/tests/Support/Classes/Post/Post.php similarity index 75% rename from tests/Support/Classes/Post.php rename to tests/Support/Classes/Post/Post.php index 53dd553..573101e 100644 --- a/tests/Support/Classes/Post.php +++ b/tests/Support/Classes/Post/Post.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Hydrator\Tests\Support\Classes; +namespace Yiisoft\Hydrator\Tests\Support\Classes\Post; final class Post { diff --git a/tests/Support/Classes/PostCategory.php b/tests/Support/Classes/Post/PostCategory.php similarity index 85% rename from tests/Support/Classes/PostCategory.php rename to tests/Support/Classes/Post/PostCategory.php index 49dfa10..aa79ce3 100644 --- a/tests/Support/Classes/PostCategory.php +++ b/tests/Support/Classes/Post/PostCategory.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Hydrator\Tests\Support\Classes; +namespace Yiisoft\Hydrator\Tests\Support\Classes\Post; use Yiisoft\Hydrator\Attribute\Parameter\Collection; diff --git a/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php b/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php new file mode 100644 index 0000000..e3d480f --- /dev/null +++ b/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php @@ -0,0 +1,21 @@ +posts; + } +} From 6fccfd3fbbe50dfc1e3be14465fe13355346eea8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 17:33:12 +0500 Subject: [PATCH 26/27] break --- src/Attribute/Parameter/CollectionResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index 903323e..66bfabc 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -38,7 +38,7 @@ public function getParameterValue( try { $collection[] = $context->getHydrator()->create($attribute->className, $item); } catch (NonInstantiableException) { - continue; + break; } } From a018aa40eb40d1e252a015c10fc79cafda549916 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 5 Aug 2024 17:47:34 +0500 Subject: [PATCH 27/27] Fix --- .../Parameter/CollectionResolver.php | 2 +- tests/Attribute/Parameter/CollectionTest.php | 12 ++++++++--- .../PostCategoryWithNonExistingPostClass.php | 21 ------------------- 3 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php diff --git a/src/Attribute/Parameter/CollectionResolver.php b/src/Attribute/Parameter/CollectionResolver.php index 66bfabc..903323e 100644 --- a/src/Attribute/Parameter/CollectionResolver.php +++ b/src/Attribute/Parameter/CollectionResolver.php @@ -38,7 +38,7 @@ public function getParameterValue( try { $collection[] = $context->getHydrator()->create($attribute->className, $item); } catch (NonInstantiableException) { - break; + continue; } } diff --git a/tests/Attribute/Parameter/CollectionTest.php b/tests/Attribute/Parameter/CollectionTest.php index 179fa6d..8a2a8d5 100644 --- a/tests/Attribute/Parameter/CollectionTest.php +++ b/tests/Attribute/Parameter/CollectionTest.php @@ -25,7 +25,6 @@ use Yiisoft\Hydrator\Tests\Support\Classes\CounterClass; use Yiisoft\Hydrator\Tests\Support\Classes\Post\Post; use Yiisoft\Hydrator\Tests\Support\Classes\Post\PostCategory; -use Yiisoft\Hydrator\Tests\Support\Classes\Post\PostCategoryWithNonExistingPostClass; use Yiisoft\Hydrator\Tests\Support\TestHelper; use Yiisoft\Test\Support\Container\SimpleContainer; @@ -94,18 +93,25 @@ public function testInvalidValueItem(): void public function testNonInstantiableValueItem(): void { $hydrator = new Hydrator(); - $object = new PostCategoryWithNonExistingPostClass(); + $object = new PostCategory(); $hydrator->hydrate( $object, [ 'posts' => [ ['name' => 'Post 1'], + ['name' => []], ['name' => 'Post 2', 'description' => 'Description for post 2'], ], ], ); - $this->assertEmpty($object->getPosts()); + $this->assertEquals( + [ + new Post(name: 'Post 1'), + new Post(name: 'Post 2', description: 'Description for post 2'), + ], + $object->getPosts(), + ); } public static function dataBase(): array diff --git a/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php b/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php deleted file mode 100644 index e3d480f..0000000 --- a/tests/Support/Classes/Post/PostCategoryWithNonExistingPostClass.php +++ /dev/null @@ -1,21 +0,0 @@ -posts; - } -}