Skip to content

Instantly share code, notes, and snippets.

@Naskalin
Last active June 21, 2024 07:19
Show Gist options
  • Save Naskalin/6306172b8081813ea213099a4d16019a to your computer and use it in GitHub Desktop.
Save Naskalin/6306172b8081813ea213099a4d16019a to your computer and use it in GitHub Desktop.
Simple Doctrine Paginator and example usage for Symfony. Простейшая постраничная навигация в Symfony.
<?php
namespace App\Controller;
use App\Utils\Paginator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class PostController extends AbstractController
{
public function index(Request $request, Paginator $paginator, EntityManagerInterface $em): Response
{
// Query or Query Builder
// NOT RESULTS!
$query = $em->getRepository(Post::class)->findActivePostsQuery();
$paginator->paginate($query, $request->query->getInt('page', 1));
return $this->render('index.html.twig', [
'paginator' => $paginator,
]);
}
}
{% for post in paginator.items %}
{{ post.title }}
{% endfor %}
{% include 'paginator.html.twig' %}
<?php
namespace App\Utils;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as OrmPaginator;
class Paginator
{
/**
* @var integer
*/
private $total;
/**
* @var integer
*/
private $lastPage;
private $items;
/**
* @param QueryBuilder|Query $query
* @param int $page
* @param int $limit
* @return Paginator
*/
public function paginate($query, int $page = 1, int $limit = 10): Paginator
{
$paginator = new OrmPaginator($query);
$paginator
->getQuery()
->setFirstResult($limit * ($page - 1))
->setMaxResults($limit);
$this->total = $paginator->count();
$this->lastPage = (int) ceil($paginator->count() / $paginator->getQuery()->getMaxResults());
$this->items = $paginator;
return $this;
}
public function getTotal(): int
{
return $this->total;
}
public function getLastPage(): int
{
return $this->lastPage;
}
public function getItems()
{
return $this->items;
}
}
{% set _currentPage = app.request.query.get('page') ?: 1 %}
{% set _currentRoute = app.request.attributes.get('_route') %}
{% set _lastPage = paginator.lastPage %}
{% set _currentParams = app.request.query.all|merge(app.request.attributes.get('_route_params')) %}
{% if paginator.lastPage > 1 %}
<nav>
<ul class="pagination justify-content-center">
<li class="page-item{{ _currentPage <= 1 ? ' disabled' : '' }}">
<a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: _currentPage - 1})) }}" aria-label="Previous">
&laquo; Назад
</a>
</li>
{% for i in 1..paginator.lastPage %}
<li class="page-item {% if i == _currentPage %}active{% endif %}">
<a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: i})) }}">{{ i }}</a>
</li>
{% endfor %}
<li class="page-item {{ _currentPage >= paginator.lastPage ? ' disabled' : '' }}">
<a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: _currentPage + 1})) }}" aria-label="Next">
Далее &raquo;
</a>
</li>
</ul>
</nav>
{% endif %}
@Kocher04
Copy link

I've got an 'Call to a member function setFirstResult() on array'. Does anybody know where this comes from?

@cgo123
Copy link

cgo123 commented Oct 29, 2023

You must have the QUERY returned in the repository, not the result!

$paginator = new OrmPaginator($query);

@rkasza92
Copy link

In the paginator template, you list all the pages as separate links. In case of 100< pages it will be a huge UI problem. Here's an updated version to display only 5 links in the pager:

paginator.html.twig

{% set _currentPage = app.request.query.get('page') ?: 1 %}
{% set _currentRoute = app.request.attributes.get('_route') %}
{% set _lastPage = paginator.lastPage %}
{% set _currentParams = app.request.query.all|merge(app.request.attributes.get('_route_params')) %}

{% if paginator.lastPage > 1 %}
    <nav>
        <ul class="pagination justify-content-center">
            <li class="page-item{{ _currentPage <= 1 ? ' disabled' : '' }}">
                <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: _currentPage - 1})) }}" aria-label="Previous">
                    &laquo; {{ "Previous"|trans }}
                </a>
            </li>
            {% set startPage = max(1, _currentPage - 2) %}
            {% set endPage = min(_lastPage, startPage + 4) %}
            {% if startPage > 1 %}
                <li class="page-item">
                    <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: 1})) }}">1</a>
                </li>
                {% if startPage > 2 %}
                    <li class="page-item disabled">
                        <span class="page-link">...</span>
                    </li>
                {% endif %}
            {% endif %}
            {% for i in startPage..endPage %}
                <li class="page-item {% if i == _currentPage %}active{% endif %}">
                    <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: i})) }}">{{ i }}</a>
                </li>
            {% endfor %}
            {% if endPage < _lastPage %}
                {% if endPage < _lastPage - 1 %}
                    <li class="page-item disabled">
                        <span class="page-link">...</span>
                    </li>
                {% endif %}
                <li class="page-item">
                    <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: _lastPage})) }}">{{ _lastPage }}</a>
                </li>
            {% endif %}
            <li class="page-item {{ _currentPage >= paginator.lastPage ? ' disabled' : '' }}">
                <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: _currentPage + 1})) }}" aria-label="Next">
                    {{ "Next"|trans }} &raquo;
                </a>
            </li>
        </ul>
    </nav>
{% endif %}

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