From 22c8ed143355b5356254168ca3cb278e28258c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4dlich?= Date: Thu, 28 Sep 2023 15:14:01 +0200 Subject: [PATCH] code challenge 4 solution --- CODING-CHALLENGE-5.md | 27 +++++++++ src/Pagination/AttendeeCollectionFactory.php | 10 +++- src/Pagination/PaginatedCollection.php | 12 ++++ src/Pagination/PaginatedCollectionFactory.php | 38 ++++++++++++- src/Pagination/WorkshopCollectionFactory.php | 10 +++- src/Serializer/AttendeeNormalizer.php | 16 +++++- src/Serializer/WorkshopNormalizer.php | 16 +++++- ..._test_it_should_list_all_attendees__1.json | 27 ++++++++- ...ta set show 1st page, 3 items each__1.json | 46 +++++++++++++-- ...ta set show 1st page, 5 items each__1.json | 57 +++++++++++++++++-- ...ta set show 2nd page, 3 items each__1.json | 36 +++++++++++- ...ta set show 2nd page, 5 items each__1.json | 10 +++- ..._it_should_show_requested_attendee__1.json | 20 ++++++- ..._test_it_should_list_all_workshops__1.json | 27 ++++++++- ...ta set show 1st page, 3 items each__1.json | 46 +++++++++++++-- ...ta set show 1st page, 5 items each__1.json | 57 +++++++++++++++++-- ...ta set show 2nd page, 3 items each__1.json | 36 +++++++++++- ...ta set show 2nd page, 5 items each__1.json | 10 +++- ..._it_should_show_requested_workshop__1.json | 20 ++++++- 19 files changed, 475 insertions(+), 46 deletions(-) create mode 100644 CODING-CHALLENGE-5.md diff --git a/CODING-CHALLENGE-5.md b/CODING-CHALLENGE-5.md new file mode 100644 index 0000000..43d0e63 --- /dev/null +++ b/CODING-CHALLENGE-5.md @@ -0,0 +1,27 @@ +# RESTful Webservices in Symfony + +## Coding Challenge 5 - Content Negotiation + +### Tasks + +- set the correct format option (JSON or XML) of the current Request +- read the `Accept` request header and negotiate the content-type using Will Durand's negotiation library + +### Solution + +- require the willdurand/negotiation library: `composer require willdurand/negotiation` +- create a `ContentNegotiator` class, use the `RequestStack` and implement a method to retrieve + the negotiated request format (`json` should be the default request format) +- create a `RequestFormatListener`, subscribe on the `kernel.request` event (priority: 8) and + use the `ContentNegotiator` to set the request's request format +- adjust all your Controllers, Normalizers and Data Transfer Objects to provide your representation of + your resources in the format accepted by the client + +#### Hints + +You can get the best fitting format by using: + +``` +$negotiator = new Negotiator(); +$acceptHeader = $negotiator->getBest($request->getAcceptableContentTypes(), self::ACCEPTED_CONTENT_TYPES); +``` diff --git a/src/Pagination/AttendeeCollectionFactory.php b/src/Pagination/AttendeeCollectionFactory.php index 82618f0..42738e7 100644 --- a/src/Pagination/AttendeeCollectionFactory.php +++ b/src/Pagination/AttendeeCollectionFactory.php @@ -6,16 +6,24 @@ use App\Repository\AttendeeRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; final class AttendeeCollectionFactory extends PaginatedCollectionFactory { public function __construct( - private readonly AttendeeRepository $attendeeRepository + private readonly AttendeeRepository $attendeeRepository, + UrlGeneratorInterface $urlGenerator, ) { + parent::__construct($urlGenerator); } public function getRepository(): ServiceEntityRepositoryInterface { return $this->attendeeRepository; } + + public function getRouteName(): string + { + return 'list_attendee'; + } } diff --git a/src/Pagination/PaginatedCollection.php b/src/Pagination/PaginatedCollection.php index 7b8ffef..5ff5cbc 100644 --- a/src/Pagination/PaginatedCollection.php +++ b/src/Pagination/PaginatedCollection.php @@ -4,16 +4,28 @@ namespace App\Pagination; +use Symfony\Component\Serializer\Annotation\SerializedName; + final class PaginatedCollection { public readonly array $items; public readonly int $total; public readonly int $count; + #[SerializedName('_links')] + public array $links; + public function __construct(\Iterator $items, int $total) { $this->items = iterator_to_array($items); $this->total = $total; $this->count = \count($this->items); } + + public function addLink(string $rel, string $href): self + { + $this->links[$rel]['href'] = $href; + + return $this; + } } diff --git a/src/Pagination/PaginatedCollectionFactory.php b/src/Pagination/PaginatedCollectionFactory.php index 181b557..90b0490 100644 --- a/src/Pagination/PaginatedCollectionFactory.php +++ b/src/Pagination/PaginatedCollectionFactory.php @@ -6,11 +6,19 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface; use Doctrine\ORM\Tools\Pagination\Paginator; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; abstract class PaginatedCollectionFactory { + public function __construct( + private readonly UrlGeneratorInterface $urlGenerator + ) { + } + abstract public function getRepository(): ServiceEntityRepositoryInterface; + abstract public function getRouteName(): string; + public function create(int $page, int $size): PaginatedCollection { $query = $this->getRepository() @@ -21,12 +29,38 @@ public function create(int $page, int $size): PaginatedCollection $paginator = new Paginator($query); $total = count($paginator); + $pageCount = (int) ceil($total / $size); $paginator ->getQuery() ->setFirstResult($size * ($page - 1)) - ->setMaxResults($size); + ->setMaxResults($size) + ; + + $paginatedCollection = new PaginatedCollection($paginator->getIterator(), $total); + + $routeName = $this->getRouteName(); + + $paginatedCollection + ->addLink('self', $this->urlGenerator->generate($routeName, ['page' => $page, 'size' => $size])); + + if (1 < $pageCount) { + $paginatedCollection + ->addLink('first', $this->urlGenerator->generate($routeName, ['page' => 1, 'size' => $size])) + ->addLink('last', $this->urlGenerator->generate($routeName, ['page' => $pageCount, 'size' => $size])) + ; + } + + if ($page < $pageCount) { + $paginatedCollection + ->addLink('next', $this->urlGenerator->generate($routeName, ['page' => $page + 1, 'size' => $size])); + } + + if ($page > 1) { + $paginatedCollection + ->addLink('prev', $this->urlGenerator->generate($routeName, ['page' => $page - 1, 'size' => $size])); + } - return new PaginatedCollection($paginator->getIterator(), $total); + return $paginatedCollection; } } diff --git a/src/Pagination/WorkshopCollectionFactory.php b/src/Pagination/WorkshopCollectionFactory.php index e5015e0..d22442c 100644 --- a/src/Pagination/WorkshopCollectionFactory.php +++ b/src/Pagination/WorkshopCollectionFactory.php @@ -6,16 +6,24 @@ use App\Repository\WorkshopRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; final class WorkshopCollectionFactory extends PaginatedCollectionFactory { public function __construct( - private readonly WorkshopRepository $workshopRepository + private readonly WorkshopRepository $workshopRepository, + UrlGeneratorInterface $urlGenerator, ) { + parent::__construct($urlGenerator); } public function getRepository(): ServiceEntityRepositoryInterface { return $this->workshopRepository; } + + public function getRouteName(): string + { + return 'list_workshop'; + } } diff --git a/src/Serializer/AttendeeNormalizer.php b/src/Serializer/AttendeeNormalizer.php index 3035b65..29081a0 100644 --- a/src/Serializer/AttendeeNormalizer.php +++ b/src/Serializer/AttendeeNormalizer.php @@ -6,6 +6,7 @@ use App\Entity\Attendee; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -14,7 +15,8 @@ final class AttendeeNormalizer implements NormalizerInterface public function __construct( // see: https://github.com/symfony/maker-bundle/issues/1252 #[Autowire(service: 'serializer.normalizer.object')] - private readonly NormalizerInterface $normalizer + private readonly NormalizerInterface $normalizer, + private UrlGeneratorInterface $urlGenerator ) { } @@ -37,7 +39,17 @@ public function normalize($object, string $format = null, array $context = []) $context = array_merge($context, $customContext); - return $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context); + + if (\is_array($data)) { + $data['_links']['self']['href'] = $this->urlGenerator->generate('read_workshop', [ + 'identifier' => $object->getIdentifier(), + ]); + + $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_workshop'); + } + + return $data; } // see: https://github.com/symfony/symfony-docs/issues/18042 diff --git a/src/Serializer/WorkshopNormalizer.php b/src/Serializer/WorkshopNormalizer.php index 5b23d09..e0fdf0b 100644 --- a/src/Serializer/WorkshopNormalizer.php +++ b/src/Serializer/WorkshopNormalizer.php @@ -6,6 +6,7 @@ use App\Entity\Workshop; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -14,7 +15,8 @@ final class WorkshopNormalizer implements NormalizerInterface public function __construct( // see: https://github.com/symfony/maker-bundle/issues/1252 #[Autowire(service: 'serializer.normalizer.object')] - private readonly NormalizerInterface $normalizer + private readonly NormalizerInterface $normalizer, + private UrlGeneratorInterface $urlGenerator, ) { } @@ -37,7 +39,17 @@ public function normalize($object, string $format = null, array $context = []) $context = array_merge($context, $customContext); - return $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context); + + if (\is_array($data)) { + $data['_links']['self']['href'] = $this->urlGenerator->generate('read_attendee', [ + 'identifier' => $object->getIdentifier(), + ]); + + $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_attendee'); + } + + return $data; } // see: https://github.com/symfony/symfony-docs/issues/18042 diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees__1.json index aef2fdc..543aa13 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees__1.json @@ -12,11 +12,32 @@ "workshop_date": "2022-06-14", "attendees": [ "Jan Sch\u00e4dlich" - ] + ], + "_links": { + "self": { + "href": "\/attendees\/abba667a-96ae-4f75-9b71-97819b682e8d" + }, + "collection": { + "href": "\/attendees" + } + } } - ] + ], + "_links": { + "self": { + "href": "\/workshops\/803449f4-9a4c-4ecb-8ce4-cebc804fe70a" + }, + "collection": { + "href": "\/workshops" + } + } } ], "total": 1, - "count": 1 + "count": 1, + "_links": { + "self": { + "href": "\/attendees?page=1&size=10" + } + } } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json index c7a3368..d6dca91 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json @@ -5,23 +5,61 @@ "firstname": "a", "lastname": "1", "email": "a1@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/4878f198-36ab-4fe3-8189-19662a9764fa" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "e942ce16-27c2-494f-9d93-03412da980c5", "firstname": "b", "lastname": "2", "email": "b2@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/e942ce16-27c2-494f-9d93-03412da980c5" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "4714fb8a-83d8-49af-abbf-7c68fc6c9656", "firstname": "c", "lastname": "3", "email": "c3@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/4714fb8a-83d8-49af-abbf-7c68fc6c9656" + }, + "collection": { + "href": "\/workshops" + } + } } ], "total": 5, - "count": 3 + "count": 3, + "_links": { + "self": { + "href": "\/attendees?page=1&size=3" + }, + "first": { + "href": "\/attendees?page=1&size=3" + }, + "last": { + "href": "\/attendees?page=2&size=3" + }, + "next": { + "href": "\/attendees?page=2&size=3" + } + } } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json index f6ffe29..894de6a 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json @@ -5,37 +5,82 @@ "firstname": "a", "lastname": "1", "email": "a1@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/4878f198-36ab-4fe3-8189-19662a9764fa" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "e942ce16-27c2-494f-9d93-03412da980c5", "firstname": "b", "lastname": "2", "email": "b2@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/e942ce16-27c2-494f-9d93-03412da980c5" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "4714fb8a-83d8-49af-abbf-7c68fc6c9656", "firstname": "c", "lastname": "3", "email": "c3@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/4714fb8a-83d8-49af-abbf-7c68fc6c9656" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "65445e8c-a6c6-4955-9eb2-5fb60d6a991e", "firstname": "d", "lastname": "4", "email": "d4@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/65445e8c-a6c6-4955-9eb2-5fb60d6a991e" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "3aacd688-5b81-4aba-a5ea-ac7668ba95b6", "firstname": "e", "lastname": "5", "email": "e5@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/3aacd688-5b81-4aba-a5ea-ac7668ba95b6" + }, + "collection": { + "href": "\/workshops" + } + } } ], "total": 5, - "count": 5 + "count": 5, + "_links": { + "self": { + "href": "\/attendees?page=1&size=5" + } + } } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json index dd26fbd..4643d9e 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json @@ -5,16 +5,46 @@ "firstname": "d", "lastname": "4", "email": "d4@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/65445e8c-a6c6-4955-9eb2-5fb60d6a991e" + }, + "collection": { + "href": "\/workshops" + } + } }, { "identifier": "3aacd688-5b81-4aba-a5ea-ac7668ba95b6", "firstname": "e", "lastname": "5", "email": "e5@test.de", - "workshops": [] + "workshops": [], + "_links": { + "self": { + "href": "\/workshops\/3aacd688-5b81-4aba-a5ea-ac7668ba95b6" + }, + "collection": { + "href": "\/workshops" + } + } } ], "total": 5, - "count": 2 + "count": 2, + "_links": { + "self": { + "href": "\/attendees?page=2&size=3" + }, + "first": { + "href": "\/attendees?page=1&size=3" + }, + "last": { + "href": "\/attendees?page=2&size=3" + }, + "prev": { + "href": "\/attendees?page=1&size=3" + } + } } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json index b985846..e6c1a1f 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json @@ -1,5 +1,13 @@ { "items": [], "total": 5, - "count": 0 + "count": 0, + "_links": { + "self": { + "href": "\/attendees?page=2&size=5" + }, + "prev": { + "href": "\/attendees?page=1&size=5" + } + } } diff --git a/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee__1.json b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee__1.json index 763a24e..2388fd4 100644 --- a/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee__1.json +++ b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee__1.json @@ -10,7 +10,23 @@ "workshop_date": "2022-06-14", "attendees": [ "Jan Sch\u00e4dlich" - ] + ], + "_links": { + "self": { + "href": "\/attendees\/12bc7c60-7f77-41e0-90eb-9996a64a3b14" + }, + "collection": { + "href": "\/attendees" + } + } } - ] + ], + "_links": { + "self": { + "href": "\/workshops\/17058af8-1b0f-4afe-910d-669b4bd0fd26" + }, + "collection": { + "href": "\/workshops" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops__1.json index 46094b5..ce823b4 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops__1.json @@ -12,11 +12,32 @@ "email": "schaedlich.jan@gmail.com", "workshops": [ "RESTful Webservices in Symfony" - ] + ], + "_links": { + "self": { + "href": "\/workshops\/2a451d60-2fef-437b-838e-10edb2ade8eb" + }, + "collection": { + "href": "\/workshops" + } + } } - ] + ], + "_links": { + "self": { + "href": "\/attendees\/ef6e13ef-f601-4e74-bae8-bdf8f41cd8db" + }, + "collection": { + "href": "\/attendees" + } + } } ], "total": 1, - "count": 1 + "count": 1, + "_links": { + "self": { + "href": "\/workshops?page=1&size=10" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json index 5fa4b11..db902d7 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json @@ -4,21 +4,59 @@ "identifier": "775ead00-7f50-466f-b89d-73150c9adfc8", "title": "a", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/775ead00-7f50-466f-b89d-73150c9adfc8" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2", "title": "b", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "531b95c9-05c5-49ac-98ec-e0263f35d83e", "title": "c", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/531b95c9-05c5-49ac-98ec-e0263f35d83e" + }, + "collection": { + "href": "\/attendees" + } + } } ], "total": 5, - "count": 3 + "count": 3, + "_links": { + "self": { + "href": "\/workshops?page=1&size=3" + }, + "first": { + "href": "\/workshops?page=1&size=3" + }, + "last": { + "href": "\/workshops?page=2&size=3" + }, + "next": { + "href": "\/workshops?page=2&size=3" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json index 53fee32..3c38d22 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json @@ -4,33 +4,78 @@ "identifier": "775ead00-7f50-466f-b89d-73150c9adfc8", "title": "a", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/775ead00-7f50-466f-b89d-73150c9adfc8" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2", "title": "b", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "531b95c9-05c5-49ac-98ec-e0263f35d83e", "title": "c", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/531b95c9-05c5-49ac-98ec-e0263f35d83e" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "d0e2cbfd-eff0-4025-90fb-3350aada0939", "title": "d", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/d0e2cbfd-eff0-4025-90fb-3350aada0939" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "9aa6e731-6058-4f2c-9081-aadeb15db35e", "title": "e", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/9aa6e731-6058-4f2c-9081-aadeb15db35e" + }, + "collection": { + "href": "\/attendees" + } + } } ], "total": 5, - "count": 5 + "count": 5, + "_links": { + "self": { + "href": "\/workshops?page=1&size=5" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json index 349d498..55e4f54 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json @@ -4,15 +4,45 @@ "identifier": "d0e2cbfd-eff0-4025-90fb-3350aada0939", "title": "d", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/d0e2cbfd-eff0-4025-90fb-3350aada0939" + }, + "collection": { + "href": "\/attendees" + } + } }, { "identifier": "9aa6e731-6058-4f2c-9081-aadeb15db35e", "title": "e", "workshop_date": "2021-12-07", - "attendees": [] + "attendees": [], + "_links": { + "self": { + "href": "\/attendees\/9aa6e731-6058-4f2c-9081-aadeb15db35e" + }, + "collection": { + "href": "\/attendees" + } + } } ], "total": 5, - "count": 2 + "count": 2, + "_links": { + "self": { + "href": "\/workshops?page=2&size=3" + }, + "first": { + "href": "\/workshops?page=1&size=3" + }, + "last": { + "href": "\/workshops?page=2&size=3" + }, + "prev": { + "href": "\/workshops?page=1&size=3" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json index b985846..eab1cbb 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json @@ -1,5 +1,13 @@ { "items": [], "total": 5, - "count": 0 + "count": 0, + "_links": { + "self": { + "href": "\/workshops?page=2&size=5" + }, + "prev": { + "href": "\/workshops?page=1&size=5" + } + } } diff --git a/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop__1.json b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop__1.json index 0bb7af6..bd4a30c 100644 --- a/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop__1.json +++ b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop__1.json @@ -10,7 +10,23 @@ "email": "schaedlich.jan@gmail.com", "workshops": [ "RESTful Webservices in Symfony" - ] + ], + "_links": { + "self": { + "href": "\/workshops\/36ffcf19-7560-4ead-8d8e-3c40cf169784" + }, + "collection": { + "href": "\/workshops" + } + } } - ] + ], + "_links": { + "self": { + "href": "\/attendees\/8acf8f2b-95c1-46e1-85a4-ea6ff88081ce" + }, + "collection": { + "href": "\/attendees" + } + } }