diff --git a/composer.json b/composer.json index 41f0288..14c884d 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,10 @@ "gedmo/doctrine-extensions": "^2.4", "hubspot/hubspot-php": "^2.0", "knplabs/doctrine-behaviors": "^2.0.6", - "runroom-packages/form-handler-bundle": "^0.8.1", - "runroom-packages/render-event-bundle": "^0.8.1", - "runroom-packages/sortable-behavior-bundle": "^0.8.1", + "knplabs/knp-paginator-bundle": "^5.3", + "runroom-packages/form-handler-bundle": "^0.8", + "runroom-packages/render-event-bundle": "^0.8", + "runroom-packages/sortable-behavior-bundle": "^0.8", "sonata-project/admin-bundle": "^3.68", "sonata-project/doctrine-extensions": "^1.9", "sonata-project/doctrine-orm-admin-bundle": "^3.20", diff --git a/src/BasicEntities/Admin/BookAdmin.php b/src/BasicEntities/Admin/BookAdmin.php index 4e7b074..93a0426 100644 --- a/src/BasicEntities/Admin/BookAdmin.php +++ b/src/BasicEntities/Admin/BookAdmin.php @@ -52,6 +52,7 @@ protected function configureListFields(ListMapper $listMapper): void ->add('_action', 'actions', [ 'actions' => [ 'delete' => [], + 'edit' => [], 'move' => [ 'template' => '@SortableBehavior/sort.html.twig', ], diff --git a/src/BasicEntities/Controller/BookController.php b/src/BasicEntities/Controller/BookController.php index 299da14..d3acc0a 100644 --- a/src/BasicEntities/Controller/BookController.php +++ b/src/BasicEntities/Controller/BookController.php @@ -15,6 +15,7 @@ use Runroom\RenderEventBundle\Renderer\PageRenderer; use Runroom\SamplesBundle\BasicEntities\Service\BookService; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class BookController @@ -52,4 +53,15 @@ public function book(string $slug): Response $model ); } + + public function list(Request $request): Response + { + $page = (int) $request->get('page', 1); + $model = $this->service->getBooksListViewModel($page); + + return $this->renderer->renderResponse( + '@RunroomSamples/BasicEntities/book-list.html.twig', + $model + ); + } } diff --git a/src/BasicEntities/Repository/BookRepository.php b/src/BasicEntities/Repository/BookRepository.php index ba3c14c..4152fc0 100644 --- a/src/BasicEntities/Repository/BookRepository.php +++ b/src/BasicEntities/Repository/BookRepository.php @@ -15,6 +15,7 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\Query\Expr\Join; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Runroom\SamplesBundle\BasicEntities\Entity\Book; use Symfony\Component\HttpFoundation\Request; @@ -47,4 +48,14 @@ public function findBySlug(string $slug): Book return $query->getSingleResult(); } + + public function getBooksQueryBuilder(): QueryBuilder + { + $request = $this->requestStack->getCurrentRequest() ?? new Request(); + + return $this->createQueryBuilder('book') + ->where('book.publish = true') + ->leftJoin('book.translations', 'translations', Join::WITH, 'translations.locale = :locale') + ->setParameter('locale', $request->getLocale()); + } } diff --git a/src/BasicEntities/Service/BookService.php b/src/BasicEntities/Service/BookService.php index 3e4ecf5..438ddba 100644 --- a/src/BasicEntities/Service/BookService.php +++ b/src/BasicEntities/Service/BookService.php @@ -13,18 +13,30 @@ namespace Runroom\SamplesBundle\BasicEntities\Service; +use Knp\Bundle\PaginatorBundle\Pagination\SlidingPaginationInterface; +use Knp\Component\Pager\PaginatorInterface; use Runroom\SamplesBundle\BasicEntities\Repository\BookRepository; +use Runroom\SamplesBundle\BasicEntities\ViewModel\BooksListViewModel; use Runroom\SamplesBundle\BasicEntities\ViewModel\BooksViewModel; use Runroom\SamplesBundle\BasicEntities\ViewModel\BookViewModel; class BookService { + /** @var int */ + private const MAX_RESULT = 5; + /** @var BookRepository */ private $repository; - public function __construct(BookRepository $repository) - { + /** @var PaginatorInterface */ + private $paginator; + + public function __construct( + BookRepository $repository, + PaginatorInterface $paginator + ) { $this->repository = $repository; + $this->paginator = $paginator; } public function getBooksViewModel(): BooksViewModel @@ -46,4 +58,16 @@ public function getBookViewModel(string $slug): BookViewModel return $model; } + + public function getBooksListViewModel(int $page): BooksListViewModel + { + $queryBuilder = $this->repository->getBooksQueryBuilder(); + + $pagination = $this->paginator->paginate($queryBuilder, $page, self::MAX_RESULT); + \assert($pagination instanceof SlidingPaginationInterface); + $model = new BooksListViewModel(); + $model->setPagination($pagination); + + return $model; + } } diff --git a/src/BasicEntities/ViewModel/BooksListViewModel.php b/src/BasicEntities/ViewModel/BooksListViewModel.php new file mode 100644 index 0000000..d8f9814 --- /dev/null +++ b/src/BasicEntities/ViewModel/BooksListViewModel.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Runroom\SamplesBundle\BasicEntities\ViewModel; + +use Knp\Bundle\PaginatorBundle\Pagination\SlidingPaginationInterface; +use Runroom\SamplesBundle\BasicEntities\Entity\Book; + +class BooksListViewModel +{ + /** + * @phpstan-var SlidingPaginationInterface + * @psalm-var SlidingPaginationInterface + */ + protected $pagination; + + /** @var array */ + protected $paginationData; + + /** @phpstan-return SlidingPaginationInterface|null + * @psalm-return SlidingPaginationInterface|null + */ + public function getPagination(): ?SlidingPaginationInterface + { + return $this->pagination; + } + + /** + * @phpstan-param SlidingPaginationInterface $pagination + * @psalm-param SlidingPaginationInterface $pagination + * */ + public function setPagination(SlidingPaginationInterface $pagination): self + { + $this->pagination = $pagination; + $this->paginationData = $pagination->getPaginationData(); + + return $this; + } + + public function getPreviousPage(): ?int + { + return $this->getPaginationData('previous'); + } + + public function getNextPage(): ?int + { + return $this->getPaginationData('next'); + } + + public function getPaginationData(string $data): ?int + { + return $this->paginationData[$data] ?? null; + } +} diff --git a/src/Resources/config/routing.yaml b/src/Resources/config/routing.yaml index 4e4bb6d..2afd6f2 100644 --- a/src/Resources/config/routing.yaml +++ b/src/Resources/config/routing.yaml @@ -13,6 +13,10 @@ runroom_samples.basic_entities.book: path: /book/{slug} controller: Runroom\SamplesBundle\BasicEntities\Controller\BookController::book +runroom_samples.basic_entities.books_list: + path: /pagination + controller: Runroom\SamplesBundle\BasicEntities\Controller\BookController::list + # Forms runroom_samples.forms.contact: path: /contact diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index fb78c1e..11152a0 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -24,6 +24,10 @@ services: arguments: [null, Runroom\SamplesBundle\BasicEntities\Entity\Category, null] tags: - { name: sonata.admin, manager_type: orm, label: 'Categories' } + + Runroom\SamplesBundle\BasicEntities\Service\BookService: + public: true + arguments: ['@Runroom\SamplesBundle\BasicEntities\Repository\BookRepository', '@knp_paginator'] # Forms Runroom\SamplesBundle\Forms\Controller\: diff --git a/src/Resources/views/BasicEntities/book-list.html.twig b/src/Resources/views/BasicEntities/book-list.html.twig new file mode 100644 index 0000000..fff211a --- /dev/null +++ b/src/Resources/views/BasicEntities/book-list.html.twig @@ -0,0 +1,20 @@ +{% extends '@RunroomSamples/base.html.twig' %} + +{% block content %} +
+
+
    + {% for book in model.pagination.items %} +
    +
  • + {{book.title}} +
  • +
    + {% endfor %} +
+
+ +
+{% endblock content %} diff --git a/tests/App/Kernel.php b/tests/App/Kernel.php index d9b82fd..331ab1f 100644 --- a/tests/App/Kernel.php +++ b/tests/App/Kernel.php @@ -18,6 +18,7 @@ use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use FOS\CKEditorBundle\FOSCKEditorBundle; use Knp\Bundle\MenuBundle\KnpMenuBundle; +use Knp\Bundle\PaginatorBundle\KnpPaginatorBundle; use Knp\DoctrineBehaviors\DoctrineBehaviorsBundle; use Runroom\FormHandlerBundle\RunroomFormHandlerBundle; use Runroom\RenderEventBundle\RunroomRenderEventBundle; @@ -51,6 +52,7 @@ public function registerBundles(): iterable new FOSCKEditorBundle(), new FrameworkBundle(), new KnpMenuBundle(), + new KnpPaginatorBundle(), new SecurityBundle(), new TwigBundle(), new SonataMediaBundle(), diff --git a/tests/BasicEntities/Unit/BookServiceTest.php b/tests/BasicEntities/Unit/BookServiceTest.php index 679e62e..c63c21c 100644 --- a/tests/BasicEntities/Unit/BookServiceTest.php +++ b/tests/BasicEntities/Unit/BookServiceTest.php @@ -13,6 +13,7 @@ namespace Runroom\SamplesBundle\Tests\BasicEntities\Unit; +use Knp\Component\Pager\PaginatorInterface; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; @@ -30,11 +31,18 @@ class BookServiceTest extends TestCase /** @var BookService */ private $service; + /** @var PaginatorInterface */ + private $paginator; + protected function setUp(): void { $this->repository = $this->prophesize(BookRepository::class); + $this->paginator = $this->createMock(PaginatorInterface::class); - $this->service = new BookService($this->repository->reveal()); + $this->service = new BookService( + $this->repository->reveal(), + $this->paginator + ); } /** @test */