Skip to content

Commit

Permalink
Merge pull request #25 from spiral-packages/feature/uuid-normalizer
Browse files Browse the repository at this point in the history
Adding Ramsey UUID normalizer
  • Loading branch information
butschster committed Jun 7, 2023
2 parents d970f90 + c6a8088 commit f95e2c7
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 7 deletions.
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
"symfony/property-access": "^5.4 || ^6.0"
},
"require-dev": {
"phpunit/phpunit": "^10.1",
"spiral/testing": "^3.0",
"phpunit/phpunit": "^10.2",
"spiral/testing": "^2.3",
"symfony/yaml": "^5.4 || ^6.0",
"vimeo/psalm": "^5.9"
"vimeo/psalm": "^5.12",
"ramsey/uuid": "^4.7"
},
"suggest": {
"symfony/yaml": "For using the YamlEncoder."
Expand Down
44 changes: 44 additions & 0 deletions src/Normalizer/RamseyUuidNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Spiral\Serializer\Symfony\Normalizer;

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

final class RamseyUuidNormalizer implements NormalizerInterface, DenormalizerInterface
{
/**
* @param UuidInterface $object
* @psalm-suppress MoreSpecificImplementedParamType
*/
public function normalize(mixed $object, string $format = null, array $context = []): string
{
return $object->toString();
}

public function supportsNormalization(mixed $data, string $format = null): bool
{
return $data instanceof UuidInterface;
}

public function denormalize(mixed $data, string $type, string $format = null, array $context = []): UuidInterface
{
try {
return Uuid::fromString($data);
} catch (\InvalidArgumentException) {
throw new NotNormalizableValueException(
\sprintf('The data is not a valid "%s" string representation.', $type)
);
}
}

public function supportsDenormalization(mixed $data, string $type, string $format = null): bool
{
return \is_string($data) && \is_a($type, UuidInterface::class, true) && Uuid::isValid($data);
}
}
6 changes: 6 additions & 0 deletions src/NormalizersRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Spiral\Serializer\Symfony;

use Ramsey\Uuid\UuidInterface;
use Spiral\Boot\Environment\DebugMode;
use Spiral\Serializer\Symfony\Normalizer\RamseyUuidNormalizer;
use Symfony\Component\Messenger\Transport\Serialization\Normalizer\FlattenExceptionNormalizer;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
Expand Down Expand Up @@ -99,5 +101,9 @@ classMetadataFactory: $factory,
if (\class_exists(FlattenExceptionNormalizer::class)) {
$this->register(new FlattenExceptionNormalizer(), 750);
}

if (\interface_exists(UuidInterface::class)) {
$this->register(new RamseyUuidNormalizer(), 155);
}
}
}
16 changes: 16 additions & 0 deletions tests/app/Object/Author.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Spiral\Serializer\Symfony\Tests\App\Object;

use Ramsey\Uuid\UuidInterface;

class Author
{
public function __construct(
public UuidInterface $uuid,
public string $name
) {
}
}
14 changes: 14 additions & 0 deletions tests/app/Object/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Spiral\Serializer\Symfony\Tests\App\Object;

class User
{
public function __construct(
public int $id,
public \DateTimeInterface $registeredAt
) {
}
}
55 changes: 55 additions & 0 deletions tests/src/Feature/Normalizer/RamseyUuidNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Spiral\Serializer\Symfony\Tests\Feature\Normalizer;

use PHPUnit\Framework\Attributes\DataProvider;
use Ramsey\Uuid\Uuid;
use Spiral\Serializer\SerializerManager;
use Spiral\Serializer\Symfony\Tests\App\Object\Author;
use Spiral\Serializer\Symfony\Tests\Feature\TestCase;

final class RamseyUuidNormalizerTest extends TestCase
{
#[DataProvider('serializeDataProvider')]
public function testSerialize(string $expected, mixed $payload, string $format): void
{
$manager = $this->getContainer()->get(SerializerManager::class);

$this->assertSame($expected, preg_replace('/\s+/', '', $manager->serialize($payload, $format)));
}

public function testUnserialize(): void
{
$manager = $this->getContainer()->get(SerializerManager::class);

$result = $manager->unserialize(
'{"uuid":"1d96a152-9838-43a0-a189-159befc9e38f","name":"some"}',
Author::class,
'symfony-json'
);

$this->assertInstanceOf(Author::class, $result);
$this->assertSame('1d96a152-9838-43a0-a189-159befc9e38f', $result->uuid->toString());
}

public static function serializeDataProvider(): \Traversable
{
yield [
'{"uuid":"1d96a152-9838-43a0-a189-159befc9e38f","name":"some"}',
new Author(Uuid::fromString('1d96a152-9838-43a0-a189-159befc9e38f'), 'some'),
'symfony-json'
];
yield [
'uuid,name1d96a152-9838-43a0-a189-159befc9e38f,some',
new Author(Uuid::fromString('1d96a152-9838-43a0-a189-159befc9e38f'), 'some'),
'symfony-csv'
];
yield [
'{uuid:1d96a152-9838-43a0-a189-159befc9e38f,name:some}',
new Author(Uuid::fromString('1d96a152-9838-43a0-a189-159befc9e38f'), 'some'),
'symfony-yaml'
];
}
}
35 changes: 34 additions & 1 deletion tests/src/Feature/SerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@

namespace Spiral\Serializer\Symfony\Tests\Feature;

use PHPUnit\Framework\Attributes\DataProvider;
use Spiral\Serializer\SerializerManager;
use Spiral\Serializer\Symfony\Serializer;
use Spiral\Serializer\Symfony\Tests\App\NestedObjects\City;
use Spiral\Serializer\Symfony\Tests\App\NestedObjects\Country;
use Spiral\Serializer\Symfony\Tests\App\Object\Post;
use Spiral\Serializer\Symfony\Tests\App\Object\Product;
use Spiral\Serializer\Symfony\Tests\App\Object\User;

final class SerializerTest extends TestCase
{
/** @dataProvider serializeDataProvider */
#[DataProvider('serializeDataProvider')]
public function testSerialize(string $expected, mixed $payload, string $format): void
{
$manager = $this->getContainer()->get(SerializerManager::class);
Expand Down Expand Up @@ -75,6 +77,22 @@ public function testUnserializeNestedObjects(): void
$this->assertSame('NewYork', $result->cities[1]->name);
}

public function testUnserializeDateTimeInterface(): void
{
$manager = $this->getContainer()->get(SerializerManager::class);

$result = $manager->unserialize(
'{"id":3,"registeredAt":"2023-06-05T22:12:55+00:00"}',
User::class,
'symfony-json'
);

$this->assertInstanceOf(User::class, $result);
$this->assertSame(3, $result->id);
$this->assertInstanceOf(\DateTimeImmutable::class, $result->registeredAt);
$this->assertSame('2023-06-05T22:12:55+00:00', $result->registeredAt->format('c'));
}

public function testGroupNormalize(): void
{
$manager = $this->getContainer()->get(SerializerManager::class);
Expand Down Expand Up @@ -133,5 +151,20 @@ public static function serializeDataProvider(): \Traversable
]),
'symfony-xml'
];
yield [
'{"id":3,"registeredAt":"2023-06-05T22:12:55+00:00"}',
new User(3, new \DateTimeImmutable('2023-06-05T22:12:55+00:00')),
'symfony-json'
];
yield [
'id,registeredAt3,2023-06-05T22:12:55+00:00',
new User(3, new \DateTimeImmutable('2023-06-05T22:12:55+00:00')),
'symfony-csv'
];
yield [
'<?xmlversion="1.0"?><response><id>3</id><registeredAt>2023-06-05T22:12:55+00:00</registeredAt></response>',
new User(3, new \DateTimeImmutable('2023-06-05T22:12:55+00:00')),
'symfony-xml'
];
}
}
8 changes: 5 additions & 3 deletions tests/src/Unit/NormalizersRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Spiral\Serializer\Symfony\Tests\Unit;

use Spiral\Boot\Environment\DebugMode;
use Spiral\Serializer\Symfony\Normalizer\RamseyUuidNormalizer;
use Spiral\Serializer\Symfony\NormalizersRegistry;
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
use Symfony\Component\Serializer\Normalizer;
Expand All @@ -18,7 +19,7 @@ public function testConstructWithDefaultNormalizers(): void
DebugMode::Enabled
);

$this->assertCount(14, $registry->all());
$this->assertCount(15, $registry->all());

$this->assertTrue($registry->has(Normalizer\UnwrappingDenormalizer::class));
$this->assertTrue($registry->has(Normalizer\ProblemNormalizer::class));
Expand All @@ -34,6 +35,7 @@ public function testConstructWithDefaultNormalizers(): void
$this->assertTrue($registry->has(Normalizer\DataUriNormalizer::class));
$this->assertTrue($registry->has(Normalizer\ArrayDenormalizer::class));
$this->assertTrue($registry->has(Normalizer\ObjectNormalizer::class));
$this->assertTrue($registry->has(RamseyUuidNormalizer::class));
}

public function testRegister(): void
Expand All @@ -47,11 +49,11 @@ public function testRegister(): void
$normalizer2 = $this->createMock(Normalizer\DenormalizerInterface::class);

$registry->register($normalizer, 2);
$this->assertCount(15, $registry->all());
$this->assertCount(16, $registry->all());
$this->assertTrue($registry->has($normalizer::class));

$registry->register($normalizer2, 1);
$this->assertCount(16, $registry->all());
$this->assertCount(17, $registry->all());
$this->assertTrue($registry->has($normalizer2::class));

$this->assertSame($normalizer2, $registry->all()[0]);
Expand Down

0 comments on commit f95e2c7

Please sign in to comment.