Skip to content

Instantly share code, notes, and snippets.

@SimonSimCity
Last active September 30, 2023 17:29
Show Gist options
  • Save SimonSimCity/4594748 to your computer and use it in GitHub Desktop.
Save SimonSimCity/4594748 to your computer and use it in GitHub Desktop.
A gist for pagination in Twig, based on the total number of pages, the current page and some URL-settings.
{#
Source: http://dev.dbl-a.com/symfony-2-0/symfony2-and-twig-pagination/
Updated by: Simon Schick <simonsimcity@gmail.com>
Parameters:
* currentFilters (array) : associative array that contains the current route-arguments
* currentPage (int) : the current page you are in
* paginationPath (string) : the route name to use for links
* showAlwaysFirstAndLast (bool) : Always show first and last link (just disabled)
* lastPage (int) : represents the total number of existing pages
#}
{% spaceless %}
{% if lastPage > 1 %}
{# the number of first and last pages to be displayed #}
{% set extremePagesLimit = 3 %}
{# the number of pages that are displayed around the active page #}
{% set nearbyPagesLimit = 2 %}
<div class="pagination">
{% if currentPage > 1 %}
<a href="{{ path(paginationPath, currentFilters|merge({page: currentPage-1})) }}">Previous</a>
{% for i in range(1, extremePagesLimit) if ( i < currentPage - nearbyPagesLimit ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% if extremePagesLimit + 1 < currentPage - nearbyPagesLimit %}
<span class="sep-dots">...</span>
{% endif %}
{% for i in range(currentPage-nearbyPagesLimit, currentPage-1) if ( i > 0 ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% elseif showAlwaysFirstAndLast %}
<span class="disabled">Previous</span>
{% endif %}
<a href="{{ path(paginationPath, currentFilters|merge({ page: currentPage })) }}"
class="active">{{ currentPage }}</a>
{% if currentPage < lastPage %}
{% for i in range(currentPage+1, currentPage + nearbyPagesLimit) if ( i <= lastPage ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% if (lastPage - extremePagesLimit) > (currentPage + nearbyPagesLimit) %}
<span class="sep-dots">...</span>
{% endif %}
{% for i in range(lastPage - extremePagesLimit+1, lastPage) if ( i > currentPage + nearbyPagesLimit ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
<a href="{{ path(paginationPath, currentFilters|merge({page: currentPage+1})) }}">Next</a>
{% elseif showAlwaysFirstAndLast %}
<span class="disabled">Next</span>
{% endif %}
</div>
{% endif %}
{% endspaceless %}
@KaiCMueller
Copy link

Thank you for the great work! I adapted your approach to build a paginator for the Symfony EasyAdmin bundle: https://gist.github.com/KaiCMueller/6692ee84f51341acf582e2103f05f3d4

@Matt-PMCT
Copy link

As a warning to newer users Twig is deprecating the if appended to the end of the for statements

@ulab
Copy link

ulab commented Feb 2, 2021

Twig 2.10 deprecated the inline if statements in for loops. You now have to use filters instead or move the if inside of the loop:

https://twig.symfony.com/doc/2.x/filters/filter.html#filter

@AliAkinK
Copy link

I wish there could be an update for this without the depreciated for statements.

@ardentsword
Copy link

@ulab @AliAkinK I actually fixed this problem years ago but never thought about uploading it here, if anyone is still interested:
I'm actually still actively using it, and it works quite well :)

{#
  Source: http://dev.dbl-a.com/symfony-2-0/symfony2-and-twig-pagination/
  Updated by: Simon Schick <simonsimcity@gmail.com>
  Parameters:
    * currentFilters (array) : associative array that contains the current route-arguments
    * currentPage (int) : the current page you are in
    * paginationPath (string) : the route name to use for links
    * showAlwaysFirstAndLast (bool) : Always show first and last link (just disabled)
    * lastPage (int) : represents the total number of existing pages
#}
{% apply spaceless %}
    {% if lastPage > 1 %}

        {# the number of first and last pages to be displayed #}
        {% set extremePagesLimit = 3 %}

        {# the number of pages that are displayed around the active page #}
        {% set nearbyPagesLimit = 2 %}

        {% if currentFilters is not defined %}{% set currentFilters = app.request.attributes.get('_route_params')|merge(app.request.query.all) %}{% endif %}
        {% if paginationPath is not defined %}{% set paginationPath = app.request.attributes.get('_route') %}{% endif %}
        {% if showAlwaysFirstAndLast is not defined %}{% set showAlwaysFirstAndLast = true %}{% endif %}

        <nav aria-label="Page navigation">
        <ul class="pagination">
            {% if currentPage > 1 %}
                <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: currentPage-1})) }}">Previous</a></li>

                {% for i in range(1, extremePagesLimit) | filter(i => i < currentPage - nearbyPagesLimit ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                {% if extremePagesLimit + 1 < currentPage - nearbyPagesLimit %}
                    <span class="sep-dots">...</span>
                {% endif %}

                {% for i in range(currentPage-nearbyPagesLimit, currentPage-1) | filter(i => i > 0 ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}
            {% elseif showAlwaysFirstAndLast %}
                <li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
            {% endif %}

            <li class="page-item active"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({ page: currentPage })) }}">{{ currentPage }}</a></li>

            {% if currentPage < lastPage %}
                {% for i in range(currentPage+1, currentPage + nearbyPagesLimit) | filter(i => i <= lastPage) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                {% if  (lastPage - extremePagesLimit) > (currentPage + nearbyPagesLimit) %}
                    <span class="sep-dots">...</span>
                {% endif %}

                {% for i in range(lastPage - extremePagesLimit+1, lastPage) | filter( i => i > currentPage + nearbyPagesLimit ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: currentPage+1})) }}">Next</a></li>
            {% elseif showAlwaysFirstAndLast %}
                <li class="page-item disabled"><a class="page-link" href="#">Next</a></li>
            {% endif %}
        </ul>
        </nav>
    {% endif %}
{% endapply %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment