Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example of pagination with Twig
{#
Parameters:
* nbPages (int): number of pages
* currentPage (int): current pages
* url (string): route name & query (string): route parameter
ex: list/page-5?q=myFilter (5 = page and query = myFilter)
#}
{% spaceless %}
{% if nbPages > 1 %}
{# Number of page around current page+1 #}
{% set nearbyPagesLimit = 4 %}
<div>
<ul class="pagination">
{% if currentPage != 1 %}
<li>
<a href="{{ path(url, { 'page': 1, 'q': query }) }}">First</a>
</li>
{% endif %}
{% for i in 1..nbPages %}
{% if 0 == (currentPage - nearbyPagesLimit) - loop.index %} {# dot before #}
<li class="disabled"><a href="#">...</a></li>
{% elseif 0 == (currentPage + nearbyPagesLimit) - loop.index %} {# dot after #}
<li class="disabled"><a href="#">...</a></li>
{% elseif 0 < (currentPage - nearbyPagesLimit) - loop.index %} {# hide all before #}
{% elseif 0 > (currentPage + nearbyPagesLimit) - loop.index %} {# hide all after #}
{% else %}
<li {% if currentPage == loop.index %} class="active"{% endif %}>
<a href="{{ path(url, { 'page': loop.index, 'q': query }) }}">{{ loop.index }}</a>
</li>
{% endif %}
{% endfor %}
{% if currentPage != nbPages %}
<li>
<a href="{{ path(url, { 'page': nbPages, 'q': query }) }}">Last</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{% endspaceless %}
@radiocity
Copy link

radiocity commented May 21, 2019

{% macro pagination(total, current, url) %}
  {% spaceless %}
    {% if total > 1 %}
      {% set nearbyPagesLimit = 4 %}
      <div class="pagination">
        {% for i in 1..total %}
          {% if 0 == (current - nearbyPagesLimit) - loop.index %}
            <a href="{{ (url ~ 1)|e }}">1</a>
            <span>…</span>
          {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
            <span>…</span>
          {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
          {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
          {% else %}
            <a href="{{ url ~ loop.index }}" {% if current == loop.index  %} class="active"{% endif %}>{{ loop.index }}</a>
          {% endif %}
        {% endfor %}
        {% if current != total  and (current + nearbyPagesLimit) < total %}
          <a href="{{ (url ~ total)|e  }}">{{ total }}</a>
        {% endif %}
      </div>
    {% endif %}
  {% endspaceless %}
{% endmacro %}

@devnix
Copy link

devnix commented May 29, 2019

There is a little bug showing the first dots, where it does this strange behavour:

image

And this would be the fixed sample, note there is no ... separation between 1 and 2:

image

Also, this macro is adapted directly to render Bootstrap 3 lists.

{% macro pagination(total, current, url, nearbyPagesLimit = 4) %}
    {% spaceless %}
        {% if total > 1 %}
            <ul class="pagination">
                {% for i in 1..total %}
                    {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                        <li><a href="{{ (url ~ 1)|e }}">1</a></li>
                        {% if 1 != loop.index %}
                            <li><span>…</span></li>
                        {% endif %}
                    {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                        <li><span>…</span></li>
                    {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                    {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                    {% else %}
                        <li {{ current == loop.index ? 'class="active"' }}>
                            {% if current == loop.index  %}
                                <span>{{ loop.index }}</span>
                            {% else %}
                                <a href="{{ url ~ loop.index }}">{{ loop.index }}</a>
                            {% endif %}
                        </li>
                    {% endif %}
                {% endfor %}
                {% if current != total  and (current + nearbyPagesLimit) < total %}
                    <li><a href="{{ (url ~ total)|e }}">{{ total }}</a></li>
                {% endif %}
            </ul>
        {% endif %}
    {% endspaceless %}
{% endmacro %}

@Qrzysio
Copy link

Qrzysio commented Jan 19, 2020

Added prev and next buttons. Compatible with Twig 3.0+.

{% macro pagination(total, current, url, nearbyPagesLimit = 4) %}
    {% apply spaceless %}
        {% if total > 1 %}
            <div class="row">
                <div class="col">
                    <div class="pagination-area">
                        {% if current > 1 %}
                            <a class="prev page-numbers" href="{{ (url ~ (current-1))|e }}">Prev</a>
                        {% endif %}

                        {% for i in 1..total %}
                            {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                                <a class="page-numbers" href="{{ (url ~ 1)|e }}">1</a>
                                {% if 1 != loop.index %}
                                    <span class="page-numbers">...</span>
                                {% endif %}
                            {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                                <span class="page-numbers">...</span>
                            {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                            {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                            {% else %}
                                    {% if current == loop.index  %}
                                        <span class="page-numbers current" aria-current="page">{{ loop.index }}</span>
                                    {% else %}
                                        <a class="page-numbers" href="{{ url ~ loop.index }}">{{ loop.index }}</a>
                                    {% endif %}

                            {% endif %}
                        {% endfor %}
                        {% if current != total  and (current + nearbyPagesLimit) < total %}
                            <a class="page-numbers" href="{{ (url ~ total)|e }}">{{ total }}</a>
                        {% endif %}
                        {% if current < total %}
                            <a class="next page-numbers" href="{{ (url ~ (current+1))|e }}">Next</a>
                        {% endif %}
                    </div>
                </div>
            </div>
        {% endif %}
    {% endapply %}
{% endmacro %}

{{ _self.pagination(total, current, url) }}

@Qrzysio
Copy link

Qrzysio commented Jan 31, 2020

Updated.

Dropping page number for page no. 1.

BEFORE:
www.example.com/articles/page-1
www.example.com/articles/page-2
www.example.com/articles/page-3

NOW:
www.example.com/articles/
www.example.com/articles/page-2
www.example.com/articles/page-3

{% macro pagination(total, current, url, nearbyPagesLimit = 4) %}

    {# Create "main_url" variable with link for the first page #}
    {% set foo = url|split('/') %}
    {% set foo = foo|slice(0, -1) %}
    {% set main_url = foo|join('/') ~ "/" %}

    {% apply spaceless %}
        {% if total > 1 %}
            <div class="row">
                <div class="col-md-12 blog-posts">
                    <nav>
                    <ul class="pagination theme-colored2">
                        {% if current > 1 %}
                            <li><a class="prev page-numbers" href="{{ (url ~ (current-1))|e }}/">&laquo;</a></li>
                        {% endif %}

                        {% for i in 1..total %}
                            {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                                <li><a class="page-numbers" href="{{ (url ~ 1)|e }}/">1</a></li>
                                {% if 1 != loop.index %}
                                    <li><a href="#"><span class="page-numbers">...</span></a></li>
                                {% endif %}
                            {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                                <li><a href="#"><span class="page-numbers">...</span></a></li>
                            {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                                <!-- #1 -->
                            {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                                <!-- #2 -->
                            {% else %}
                                    {% if current == loop.index  %}
                                        <li class="active"><a href="#"><span class="page-numbers" aria-current="page">{{ loop.index }}</span></a></li>
                                    {% else %}
                                        {% if loop.index == 1 %}
                                            <li><a class="page-numbers" href="{{ main_url }}">{{ loop.index }}</a></li>
                                        {% else %}
                                            <li><a class="page-numbers" href="{{ url ~ loop.index }}/">{{ loop.index }}</a></li>
                                        {% endif %}
                                    {% endif %}
                            {% endif %}
                        {% endfor %}
                        {% if current != total  and (current + nearbyPagesLimit) < total %}
                            <li><a class="page-numbers" href="{{ (url ~ total)|e }}/">{{ total }}</a></li>
                        {% endif %}
                        {% if current < total %}
                            <li><a class="next page-numbers" href="{{ (url ~ (current+1))|e }}/">&raquo;</a></li>
                        {% endif %}
                    </ul>
                    </nav>
                </div>
            </div>
        {% endif %}
    {% endapply %}
{% endmacro %}

{{ _self.pagination(total, current, url) }}

@db306
Copy link

db306 commented Apr 20, 2020

@Qrzysio you're missing a </nav>

@Qrzysio
Copy link

Qrzysio commented Apr 20, 2020

Correct. Thanks a lot. I've updated the code.

@skylord123
Copy link

skylord123 commented Apr 5, 2021

{% macro pagination(total, current, pageQueryParam = 'page', nearbyPagesLimit = 4) %}
    {% if total > 1 %}
        <ul class="pagination my-4">
            {% for i in 1..total %}
                {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                    <li class="page-item"><a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): 1})|url_encode))|e }}" class="page-link">1</a></li>
                    {% if 1 != loop.index %}
                        <li class="page-item">
                            <a href="javascript:void(0)" class="page-link">...</a>
                        </li>
                    {% endif %}
                {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                    <li class="page-item">
                        <a href="javascript:void(0)" class="page-link">...</a>
                    </li>
                {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                {% else %}
                    <li class="page-item{{ current == loop.index ? ' active' }}">
                        <a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): loop.index})|url_encode))|e }}" class="page-link">{{ loop.index }}</a>
                    </li>
                {% endif %}
            {% endfor %}
            {% if current != total  and (current + nearbyPagesLimit) < total %}
                <li class="page-item"><a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): total})|url_encode))|e }}" class="page-link">{{ total }}</a></li>
            {% endif %}
        </ul>
    {% endif %}
{% endmacro %}

I didn't like how the page had to be passed in with the macro so I decided to make it use the app.request object to retain all query parameters and only modify the page query parameter (with the option to change what the page query param is called). This assumes that pagination will always be hitting the same route the user is already on (which is true in my case but if not for your case you can easily modify the macro).

I also changed it to display via bootstrap 4.

@cadot-info
Copy link

cadot-info commented Sep 23, 2021

thanks, for your share ;-)

@cadot-eu
Copy link

cadot-eu commented Apr 20, 2022

Pagination with a variable.

	<nav aria-label="Page navigation example">
		<ul
			class="pagination justify-content-center">
			{# precedent #}
			<li class="page-item {{ app.request.query.get('page')==1 or app.request.query.get('page')==0 ? 'disabled' }} ">
				{% if app.request.query.get('page') is defined and app.request.query.get('page')>1 %}
					{% set precedent = app.request.query.get('page') -1 %}
				{% else %}
					{% set precedent = 1 %}
				{% endif %}
				<a class="page-link" href="{{ path('¤entity¤_index',{'page':precedent,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">
					<span aria-hidden="true">&laquo;</span>
				</a>
			</li>
			{% if pagesMaxi/10>6 %}
				{# 1 #}
				<li class="page-item">
					<a class="page-link" href="{{ path('¤entity¤_index',{'page':1,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">1</a>
				</li>
				{# pages millieu #}
				{% for item in 1..5 %}
					<li class="page-item">
						<a class="page-link " href="{{ path('¤entity¤_index',{'page':(pagesMaxi/10/6*loop.index)|number_format(0),'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">{{(pagesMaxi/10/6*loop.index)|number_format(0)}}</a>
					</li>
				{% endfor %}
				{# maxi #}
				<li class="page-item">
					<a class="page-link" href={{ path('¤entity¤_index',{'page':(pagesMaxi/10)|number_format(0),'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')} ) }}>{{(pagesMaxi/10)|number_format(0)}}</a>
				</li>
			{% else %}
				{% for item in 1..(pagesMaxi/10)|number_format(0) %}
					<li class="page-item">
						<a class="page-link " href="{{ path('¤entity¤_index',{'page':loop.index,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">{{loop.index}}</a>
					</li>
				{% endfor %}
			{% endif %}
			{# suivant #}
			<li class="page-item {{ app.request.query.get('page') >= (pagesMaxi/10)|number_format ? 'disabled' }}">
				{% if app.request.query.get('page')==0 %}
					{% set suivant = 2 %}
				{% else %}
					{% set suivant = app.request.query.get('page') +1 %}
				{% endif %}
				<a class="page-link" href="{{ path('¤entity¤_index',{'page':suivant,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">
					<span aria-hidden="true">&raquo;</span>
				</a>
			</li>
		</ul>
	</nav>

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