From 53a6ca3d6b8402fcc0e5465903e05a84896c3f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 5 Sep 2016 23:16:42 +0200 Subject: [PATCH] adding doc about pagination --- source/development/index.rst | 1 + source/development/pagination.rst | 189 ++++++++++++++++++++ source/development/pagination/example.php | 42 +++++ source/static/puml/pagination-sequence.png | Bin 0 -> 15076 bytes source/static/puml/pagination-sequence.puml | 20 +++ 5 files changed, 252 insertions(+) create mode 100644 source/development/pagination.rst create mode 100644 source/development/pagination/example.php create mode 100644 source/static/puml/pagination-sequence.png create mode 100644 source/static/puml/pagination-sequence.puml diff --git a/source/development/index.rst b/source/development/index.rst index f28ed424a..043b32920 100644 --- a/source/development/index.rst +++ b/source/development/index.rst @@ -20,6 +20,7 @@ As Chill rely on the `symfony `_ framework, reading the fram Menus Access control model Messages to users + Pagination Localisation Logging Database migrations diff --git a/source/development/pagination.rst b/source/development/pagination.rst new file mode 100644 index 000000000..52d0d81f1 --- /dev/null +++ b/source/development/pagination.rst @@ -0,0 +1,189 @@ +.. Copyright (C) 2016 Champs Libres Cooperative SCRLFS + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + + +Pagination +########## + +The Bundle :code:`Chill\MainBundle` provides a **Pagination** api which allow you to easily divide results list on different pages. + +A simple example +**************** + +In the controller, get the :class:`Chill\Main\Pagination\PaginatorFactory` from the `Container` and use this :code:`PaginatorFactory` to create a :code:`Paginator` instance. + + +.. literalinclude:: pagination/example.php + :language: php + + +Then, render the pagination using the dedicated twig function. + +.. code-block:: html+jinja2 + + {% extends "ChillPersonBundle::layout.html.twig" %} + + {% block title 'Item list'|trans %} + + {% block personcontent %} + + + + {# ... your items here... #} + +
+ + {% if items|length > paginator.getTotalItems %} + {{ chill_pagination(paginator) }} + {% endif %} + + +The function :code:`chill_pagination` will, by default, render a link to the 10 previous page (if they exists) and the 10 next pages (if they exists). Assuming that we are on page 5, the function will render a list to :: + + Previous 1 2 3 4 **5** 6 7 8 9 10 11 12 13 14 Next + +Understanding the magic +======================= + +Where does the :code:`$paginator` get the page number ? +------------------------------------------------------- + +Internally, the :code:`$paginator` object has a link to the :code:`Request` object, and it reads the :code:`page` parameter which contains the current page number. If this parameter is not present, the :code:`$paginator` assumes that we are on page 1. + +.. figure:: /static/puml/pagination-sequence.png + + The :code:`$paginator` get the current page from the request. + +Where does the :code:`$paginator` get the number of items per page ? +-------------------------------------------------------------------- + +As above, the :code:`$paginator` can get the number of items per page from the :code:`Request`. If none is provided, this is given by the configuration which is, by default, 50 items per page. + +:code:`PaginatorFactory`, :code:`Paginator` and :code:`Page` +************************************************************ + +:code:`PaginatorFactory` +======================== + +The :code:`PaginatorFactory` may create more than one :code:`Paginator` in a single action. Those :code:`Paginator` instance may redirect to different routes and/or routes parameters. + +.. code-block:: php + + // create a paginator for the route 'my_route' with some parameters (arg1 and arg2) + $paginatorMyRoute = $paginatorFactory->create($total, 'my_route', array('arg1' => 'foo', 'arg2' => $bar); + +Those parameters will override the current parameters. + +The :code:`PaginatorFactory` has also some useful shortcuts : + +.. code-block:: php + + // get current page number + $paginatorFactory->getCurrentPageNumber( ) + // get the number of items per page **for the current request** + $paginatorFactory->getCurrentItemsPerPage( ) + // get the number of the first item **for the current page** + $paginatorFactory->getCurrentPageFirstItemNumber( ) + + +Working with :code:`Paginator` and :code:`Page` +=============================================== + +The paginator has some function to give the number of pages are required to displayed all the results, and give some information about the number of items per page : + +.. code-block:: php + + // how many page count this paginator ? + $paginator->countPages(); // return 20 in our example + + // we may get the number of items per page + $paginator->getItemsPerPage(); // return 20 in our example + +A :code:`Paginator` instance create instance of :code:`Page`, each :code:`Page`, which is responsible for generating the URL to the page number it represents. Here are some possibilities using :code:`Page` and :code:`Paginator` : + +.. code-block:: php + + // get the current page + $page = $paginator->getCurrentPage(); + // on which page are we ? + $page->getNumber(); // return 5 in this example (we are on page 5) + // generate the url for page 5 + $page->generateUrl(); // return '/?page=5 + // what is the first item number on this page ? + $page->getFistItemNumber(); // return 101 in our example (20 items per page) + // what is the last item number on this page ? + $page->getLastItemNumber(); // return 120 in our example + + // we can access directly the next and current page + if ($paginator->hasNextPage()) { + $next = $paginator->getNextPage(); + } + if ($paginator->hasPreviousPage()) { + $previous = $paginator->getPreviousPage(); + } + + // we can access directly to a given page number + if ($paginator->hasPage(10)) { + $page10 = $paginator->getPage(10); + } + + // we can iterate over our pages through a generator + foreach ($paginator->getPagesGenerator() as $page) { + $page->getNumber(); + } + + // check that a page object is the current page + $paginator->isCurrentPage($page); // return false + +.. warning:: + + When calling a page which does not exists, the :code:`Paginator` will throw a `RuntimeException`. Example : + + .. code-block:: php + + // our last page is 10 + $paginator->getPage(99); // out of range => throw `RuntimeException` + + // our current page is 1 (the first page) + $paginator->getPreviousPage; // does not exists (the fist page is always 1) => throw `RuntimeException` + +.. note:: + + When you create a :code:`Paginator` for the current route and route parameters, the :code:`Page` instances will keep the same parameters and routes : + + .. code-block:: php + + // assuming our route is 'my_route', for the pattern '/my/{foo}/route', + // and the current route is '/my/value/route?arg2=bar' + + // create a paginator for the current route and route parameters : + $paginator = $paginatorFactory->create($total); + + // get the next page + if ($paginator->hasNext()) { + $next = $paginator->getNextPage(); + + // get the route to the page + $page->generateUrl(); // will print 'my/value/route?arg2=bar&page=2' + } + + +Having a look to the `full classes documentation may provide some useful information `_. + + +Customizing the rendering of twig's :code:`chill_pagination` +************************************************************ + +You can provide your own layout for rendering the pagination: provides your twig template as a second argument : + +.. code-block:: html+jinja + + {{ chill_pagination(paginator, 'MyBundle:Pagination:MyTemplate.html.twig') }} + +The template will receive the :code:`$paginator` as :code:`paginator` variable. Let's have a look `at the current template `_. + diff --git a/source/development/pagination/example.php b/source/development/pagination/example.php new file mode 100644 index 000000000..bfcac41eb --- /dev/null +++ b/source/development/pagination/example.php @@ -0,0 +1,42 @@ +getDoctrine()->getManager(); + // first, get the number of total item are available + $total = $em + ->createQuery("COUNT (item.id) FROM ChillMyBundle:Item item") + ->getSingleScalarResult(); + + // get the PaginatorFactory + $paginatorFactory = $this->get('chill_main.paginator_factory'); + + // create a pagination instance. This instance is only valid for + // the current route and parameters + $paginator = $paginatorFactory->create($total); + + // launch your query on item. Limit the query to the results + // for the current page using the paginator + $items = $em->createQuery("SELECT item FROM ChillMyBundle:Item item WHERE ") + // use the paginator to get the first item number + ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) + // use the paginator to get the number of items to display + ->setMaxResults($paginator->getItemsPerPage()); + + return $this->render('ChillMyBundle:Item:list.html.twig', array( + 'items' => $items, + 'paginator' => $paginator + ); + + } + +} + diff --git a/source/static/puml/pagination-sequence.png b/source/static/puml/pagination-sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..7016be6e2d5208ead441892e04dfaf0c114b77a6 GIT binary patch literal 15076 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 z paginator: getCurrentPage() +activate paginator + +paginator -> Request: read the `page` parameter in GET request +activate Request +Request -> paginator +deactivate Request + +paginator -> paginator: construct a page object for current page number +paginator -> x: return the `page` +deactivate paginator + +@enduml +