Using a Bootstrap 4 Pagination Control Layout with KnpPaginatorBundle in Symfony 3

If you are willing to improve the performance of a page that displays more than 1000 results, yes or yes, you will have to implement a paginator. Not only for the sake that displaying 1K rows in your browser may decrease the performance of your page clearly according to the JavaScript or Styles that you use, besides not any user will want to scroll with those 1K rows.

For Symfony, the KnpPaginatorBundle is the most known bundle that helps you to paginate the results of your query with a very simple API:

  • Does not require initializing specific adapters
  • Can be customized in any way needed, etc.: pagination view, event subscribers.
  • Possibility to add custom filtering, sorting functionality depending on request parameters.
  • Separation of concerns, paginator is responsible for generating the pagination view only, pagination view - for representation purposes.

You can configure default query parameter names and templates in the config.yml and use custom pagination templates. One of the most known templates to show is the Bootstrap 3 version, however with the introduction of Bootstrap 4, till the date there's no Bootstrap v4 Sliding Pagination Control template in the official bundle. That's why we want to share with you our own implementation for Bootstrap 4 made in Twig:

{#
/**
 * @file
 * Twitter Bootstrap v4 Sliding pagination control implementation.
 *
 * View that can be used with the pagination module
 * from the Twitter Bootstrap v4 CSS Toolkit
 * https://v4-alpha.getbootstrap.com/components/pagination/
 *
 * @author Carlos Delgado <dev@ourcodeworld.com>
 */
#}

{% if pageCount > 1 %}
    <ul class="pagination">
        {% if previous is defined %}
            <li class="page-item">
                <a rel="prev" href="{{ path(route, query|merge({(pageParameterName): previous})) }}" class="page-link">&laquo;&nbsp; Previous</a>
            </li>
        {% else %}
            <li class="page-item disabled">
                <span class="page-link">&laquo;&nbsp; Previous</span>
            </li>
        {% endif %}

        {% if startPage > 1 %}
            <li class="page-item">
                <a href="{{ path(route, query|merge({(pageParameterName): 1})) }}" class="page-link">1</a>
            </li>
            {% if startPage == 3 %}
                <li class="page-item">
                    <a href="{{ path(route, query|merge({(pageParameterName): 2})) }}" class="page-link">2</a>
                </li>
            {% elseif startPage != 2 %}
                <li class="page-item disabled">
                    <span class="page-link">&hellip;</span>
                </li>
            {% endif %}
        {% endif %}

        {% for page in pagesInRange %}
            {% if page != current %}
                <li class="page-item">
                    <a href="{{ path(route, query|merge({(pageParameterName): page})) }}" class="page-link">{{ page }}</a>
                </li>
            {% else %}
                <li class="active page-item">
                    <span class="page-link">{{ page }}</span>
                </li>
            {% endif %}

        {% endfor %}

        {% if pageCount > endPage %}
            {% if pageCount > (endPage + 1) %}
                {% if pageCount > (endPage + 2) %}
                    <li class="page-item disabled">
                        <span class="page-link">&hellip;</span>
                    </li>
                {% else %}
                    <li class="page-item">
                        <a href="{{ path(route, query|merge({(pageParameterName): (pageCount - 1)})) }}" class="page-link">{{ pageCount -1 }}</a>
                    </li>
                {% endif %}
            {% endif %}
            <li>
                <a href="{{ path(route, query|merge({(pageParameterName): pageCount})) }}" class="page-link">{{ pageCount }}</a>
            </li>
        {% endif %}

        {% if next is defined %}
            <li class="page-item">
                <a rel="next" class="page-link" href="{{ path(route, query|merge({(pageParameterName): next})) }}">Next&nbsp;&raquo;</a>
            </li>
        {% else %}
            <li class="page-item disabled">
                <span class="page-link">Next&nbsp;&raquo;</span>
            </li>
        {% endif %}
    </ul>
{% endif %}

In our case, we will store the file in the app/resources/views/others/twitter_bootstrap_v4_pagination.html directory, so we can configure the paginator bundle to use this file in the config.yml file with the following yaml:

knp_paginator:
    template:
        pagination: "others/twitter_bootstrap_v4_pagination.html.twig"

Now that your projects implements Bootstrap 4, the paginator will render correctly as it's using the classes of the latest "stable" version of Bootstrap 4.

Happy coding !

Become a more social person