From 9a0989e3cab1ef7faceeabc3217098a54c75906d Mon Sep 17 00:00:00 2001 From: Aymeric Ratinaud Date: Mon, 2 Aug 2021 15:33:15 +0200 Subject: [PATCH] Custom Paginator in Provider with QueryBuilder --- README.md | 18 ++- config/services.yaml | 2 +- fixtures/album.yaml | 5 + fixtures/artist.yaml | 3 + fixtures/post.yaml | 7 + ...ovider.php => JobEmployeeDataProvider.php} | 9 +- src/DataProvider/Pagination/PostPaginator.php | 79 ++++++++++ .../PostCollectionDataProvider.php | 34 +++++ src/Entity/Album.php | 81 +++++++++++ src/Entity/Artist.php | 99 +++++++++++++ src/Entity/Post.php | 135 ++++++++++++++++++ src/Repository/AlbumRepository.php | 50 +++++++ src/Repository/ArtistRepository.php | 50 +++++++ src/Repository/PostRepository.php | 34 +++++ 14 files changed, 600 insertions(+), 6 deletions(-) create mode 100644 fixtures/album.yaml create mode 100644 fixtures/artist.yaml create mode 100644 fixtures/post.yaml rename src/DataProvider/{JobDataProvider.php => JobEmployeeDataProvider.php} (71%) create mode 100644 src/DataProvider/Pagination/PostPaginator.php create mode 100644 src/DataProvider/PostCollectionDataProvider.php create mode 100644 src/Entity/Album.php create mode 100644 src/Entity/Artist.php create mode 100644 src/Entity/Post.php create mode 100644 src/Repository/AlbumRepository.php create mode 100644 src/Repository/ArtistRepository.php create mode 100644 src/Repository/PostRepository.php diff --git a/README.md b/README.md index 294742e..40e1528 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Inspired by https://github.com/api-platform/demo - [Fifth example use JobCollectionDataProvider (paginationExtension)](#fifth-example-use-jobcollectiondataprovider-paginationextension) - [Sixth example use FurnitureDataProvider (collectionDataProvider)](#sixth-example-use-furnituredataprovider-collectiondataprovider) - [Seventh example use QueryBuilder in subresource](#seventh-example--simple-dataprovider-using-subresourcedataprovider) -- [Eight example use QueryBuilder in subresource](#eight-example-use-querybuilder-in-subresource) +- [Eighth example use QueryBuilder in subresource](#eight-example-use-querybuilder-in-subresource) - [Ninth use custom subresource with provider (without subresourceDataProvider)](#ninth-example---custom-subresource-with-provider-without-subresourcedataprovider) @@ -105,7 +105,7 @@ CommentSubresourceDataProvider show how use the standard behaviour `api/movies/{id}/comments` -## Eight example use QueryBuilder in subresource +## Eighth example use QueryBuilder in subresource DiscussionSubresourceDataProvider show how use QueryBuilder and order by DESC the result @@ -121,7 +121,19 @@ With JobDataProvider and path `jobs/{id}/employees` return employees from id's j ### Usage -`api/jobs/{id}/employees` +`api/jobs/{id}/employees/{arg1}` + +## Tenth example - Custom Paginator in Provider with QueryBuilder + +PostCollectionDataProvider call the method findLatest from PostRepository and PostRepository call PostPaginator + +### Usage + +`/api/posts?page=2` + +## Notes + +`Album` and `Artist` are ready to be used ## Install diff --git a/config/services.yaml b/config/services.yaml index a9b4a67..233557f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -38,6 +38,6 @@ services: bind: $collectionDataProvider: '@api_platform.doctrine.orm.default.collection_data_provider' - App\DataProvider\JobDataProvider: + App\DataProvider\JobEmployeeDataProvider: bind: $itemDataProvider: '@api_platform.doctrine.orm.default.item_data_provider' diff --git a/fixtures/album.yaml b/fixtures/album.yaml new file mode 100644 index 0000000..9be1a4c --- /dev/null +++ b/fixtures/album.yaml @@ -0,0 +1,5 @@ +App\Entity\Album: + album_{1..1000}: + title: + year: '' + artist: '@artist*' diff --git a/fixtures/artist.yaml b/fixtures/artist.yaml new file mode 100644 index 0000000..920e177 --- /dev/null +++ b/fixtures/artist.yaml @@ -0,0 +1,3 @@ +App\Entity\Artist: + artist_{1..10}: + author: diff --git a/fixtures/post.yaml b/fixtures/post.yaml new file mode 100644 index 0000000..56450d1 --- /dev/null +++ b/fixtures/post.yaml @@ -0,0 +1,7 @@ +App\Entity\Post: + post_{1..100}: + title: + slug: + summary: + content: + publishedAt: '' diff --git a/src/DataProvider/JobDataProvider.php b/src/DataProvider/JobEmployeeDataProvider.php similarity index 71% rename from src/DataProvider/JobDataProvider.php rename to src/DataProvider/JobEmployeeDataProvider.php index c5b59bb..75b16a2 100644 --- a/src/DataProvider/JobDataProvider.php +++ b/src/DataProvider/JobEmployeeDataProvider.php @@ -6,16 +6,19 @@ use ApiPlatform\Core\DataProvider\DenormalizedIdentifiersAwareItemDataProviderInterface; use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; +use ApiPlatform\Core\DataProvider\Pagination; use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface; use App\Entity\Job; -final class JobDataProvider implements RestrictedDataProviderInterface, DenormalizedIdentifiersAwareItemDataProviderInterface +final class JobEmployeeDataProvider implements RestrictedDataProviderInterface, DenormalizedIdentifiersAwareItemDataProviderInterface { private $itemDataProvider; + private $pagination; - public function __construct(ItemDataProviderInterface $itemDataProvider) + public function __construct(ItemDataProviderInterface $itemDataProvider, Pagination $pagination) { $this->itemDataProvider = $itemDataProvider; + $this->pagination = $pagination; } public function supports(string $resourceClass, string $operationName = null, array $context = []): bool @@ -30,6 +33,8 @@ public function getItem(string $resourceClass, $id, string $operationName = null { $itemDataProvider = $this->itemDataProvider->getItem($resourceClass, $id, $operationName, $context); + [$page, $offset, $itemPerPage] = $this->pagination->getPagination($resourceClass, $operationName, $context); + return $itemDataProvider->getEmployees()->getValues(); } } diff --git a/src/DataProvider/Pagination/PostPaginator.php b/src/DataProvider/Pagination/PostPaginator.php new file mode 100644 index 0000000..6d8adb5 --- /dev/null +++ b/src/DataProvider/Pagination/PostPaginator.php @@ -0,0 +1,79 @@ +currentPage = $currentPage; + $this->maxResults = $maxResults; + $this->queryBuilder = $queryBuilder; + $this->totalResult = $totalResult; + } + + public function getLastPage(): float + { + return ceil($this->getTotalItems() / $this->getItemsPerPage()) ?: 1.; + } + + public function getTotalItems(): float + { + return $this->totalResult; + } + + public function getCurrentPage(): float + { + return $this->currentPage; + } + + public function getItemsPerPage(): float + { + return $this->maxResults; + } + + public function count() + { + return iterator_count($this->getIterator()); + } + + public function getIterator() + { + if ($this->postsIterator === null) { + $offset = ($this->currentPage - 1) * $this->maxResults; + + $query = $this->queryBuilder + ->setFirstResult($offset) + ->setMaxResults($this->maxResults) + ->getQuery() + ; + $this->results = $query->getResult(); + + $this->postsIterator = new \ArrayIterator( + $this->results + ); + } + + return $this->postsIterator; + } + + public function getResults(): \Traversable + { + return $this->results; + } +} diff --git a/src/DataProvider/PostCollectionDataProvider.php b/src/DataProvider/PostCollectionDataProvider.php new file mode 100644 index 0000000..4bff9c0 --- /dev/null +++ b/src/DataProvider/PostCollectionDataProvider.php @@ -0,0 +1,34 @@ +postRepository = $postRepository; + $this->pagination = $pagination; + } + + public function supports(string $resourceClass, string $operationName = null, array $context = []): bool + { + return $resourceClass === Post::class; + } + + public function getCollection(string $resourceClass, string $operationName = null, array $context = []) + { + [$page] = $this->pagination->getPagination($resourceClass, $operationName, $context); + + return $this->postRepository->findLatest($page); + } +} diff --git a/src/Entity/Album.php b/src/Entity/Album.php new file mode 100644 index 0000000..f86a87b --- /dev/null +++ b/src/Entity/Album.php @@ -0,0 +1,81 @@ +id; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(string $title): self + { + $this->title = $title; + + return $this; + } + + public function getYear(): ?int + { + return $this->year; + } + + public function setYear(int $year): self + { + $this->year = $year; + + return $this; + } + + public function getArtist(): ?Artist + { + return $this->artist; + } + + public function setArtist(?Artist $artist): self + { + $this->artist = $artist; + + return $this; + } +} diff --git a/src/Entity/Artist.php b/src/Entity/Artist.php new file mode 100644 index 0000000..c2c56f0 --- /dev/null +++ b/src/Entity/Artist.php @@ -0,0 +1,99 @@ +albums = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getAuthor(): ?string + { + return $this->author; + } + + public function setAuthor(string $author): self + { + $this->author = $author; + + return $this; + } + + /** + * @return Collection|Album[] + */ + public function getAlbums(): Collection + { + return $this->albums; + } + + public function addAlbum(Album $album): self + { + if (!$this->albums->contains($album)) { + $this->albums[] = $album; + $album->setArtist($this); + } + + return $this; + } + + public function removeAlbum(Album $album): self + { + if ($this->albums->removeElement($album)) { + // set the owning side to null (unless already changed) + if ($album->getArtist() === $this) { + $album->setArtist(null); + } + } + + return $this; + } +} diff --git a/src/Entity/Post.php b/src/Entity/Post.php new file mode 100644 index 0000000..8f7fbe7 --- /dev/null +++ b/src/Entity/Post.php @@ -0,0 +1,135 @@ +publishedAt = new \DateTime(); + // $this->comments = new ArrayCollection(); + // $this->tags = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(?string $title): void + { + $this->title = $title; + } + + public function getSlug(): ?string + { + return $this->slug; + } + + public function setSlug(string $slug): void + { + $this->slug = $slug; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(?string $content): void + { + $this->content = $content; + } + + public function getPublishedAt(): \DateTime + { + return $this->publishedAt; + } + + public function setPublishedAt(\DateTime $publishedAt): void + { + $this->publishedAt = $publishedAt; + } + + public function getSummary(): ?string + { + return $this->summary; + } + + public function setSummary(?string $summary): void + { + $this->summary = $summary; + } +} diff --git a/src/Repository/AlbumRepository.php b/src/Repository/AlbumRepository.php new file mode 100644 index 0000000..7813641 --- /dev/null +++ b/src/Repository/AlbumRepository.php @@ -0,0 +1,50 @@ +createQueryBuilder('a') + ->andWhere('a.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('a.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?Album + { + return $this->createQueryBuilder('a') + ->andWhere('a.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Repository/ArtistRepository.php b/src/Repository/ArtistRepository.php new file mode 100644 index 0000000..37fea1d --- /dev/null +++ b/src/Repository/ArtistRepository.php @@ -0,0 +1,50 @@ +createQueryBuilder('a') + ->andWhere('a.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('a.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?Artist + { + return $this->createQueryBuilder('a') + ->andWhere('a.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Repository/PostRepository.php b/src/Repository/PostRepository.php new file mode 100644 index 0000000..85a8268 --- /dev/null +++ b/src/Repository/PostRepository.php @@ -0,0 +1,34 @@ +createQueryBuilder('p') + ->where('p.publishedAt <= :now') + ->orderBy('p.publishedAt', 'DESC') + ->setParameter('now', new \DateTime()) + ; + + $totalItems = $this->createQueryBuilder('p') + ->select('count(p.id)') + ->getQuery() + ->getSingleScalarResult() + ; + + return (new PostPaginator($qb, $page, $totalItems)); + } + +}