Skip to content

Latest commit

 

History

History
590 lines (415 loc) · 20.2 KB

File metadata and controls

590 lines (415 loc) · 20.2 KB

Paginator

.. php:namespace:: Cake\View\Helper

.. php:class:: PaginatorHelper(View $view, array $config = [])

The PaginatorHelper is used to output pagination controls such as page numbers and next/previous links.

See also :doc:`/controllers/pagination` for information on how to create paginated datasets and do paginated queries.

Setting the paginated resultset

.. php:method:: setPaginated($paginated, $options)

By default the helper uses the first instance of Cake\Datasource\Paging\PaginatedInterface it finds in the view variables. (Generally the result of Controller::paginate()).

You can use PaginatorHelper::setPaginated() to explicitly set the paginated resultset that the helper should use.

PaginatorHelper Templates

Internally PaginatorHelper uses a series of simple HTML templates to generate markup. You can modify these templates to customize the HTML generated by the PaginatorHelper.

Templates use {{var}} style placeholders. It is important to not add any spaces around the {{}} or the replacements will not work.

Loading Templates from a File

When adding the PaginatorHelper in your controller, you can define the 'templates' setting to define a template file to load. This allows you to customize multiple templates and keep your code DRY:

// In your AppView.php
public function initialize(): void
{
    ...
    $this->loadHelper('Paginator', ['templates' => 'paginator-templates']);
}

This will load the file located at config/paginator-templates.php. See the example below for how the file should look like. You can also load templates from a plugin using :term:`plugin syntax`:

// In your AppView.php
public function initialize(): void
{
    ...
    $this->loadHelper('Paginator', ['templates' => 'MyPlugin.paginator-templates']);
}

Whether your templates are in the primary application or a plugin, your templates file should look something like:

return [
    'number' => '<a href="{{url}}">{{text}}</a>',
];

Changing Templates at Run-time

.. php:method:: setTemplates($templates)

This method allows you to change the templates used by PaginatorHelper at runtime. This can be useful when you want to customize templates for a particular method call:

// Read the current template value.
$result = $this->Paginator->getTemplates('number');

// Change a template
$this->Paginator->setTemplates([
    'number' => '<em><a href="{{url}}">{{text}}</a></em>'
]);

Warning

Template strings containing a percentage sign (%) need special attention, you should prefix this character with another percentage so it looks like %%. The reason is that internally templates are compiled to be used with sprintf(). Example: '<div style="width:{{size}}%%">{{content}}</div>'

Template Names

PaginatorHelper uses the following templates:

  • nextActive The active state for a link generated by next().
  • nextDisabled The disabled state for next().
  • prevActive The active state for a link generated by prev().
  • prevDisabled The disabled state for prev()
  • counterRange The template counter() uses when format == range.
  • counterPages The template counter() uses when format == pages.
  • first The template used for a link generated by first().
  • last The template used for a link generated by last()
  • number The template used for a link generated by numbers().
  • current The template used for the current page.
  • ellipsis The template used for ellipses generated by numbers().
  • sort The template for a sort link with no direction.
  • sortAsc The template for a sort link with an ascending direction.
  • sortDesc The template for a sort link with a descending direction.

Creating Sort Links

.. php:method:: sort($key, $title = null, $options = [])

    :param string $key: The name of the column that the recordset should be sorted.
    :param string $title: Title for the link. If $title is null, $key will be
        used converted to "Title Case" format and used as the title.
    :param array $options: Options for sorting link.

Generates a sorting link. Sets querystring parameters for the sort and direction. Links will default to sorting by asc. After the first click, links generated with sort() will handle direction switching automatically. If the resultset is sorted 'asc' by the specified key the returned link will sort by 'desc'. Uses the sort, sortAsc, sortDesc, sortAscLocked and sortDescLocked templates.

Accepted keys for $options:

  • escape Whether you want the contents HTML entity encoded, defaults to true.
  • direction The default direction to use when this link isn't active.
  • lock Lock direction. Will only use the default direction then, defaults to false.

Assuming you are paginating some posts, and are on page one:

echo $this->Paginator->sort('user_id');

Output:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc">User Id</a>

You can use the title parameter to create custom text for your link:

echo $this->Paginator->sort('user_id', 'User account');

Output:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc">User account</a>

If you are using HTML like images in your links remember to set escaping off:

echo $this->Paginator->sort(
  'user_id',
  '<em>User account</em>',
  ['escape' => false]
);

Output:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc"><em>User account</em></a>

The direction option can be used to set the default direction for a link. Once a link is active, it will automatically switch directions like normal:

echo $this->Paginator->sort('user_id', null, ['direction' => 'desc']);

Output:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=desc">User Id</a>

The lock option can be used to lock sorting into the specified direction:

echo $this->Paginator->sort('user_id', null, ['direction' => 'asc', 'lock' => true]);
.. php:method:: sortDir(string $model = null, mixed $options = [])

    Gets the current direction the recordset is sorted.

.. php:method:: sortKey(string $model = null, mixed $options = [])

    Gets the current key by which the recordset is sorted.

Creating Page Number Links

.. php:method:: numbers($options = [])

Returns a set of numbers for the paged result set. Uses a modulus to decide how many numbers to show on each side of the current page By default 8 links on either side of the current page will be created if those pages exist. Links will not be generated for pages that do not exist. The current page is also not a link. The number, current and ellipsis templates will be used.

Supported options are:

  • before Content to be inserted before the numbers.

  • after Content to be inserted after the numbers.

  • modulus how many numbers to include on either side of the current page, defaults to 8.

  • first Whether you want first links generated, set to an integer to define the number of 'first' links to generate. Defaults to false. If a string is set a link to the first page will be generated with the value as the title:

    echo $this->Paginator->numbers(['first' => 'First page']);
    
  • last Whether you want last links generated, set to an integer to define the number of 'last' links to generate. Defaults to false. Follows the same logic as the first option. There is a :php:meth:`~PaginatorHelper::last()` method to be used separately as well if you wish.

While this method allows a lot of customization for its output. It is also ok to just call the method without any parameters.

echo $this->Paginator->numbers();

Using the first and last options you can create links to the beginning and end of the page set. The following would create a set of page links that include links to the first 2 and last 2 pages in the paged results:

echo $this->Paginator->numbers(['first' => 2, 'last' => 2]);

Creating Jump Links

In addition to generating links that go directly to specific page numbers, you'll often want links that go to the previous and next links, first and last pages in the paged data set.

.. php:method:: prev($title = '<< Previous', $options = [])

    :param string $title: Title for the link.
    :param mixed $options: Options for pagination link.

    Generates a link to the previous page in a set of paged records. Uses
    the ``prevActive`` and ``prevDisabled`` templates.

    ``$options`` supports the following keys:

    * ``escape`` Whether you want the contents HTML entity encoded,
      defaults to ``true``.
    * ``disabledTitle`` The text to use when the link is disabled. Defaults to
      the ``$title`` parameter.

    A simple example would be::

        echo $this->Paginator->prev(' << ' . __('previous'));

    If you were currently on the second page of posts, you would get the following:

    .. code-block:: html

        <li class="prev">
            <a rel="prev" href="/posts/index?page=1&amp;sort=title&amp;order=desc">
                &lt;&lt; previous
            </a>
        </li>

    If there were no previous pages you would get:

    .. code-block:: html

        <li class="prev disabled"><a href="" onclick="return false;">&lt;&lt; previous</a></li>

    To change the templates used by this method see :ref:`paginator-templates`.

.. php:method:: next($title = 'Next >>', $options = [])

    This method is identical to :php:meth:`~PaginatorHelper::prev()` with a few exceptions. It
    creates links pointing to the next page instead of the previous one. It also
    uses ``next`` as the rel attribute value instead of ``prev``. Uses the
    ``nextActive`` and ``nextDisabled`` templates.

.. php:method:: first($first = '<< first', $options = [])

    Returns a first or set of numbers for the first pages. If a string is given,
    then only a link to the first page with the provided text will be created::

        echo $this->Paginator->first('< first');

    The above creates a single link for the first page. Will output nothing if you
    are on the first page. You can also use an integer to indicate how many first
    paging links you want generated::

        echo $this->Paginator->first(3);

    The above will create links for the first 3 pages, once you get to the third or
    greater page. Prior to that nothing will be output. Uses the ``first``
    template.

    The options parameter accepts the following:

    - ``escape`` Whether or not the text should be escaped. Set to ``false`` if your
      content contains HTML.

.. php:method:: last($last = 'last >>', $options = [])

    This method works very much like the :php:meth:`~PaginatorHelper::first()`
    method. It has a few differences though. It will not generate any links if you
    are on the last page for a string values of ``$last``. For an integer value of
    ``$last`` no links will be generated once the user is inside the range of last
    pages. Uses the ``last`` template.

Creating Header Link Tags

PaginatorHelper can be used to create pagination link tags in your page <head> elements:

// Create next/prev links for the current model.
echo $this->Paginator->meta();

// Create next/prev & first/last links for the current model.
echo $this->Paginator->meta(['first' => true, 'last' => true]);

Checking the Pagination State

.. php:method:: current()

    Gets the current page of the recordset::

        // Our URL is: /comments?page=3
        echo $this->Paginator->current();
        // Output is 3

    Uses the ``current`` template.

.. php:method:: hasNext(string $model = null)

    Returns ``true`` if the given result set is not at the last page.

.. php:method:: hasPrev()

    Returns ``true`` if the given result set is not at the first page.

.. php:method:: hasPage(int $page = 1)

    Returns ``true`` if the given result set has the page number given by ``$page``.

.. php:method:: total()

    Returns the total number of pages for the provided model.

Creating a Page Counter

.. php:method:: counter(string $format = 'pages', array $options = [])

Returns a counter string for the paged result set. Using a provided format string and a number of options you can create localized and application specific indicators of where a user is in the paged data set. Uses the counterRange, and counterPages templates.

Supported formats are 'range', 'pages' and custom. Defaults to pages which would output like '1 of 10'. In the custom mode the supplied string is parsed and tokens are replaced with actual values. The available tokens are:

  • {{page}} - the current page displayed.
  • {{pages}} - total number of pages.
  • {{current}} - current number of records being shown.
  • {{count}} - the total number of records in the result set.
  • {{start}} - number of the first record being displayed.
  • {{end}} - number of the last record being displayed.
  • {{model}} - The pluralized human form of the model name. If your model was 'RecipePage', {{model}} would be 'recipe pages'.

You could also supply only a string to the counter method using the tokens available. For example:

echo $this->Paginator->counter(
    'Page {{page}} of {{pages}}, showing {{current}} records out of
     {{count}} total, starting on record {{start}}, ending on {{end}}'
);

Setting 'format' to range would output like '1 - 3 of 13':

echo $this->Paginator->counter('range');

Generating Pagination URLs

.. php:method:: generateUrl(array $options = [], ?string $model = null, array $url = [], array $urlOptions = [])

By default returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript).

// Generates a URL similar to: /articles?sort=title&page=2
echo $this->Paginator->generateUrl(['sort' => 'title']);

// Generates a URL for a different model
echo $this->Paginator->generateUrl(['sort' => 'title'], 'Comments');

// Generates a URL to a different controller.
echo $this->Paginator->generateUrl(
    ['sort' => 'title'],
    null,
    ['controller' => 'Comments']
);

Creating a Limit Selectbox Control

.. php:method:: limitControl(array $limits = [], $default = null, array $options = [])

Create a dropdown control that changes the limit query parameter:

// Use the defaults.
echo $this->Paginator->limitControl();

// Define which limit options you want.
echo $this->Paginator->limitControl([25 => 25, 50 => 50]);

// Custom limits and set the selected option
echo $this->Paginator->limitControl([25 => 25, 50 => 50], $user->perPage);

The generated form and control will automatically submit on change.

By default limitControl() preserves existing query parameters as hidden fields. You can control this behavior with the preserveQuery option:

  • true Keep all query parameters (default).
  • false Do not include any preserved query parameters.
  • array Only keep the listed top-level keys.

Configuring Pagination Options

.. php:method:: options($options = [])

Sets all the options for the PaginatorHelper. Supported options are:

  • url The URL of the paginating action.

    The option allows your to set/override any element for URLs generated by the helper:

    $this->Paginator->options([
        'url' => [
            'lang' => 'en',
            '?' => [
                'sort' => 'email',
                'direction' => 'desc',
                'page' => 6,
            ],
        ]
    ]);
    

    The example above adds the en route parameter to all links the helper will generate. It will also create links with specific sort, direction and page values. By default PaginatorHelper will merge in all of the current passed arguments and query string parameters.

  • escape Defines if the title field for links should be HTML escaped. Defaults to true.

Example Usage

It's up to you to decide how to show records to the user, but most often this will be done inside HTML tables. The examples below assume a tabular layout, but the PaginatorHelper available in views doesn't always need to be restricted as such.

See the details on PaginatorHelper in the API. As mentioned, the PaginatorHelper also offers sorting features which can be integrated into your table column headers:

<!-- templates/Posts/index.php -->
<table>
    <tr>
        <th><?= $this->Paginator->sort('id', 'ID') ?></th>
        <th><?= $this->Paginator->sort('title', 'Title') ?></th>
    </tr>
       <?php foreach ($recipes as $recipe): ?>
    <tr>
        <td><?= $recipe->id ?> </td>
        <td><?= h($recipe->title) ?> </td>
    </tr>
    <?php endforeach; ?>
</table>

The links output from the sort() method of the PaginatorHelper allow users to click on table headers to toggle the sorting of the data by a given field.

It is also possible to sort a column based on associations:

<table>
    <tr>
        <th><?= $this->Paginator->sort('title', 'Title') ?></th>
        <th><?= $this->Paginator->sort('Authors.name', 'Author') ?></th>
    </tr>
       <?php foreach ($recipes as $recipe): ?>
    <tr>
        <td><?= h($recipe->title) ?> </td>
        <td><?= h($recipe->name) ?> </td>
    </tr>
    <?php endforeach; ?>
</table>

Note

Sorting by columns in associated models requires setting these in the PaginationComponent::paginate property. Using the example above, the controller handling the pagination would need to set its sortableFields key as follows:

$this->paginate = [
    'sortableFields' => [
        'Posts.title',
        'Authors.name',
    ],
];

For more information on using the sortableFields option, please see :ref:`control-which-fields-used-for-ordering`.

The final ingredient to pagination display in views is the addition of page navigation, also supplied by the PaginationHelper:

// Shows the page numbers
<?= $this->Paginator->numbers() ?>

// Shows the next and previous links
<?= $this->Paginator->prev('« Previous') ?>
<?= $this->Paginator->next('Next »') ?>

// Prints X of Y, where X is current page and Y is number of pages
<?= $this->Paginator->counter() ?>

The wording output by the counter() method can also be customized using special markers:

<?= $this->Paginator->counter(
    'Page {{page}} of {{pages}}, showing {{current}} records out of
    {{count}} total, starting on record {{start}}, ending on {{end}}'
) ?>

Paginating Multiple Results

If you are :ref:`paginating multiple queries <paginating-multiple-queries>` you'll need to use PaginatorHelper::setPaginated() first before calling other methods of the helper, so that they generate expected output.

PaginatorHelper will automatically use the scope defined in when the query was paginated. To set additional URL parameters for multiple pagination you can include the scope names in options():

$this->Paginator->options([
    'url' => [
        // Additional URL parameters for the 'articles' scope
        'articles' => [
            '?' => ['articles' => 'yes']
        ],
        // Additional URL parameters for the 'comments' scope
        'comments' => [
            'articleId' => 1234,
        ],
    ],
]);