Skip to content

Commit 675062b

Browse files
committed
feat: response interface and new tests
1 parent d78f5e0 commit 675062b

14 files changed

+206
-65
lines changed

config/serializer.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,42 @@
66
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
77

88
return [
9-
'default_serializer' => 'json',
9+
/*
10+
* Name (format) of the default serializer that will be registered in SerializerManager.
11+
*/
12+
'default' => 'json',
1013

14+
/*
15+
* Supported serializers: json, csv, xml, yaml
16+
* Set serializer to false to disable it.
17+
*/
1118
'serializers' => [
12-
// 'json' => \Symfony\Component\Serializer\Serializer::class,
19+
'json' => true,
20+
'csv' => false,
21+
'xml' => false,
22+
'yaml' => false,
1323
],
1424

25+
/*
26+
* Register your custom normalizers here.
27+
* Default normalizers are registered in src/NormalizersRegistry.php
28+
*/
1529
'normalizers' => [
16-
// \Symfony\Component\Serializer\Normalizer\ObjectNormalizer::class,
30+
// Symfony\Component\Messenger\Transport\Serialization\Normalizer\FlattenExceptionNormalizer
1731
],
1832

33+
/*
34+
* Register your custom encoders here.
35+
* Default encoders are registered in src/EncodersRegistry.php
36+
*
37+
* Default encoders are:
38+
* - Symfony\Component\Serializer\Encoder\JsonEncoder
39+
* - Symfony\Component\Serializer\Encoder\CsvEncoder
40+
* - Symfony\Component\Serializer\Encoder\XmlEncoder
41+
* - Symfony\Component\Serializer\Encoder\YamlEncoder
42+
*/
1943
'encoders' => [
20-
// \Symfony\Component\Serializer\Encoder\JsonEncoder::class,
21-
// \Symfony\Component\Serializer\Encoder\CsvEncoder::class,
22-
// \Symfony\Component\Serializer\Encoder\XmlEncoder::class,
23-
// \Symfony\Component\Serializer\Encoder\YamlEncoder::class,
44+
// Symfony\Component\Serializer\Encoder\JsonEncoder
2445
],
2546

2647
'metadata_loader' => new AnnotationLoader(new AnnotationReader()),

src/Bridge/Laravel/Providers/SerializerServiceProvider.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private function registerConfig(): void
6262
$config = $app['config'];
6363

6464
return Config::fromArray([
65-
'default_serializer' => $config->get('serializer.default_serializer'),
65+
'default' => $config->get('serializer.default'),
6666
'normalizers' => $config->get('serializer.normalizers'),
6767
'encoders' => $config->get('serializer.encoders'),
6868
'metadata_loader' => $config->get('serializer.metadata_loader'),
@@ -109,9 +109,9 @@ private function registerSerializerRegistry(): void
109109
$serializer = $app->make(SymfonySerializerInterface::class);
110110

111111
$serializers = [
112-
'symfony-json' => new Serializer($serializer, 'json'),
113-
'symfony-csv' => new Serializer($serializer, 'csv'),
114-
'symfony-xml' => new Serializer($serializer, 'xml'),
112+
'json' => new Serializer($serializer, 'json'),
113+
'csv' => new Serializer($serializer, 'csv'),
114+
'xml' => new Serializer($serializer, 'xml'),
115115
];
116116

117117
if (class_exists(Dumper::class)) {

src/Config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
final class Config implements ConfigRepository
1818
{
1919
private const REQUIRED_FIELDS = [
20-
'default_serializer',
20+
'default',
2121
'normalizers',
2222
'encoders',
2323
'metadata_loader',
@@ -34,7 +34,7 @@ public static function fromArray(array $config): self
3434
}
3535

3636
return new self(
37-
$config['default_serializer'],
37+
$config['default'],
3838
$config['normalizers'],
3939
$config['encoders'],
4040
$config['metadata_loader']

src/Contracts/SerializerInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ interface SerializerInterface
1111
public function serialize(mixed $payload): string|Stringable;
1212

1313
public function unserialize(string|Stringable $payload, string|object|null $type = null): mixed;
14+
15+
public function normalize(mixed $data, string $format = null, array $context = []);
1416
}

src/ResponseFactory.php

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@
66

77
use Illuminate\Http\Response as IlluminateResponse;
88
use Symfony\Component\HttpFoundation\Response;
9-
use Symfony\Component\Serializer\SerializerInterface;
9+
use WayOfDev\Serializer\Contracts\SerializerInterface;
1010

1111
final class ResponseFactory
1212
{
13+
/**
14+
* @property SerializerInterface $serializer
15+
*/
1316
private SerializerInterface $serializer;
1417

1518
private array $context = [];
1619

1720
private int $status = Response::HTTP_OK;
1821

19-
public function __construct(SerializerInterface $serializer)
22+
public function __construct(SerializerManager $serializer)
2023
{
21-
$this->serializer = $serializer;
24+
$this->serializer = $serializer->getSerializer('json');
2225
}
2326

2427
public function withStatusCode(int $code): void
@@ -33,24 +36,28 @@ public function withContext(array $context): void
3336

3437
public function create(object $response): Response
3538
{
36-
$content = $this->serializer->serialize(
37-
$response,
38-
'json',
39-
$this->context
39+
$content = $this->serializer->normalize(
40+
data: $response,
41+
context: $this->context
4042
);
4143

42-
return $this->respondWithJson($content, $this->status);
44+
return $this->respondWithJson(
45+
$this->serializer->serialize($content),
46+
$this->status
47+
);
4348
}
4449

4550
public function fromArray(array $response): Response
4651
{
47-
$content = $this->serializer->serialize(
48-
$response,
49-
'json',
50-
$this->context
52+
$content = $this->serializer->normalize(
53+
data: $response,
54+
context: $this->context
5155
);
5256

53-
return $this->respondWithJson($content, $this->status);
57+
return $this->respondWithJson(
58+
$this->serializer->serialize($content),
59+
$this->status
60+
);
5461
}
5562

5663
private function respondWithJson($content, int $status): IlluminateResponse

src/SerializerManager.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,9 @@ public function unserialize(
3232
): mixed {
3333
return $this->getSerializer($format ?? $this->defaultFormat)->unserialize($payload, $type);
3434
}
35+
36+
public function normalize(mixed $data, string $format = null, array $context = [])
37+
{
38+
return $this->getSerializer($format ?? $this->defaultFormat)->normalize($data, $format, $context);
39+
}
3540
}

tests/app/Item.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WayOfDev\Serializer\App;
6+
7+
use Ramsey\Uuid\Uuid;
8+
use Ramsey\Uuid\UuidInterface;
9+
use Symfony\Component\Serializer\Annotation\Groups;
10+
11+
final class Item
12+
{
13+
#[Groups(['default', 'private'])]
14+
private UuidInterface $id;
15+
16+
#[Groups(['default'])]
17+
private string $key = 'magic_number';
18+
19+
#[Groups(['default'])]
20+
private int $value = 12;
21+
22+
#[Groups(['private'])]
23+
private string $onlyForAdmin = 'secret';
24+
25+
public function id(): UuidInterface
26+
{
27+
return $this->id;
28+
}
29+
30+
public function key(): string
31+
{
32+
return $this->key;
33+
}
34+
35+
public function value(): int
36+
{
37+
return $this->value;
38+
}
39+
40+
public function onlyForAdmin(): string
41+
{
42+
return $this->onlyForAdmin;
43+
}
44+
45+
public function __construct()
46+
{
47+
$this->id = Uuid::fromString('0cd74c72-8920-4e4e-86c3-19fdd5103514');
48+
}
49+
}

tests/app/Response.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WayOfDev\Serializer\App;
6+
7+
use ArrayIterator;
8+
use Webmozart\Assert\Assert;
9+
10+
final class Response extends ArrayIterator
11+
{
12+
public static function create(array $items): self
13+
{
14+
return new self($items);
15+
}
16+
17+
private function __construct(array $items)
18+
{
19+
Assert::allIsInstanceOf($items, Item::class);
20+
Assert::isList($items);
21+
22+
parent::__construct($items);
23+
}
24+
}

tests/app/array.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
return [
6+
'random_object' => [
7+
'person' => [
8+
'first_name' => 'John Doe',
9+
'last_name' => 'Zhmyshenko',
10+
'birthdate' => '01.01.1976',
11+
'birth_place' => 'Chuguev',
12+
'nationality' => 'ukrainian',
13+
],
14+
],
15+
];

tests/src/Normalizers/RamseyUuidNormalizerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ public static function serializeDataProvider(): Traversable
2020
yield [
2121
'{"uuid":"1d96a152-9838-43a0-a189-159befc9e38f","name":"some"}',
2222
new Author(Uuid::fromString('1d96a152-9838-43a0-a189-159befc9e38f'), 'some'),
23-
'symfony-json',
23+
'json',
2424
];
2525
yield [
2626
'uuid,name1d96a152-9838-43a0-a189-159befc9e38f,some',
2727
new Author(Uuid::fromString('1d96a152-9838-43a0-a189-159befc9e38f'), 'some'),
28-
'symfony-csv',
28+
'csv',
2929
];
3030
yield [
3131
'{uuid:1d96a152-9838-43a0-a189-159befc9e38f,name:some}',
@@ -55,7 +55,7 @@ public function unserialize(): void
5555
$result = $manager->unserialize(
5656
'{"uuid":"1d96a152-9838-43a0-a189-159befc9e38f","name":"some"}',
5757
Author::class,
58-
'symfony-json'
58+
'json'
5959
);
6060

6161
$this::assertInstanceOf(Author::class, $result);

tests/src/ResponseFactoryTest.php

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
namespace WayOfDev\Serializer\Tests;
66

7-
use Symfony\Component\Serializer\SerializerInterface;
8-
use WayOfDev\Serializer\App\Entity;
97
use WayOfDev\Serializer\App\Item;
8+
use WayOfDev\Serializer\App\Object\Post;
109
use WayOfDev\Serializer\App\Response;
1110
use WayOfDev\Serializer\ResponseFactory;
11+
use WayOfDev\Serializer\SerializerManager;
1212

1313
final class ResponseFactoryTest extends TestCase
1414
{
@@ -17,36 +17,42 @@ final class ResponseFactoryTest extends TestCase
1717
*/
1818
public function it_creates_response(): void
1919
{
20-
$responseFactory = new ResponseFactory(app(SerializerInterface::class));
21-
$response = $responseFactory->create(new Entity());
20+
$responseFactory = new ResponseFactory(app(SerializerManager::class));
21+
$response = $responseFactory->create(new Post(
22+
id: 1,
23+
text: 'Some text',
24+
active: true,
25+
views: 777,
26+
));
2227

2328
self::assertEquals(200, $response->getStatusCode());
24-
self::assertEquals('{"items":[]}', $response->getContent());
29+
self::assertEquals('{"id":1,"text":"Some text","active":true,"views":777}', $response->getContent());
2530
}
2631

2732
/**
2833
* @test
2934
*/
3035
public function it_creates_from_array_iterator(): void
3136
{
32-
$responseFactory = new ResponseFactory(app(SerializerInterface::class));
37+
$responseFactory = new ResponseFactory(app(SerializerManager::class));
38+
$responseFactory->withContext(['groups' => ['default']]);
3339
$response = $responseFactory->create(Response::create([new Item()]));
3440

3541
self::assertEquals(200, $response->getStatusCode());
36-
self::assertEquals('[{"key":"magic_number","value":12}]', $response->getContent());
42+
self::assertEquals('[{"id":"0cd74c72-8920-4e4e-86c3-19fdd5103514","key":"magic_number","value":12}]', $response->getContent());
3743
}
3844

3945
/**
4046
* @test
4147
*/
4248
public function it_creates_response_from_array(): void
4349
{
44-
$responseFactory = new ResponseFactory(app(SerializerInterface::class));
45-
$response = $responseFactory->fromArray(require __DIR__ . '/../app/stub_array.php');
50+
$responseFactory = new ResponseFactory(app(SerializerManager::class));
51+
$response = $responseFactory->fromArray(require __DIR__ . '/../app/array.php');
4652

4753
self::assertEquals(200, $response->getStatusCode());
4854
self::assertEquals(
49-
'{"random_object":{"person":{"first_name":"Valery Albertovich","last_name":"Zhmyshenko","birthdate":"01.01.1976","birth_place":"Chuguev","nationality":"ukrainian"}}}',
55+
'{"random_object":{"person":{"first_name":"John Doe","last_name":"Zhmyshenko","birthdate":"01.01.1976","birth_place":"Chuguev","nationality":"ukrainian"}}}',
5056
$response->getContent()
5157
);
5258
}
@@ -56,26 +62,26 @@ public function it_creates_response_from_array(): void
5662
*/
5763
public function it_sets_non_default_status_code(): void
5864
{
59-
$responseFactory = new ResponseFactory(app(SerializerInterface::class));
65+
$responseFactory = new ResponseFactory(app(SerializerManager::class));
6066
$responseFactory->withStatusCode(404);
6167
$responseFactory->withContext(['groups' => 'default']);
62-
$response = $responseFactory->create(new Entity());
68+
$response = $responseFactory->create(new Item());
6369

6470
self::assertEquals(404, $response->getStatusCode());
65-
self::assertEquals('{"items":[],"amount":777,"text":"Some String"}', $response->getContent());
71+
self::assertEquals('{"id":"0cd74c72-8920-4e4e-86c3-19fdd5103514","key":"magic_number","value":12}', $response->getContent());
6672
}
6773

6874
/**
6975
* @test
7076
*/
7177
public function it_uses_given_context(): void
7278
{
73-
$responseFactory = new ResponseFactory(app(SerializerInterface::class));
74-
$responseFactory->withContext(['groups' => 'default']);
79+
$responseFactory = new ResponseFactory(app(SerializerManager::class));
80+
$responseFactory->withContext(['groups' => 'private']);
7581

76-
$response = $responseFactory->create(new Entity());
82+
$response = $responseFactory->create(new Item());
7783

7884
self::assertEquals(200, $response->getStatusCode());
79-
self::assertEquals('{"items":[],"amount":777,"text":"Some String"}', $response->getContent());
85+
self::assertEquals('{"id":"0cd74c72-8920-4e4e-86c3-19fdd5103514","onlyForAdmin":"secret"}', $response->getContent());
8086
}
8187
}

0 commit comments

Comments
 (0)