Skip to content

Commit

Permalink
Merge pull request #3 from kynx/extract-unions
Browse files Browse the repository at this point in the history
Added methods to extract unions (ie oneOf properties)
  • Loading branch information
kynx authored Nov 24, 2023
2 parents ddf445e + f61c3b4 commit 8efabb0
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/Hydrator/HydratorUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,43 @@ public static function extractEnums(array $data, array $arrayProperties, array $
return $data;
}

/**
* @param array<string, array|object> $data
* @param array<string, array<class-string, class-string<HydratorInterface>>> $unions
*/
public static function extractUnions(array $data, array $arrayProperties, array $unions): array
{
foreach ($unions as $property => $union) {
$value = $data[$property];
if (in_array($property, $arrayProperties, true)) {
assert(is_array($value));
$unionValues = [];
/** @var object $unionValue */
foreach ($value as $unionValue) {
$unionValues[] = self::extractUnion($unionValue, $union);
}
$data[$property] = $unionValues;
} else {
assert(is_object($value));
$data[$property] = self::extractUnion($value, $union);
}
}

return $data;
}

/**
* @param array<class-string, class-string<HydratorInterface>> $union
*/
public static function extractUnion(object $object, array $union): bool|array|float|int|string|null
{
if (! isset($union[$object::class])) {
return null;
}
$extractor = $union[$object::class];
return $extractor::extract($object);
}

/**
* @param array<string, class-string<HydratorInterface>> $extractors
*/
Expand Down
32 changes: 32 additions & 0 deletions test/Hydrator/Asset/ExtractableHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace KynxTest\Mezzio\OpenApi\Hydrator\Asset;

use Kynx\Mezzio\OpenApi\Hydrator\HydratorInterface;

use function assert;
use function is_array;

final class ExtractableHydrator implements HydratorInterface
{
public static function hydrate(mixed $data): object
{
assert(is_array($data));
return new Extractable((string) $data['one'], (int) $data['two'], (bool) $data['three']);
}

/**
* @inheritDoc
*/
public static function extract(mixed $object): array
{
assert($object instanceof Extractable);
return [
'one' => $object->getOne(),
'two' => $object->getTwo(),
'three' => $object->isThree(),
];
}
}
17 changes: 17 additions & 0 deletions test/Hydrator/Asset/SecondExtractable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace KynxTest\Mezzio\OpenApi\Hydrator\Asset;

final class SecondExtractable
{
public function __construct(private readonly string $value)
{
}

public function getValue(): string
{
return $this->value;
}
}
27 changes: 27 additions & 0 deletions test/Hydrator/Asset/SecondExtractableHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace KynxTest\Mezzio\OpenApi\Hydrator\Asset;

use Kynx\Mezzio\OpenApi\Hydrator\HydratorInterface;

use function assert;
use function is_string;

final class SecondExtractableHydrator implements HydratorInterface
{
public static function hydrate(mixed $data): object
{
assert(is_string($data));
return new SecondExtractable($data);
}

public static function extract(mixed $object): bool|array|float|int|string|null
{
assert($object instanceof SecondExtractable);
return [
'value' => $object->getValue(),
];
}
}
68 changes: 68 additions & 0 deletions test/Hydrator/HydratorUtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
use Kynx\Mezzio\OpenApi\Hydrator\Exception\MissingDiscriminatorException;
use Kynx\Mezzio\OpenApi\Hydrator\HydratorUtil;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\Extractable;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\ExtractableHydrator;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\GoodHydrator;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\MockEnum;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\SecondExtractable;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\SecondExtractableHydrator;
use KynxTest\Mezzio\OpenApi\Hydrator\Asset\TypeErrorHydrator;
use PHPUnit\Framework\TestCase;
use stdClass;
Expand Down Expand Up @@ -494,6 +497,71 @@ public function testExtractEnumsExtrasSingleEnum(): void
self::assertSame($expected, $actual);
}

public function testExtractUnionsExtractsArrayOfUnions(): void
{
$expected = [
'items' => [
['one' => 'a', 'two' => 2, 'three' => true],
],
];
$data = [
'items' => [
new Extractable('a', 2, true),
],
];
$unions = [
'items' => [
Extractable::class => ExtractableHydrator::class,
],
];

$actual = HydratorUtil::extractUnions($data, ['items'], $unions);
self::assertSame($expected, $actual);
}

public function testExtractUnionsExtractsSingleUnion(): void
{
$expected = [
'item' => ['one' => 'a', 'two' => 2, 'three' => true],
];
$data = [
'item' => new Extractable('a', 2, true),
];
$unions = [
'item' => [
Extractable::class => ExtractableHydrator::class,
],
];

$actual = HydratorUtil::extractUnions($data, ['foo'], $unions);
self::assertSame($expected, $actual);
}

/**
* @dataProvider extractUnionProvider
*/
public function testExtractUnionExtractsCorrectClass(object $extractable, array $expected): void
{
$union = [
Extractable::class => ExtractableHydrator::class,
SecondExtractable::class => SecondExtractableHydrator::class,
];

$actual = HydratorUtil::extractUnion($extractable, $union);
self::assertSame($expected, $actual);
}

/**
* @return array<string, array{0: Extractable|SecondExtractable, 1: array}>
*/
public static function extractUnionProvider(): array
{
return [
'first' => [new Extractable('a', 2, true), ['one' => 'a', 'two' => 2, 'three' => true]],
'second' => [new SecondExtractable('foo'), ['value' => 'foo']],
];
}

public function testExtractEnumsExtractsArrayOfEnums(): void
{
$expected = [
Expand Down

0 comments on commit 8efabb0

Please sign in to comment.