Skip to content

Commit aeb8ea1

Browse files
committed
code challenge 1 solution
1 parent 3815afc commit aeb8ea1

12 files changed

+184
-0
lines changed

CODING-CHALLENGE-2.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# RESTful Webservices in Symfony
2+
3+
## Coding Challenge 2 - Serialization
4+
5+
### Tasks
6+
7+
- use Symfony's Serializer to transform the workshop and attendee entities into a JSON representation
8+
- use `make tests` to check if the api endpoints still work as expected
9+
- make sure that all JSON property names are formatted in _snake_case_
10+
- add an attendee to one of the workshops and use the PostMan collection to test the endpoints
11+
12+
### Solution
13+
14+
- require the Symfony Serializer component: `composer req serializer`
15+
- inject the Serializer into your controllers
16+
- serialize your entities
17+
- create a custom `Normalizer` (implement `ContextAwareNormalizerInterface`) for your Workshop and Attendee entities
18+
- remove the `toArray` method in your entities
19+
- fix the `CircularReferenceException`
20+
21+
#### Problem 1: No snake_case property names anymore
22+
23+
```yaml
24+
# config/packages/framework.yaml
25+
26+
framework:
27+
# ...
28+
serializer:
29+
name_converter: 'serializer.name_converter.camel_case_to_snake_case'
30+
```
31+
32+
#### Problem 2: Fixing the `CircularReferenceException`
33+
34+
Adding an attendee to a workshop and accessing this attendee, or the respective workshop results in a `CircularReferenceException`.
35+
36+
```php
37+
// AttendeeNormalizer
38+
39+
$defaultContext = [
40+
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => fn ($object, $format, $context) => $object->getFirstname().' '.$object->getLastname(),
41+
];
42+
```
43+
44+
```php
45+
// WorkshopNormalizer
46+
47+
$customContext = [
48+
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => fn ($object, $format, $context) => $object->getTitle(),
49+
];
50+
```

fixtures/attendees.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
App\Entity\Attendee:
2+
attendee_1:
3+
__construct: [ '02d47053-4034-4818-97d1-299c4cd7168d', 'Ricardo', 'S.', 'ricardo@test.de' ]
4+
attendee_2:
5+
__construct: [ '6047c255-593f-4fa8-888b-f919fafd904f', 'Jonas', 'G.', 'jonas@test.de' ]
6+
attendee_3:
7+
__construct: [ '92e06a4b-19ac-4e67-b988-00e7250929c6', 'Cristoforo', 'C.', 'cristoforo@test.de' ]
8+
attendee_4:
9+
__construct: [ 'c153c6c3-c43b-449d-8f5a-2bfb627f9822', 'Dennis', 'P.', 'dennis@test.de' ]
10+
attendee_5:
11+
__construct: [ 'f5e2c329-f5b8-4e4b-860d-b5a0189ef35f', 'Py', 'K.', 'py@test.de' ]
12+
attendee_6:
13+
__construct: [ '0fcb1a10-d2c8-4202-9bc0-840b9683583c', 'Krishna', 'S.', 'krishna@test.de' ]
14+
attendee_7:
15+
__construct: [ '0eca8ea8-a0e6-44be-9539-44226cb5ccbc', 'Nick', 'S.', 'nick@test.de' ]

src/Controller/Attendee/.gitignore

Whitespace-only changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Controller\Attendee;
6+
7+
use App\Entity\Attendee;
8+
use App\Repository\AttendeeRepository;
9+
use Symfony\Component\HttpFoundation\Response;
10+
use Symfony\Component\Routing\Annotation\Route;
11+
12+
#[Route('/attendees', name: 'list_attendee', methods: ['GET'])]
13+
final class ListController
14+
{
15+
public function __construct(
16+
private AttendeeRepository $attendeeRepository
17+
) {
18+
}
19+
20+
public function __invoke(): Response
21+
{
22+
$allAttendees = $this->attendeeRepository->findAll();
23+
24+
$allAttendeesAsArray = array_map(
25+
static fn (Attendee $attendee) => $attendee->toArray(),
26+
$allAttendees
27+
);
28+
29+
return new Response(json_encode($allAttendeesAsArray), Response::HTTP_OK, [
30+
'Content-Type' => 'application/json',
31+
]);
32+
}
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Controller\Attendee;
6+
7+
use App\Entity\Attendee;
8+
use Symfony\Component\HttpFoundation\Response;
9+
use Symfony\Component\Routing\Annotation\Route;
10+
11+
#[Route('/attendees/{identifier}', name: 'read_attendee', methods: ['GET'])]
12+
final class ReadController
13+
{
14+
public function __invoke(Attendee $attendee): Response
15+
{
16+
return new Response(json_encode($attendee->toArray()), Response::HTTP_OK, [
17+
'Content-Type' => 'application/json',
18+
]);
19+
}
20+
}

tests/Controller/Attendee/.gitignore

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Tests\Controller\Attendee;
6+
7+
use App\Tests\ApiTestCase;
8+
9+
class ListControllerTest extends ApiTestCase
10+
{
11+
public function test_it_should_list_all_attendees(): void
12+
{
13+
$this->loadFixtures([
14+
__DIR__.'/fixtures/list_attendee.yaml',
15+
]);
16+
17+
$this->browser->request('GET', '/attendees');
18+
19+
static::assertResponseIsSuccessful();
20+
21+
$this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent());
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Tests\Controller\Attendee;
6+
7+
use App\Tests\ApiTestCase;
8+
9+
class ReadControllerTest extends ApiTestCase
10+
{
11+
public function test_it_should_show_requested_attendee(): void
12+
{
13+
$this->loadFixtures([
14+
__DIR__.'/fixtures/read_attendee.yaml',
15+
]);
16+
17+
$this->browser->request('GET', '/attendees/17058af8-1b0f-4afe-910d-669b4bd0fd26');
18+
19+
static::assertResponseIsSuccessful();
20+
21+
$this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent());
22+
}
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"identifier": "803449f4-9a4c-4ecb-8ce4-cebc804fe70a",
4+
"firstname": "Jan",
5+
"lastname": "Sch\u00e4dlich",
6+
"email": "jan.schadlich@siemens.com"
7+
}
8+
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"identifier": "17058af8-1b0f-4afe-910d-669b4bd0fd26",
3+
"firstname": "Jan",
4+
"lastname": "Sch\u00e4dlich",
5+
"email": "jan.schadlich@siemens.com"
6+
}

0 commit comments

Comments
 (0)