Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added methods to extract unions (ie oneOf properties) #3

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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