PHPackages                             inwebo/paginator-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. inwebo/paginator-bundle

ActiveSymfony-bundle[Database &amp; ORM](/categories/database)

inwebo/paginator-bundle
=======================

A lightweight Symfony bundle providing Doctrine-backed pagination with Twig helpers.

1.0.0(1mo ago)01[1 PRs](https://github.com/inwebo/paginator-bundle/pulls)GPL-3.0-or-laterPHPPHP &gt;=8.1CI passing

Since Jul 6Pushed 1mo agoCompare

[ Source](https://github.com/inwebo/paginator-bundle)[ Packagist](https://packagist.org/packages/inwebo/paginator-bundle)[ Docs](https://github.com/inwebo/paginator-bundle)[ RSS](/packages/inwebo-paginator-bundle/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (9)Versions (3)Used By (0)

inwebo/paginator-bundle
=======================

[](#inwebopaginator-bundle)

A lightweight Symfony 7/8 bundle providing Doctrine-backed pagination with Twig helpers.

Requirements
------------

[](#requirements)

- PHP ≥ 8.1
- Symfony 7 or 8
- Doctrine ORM ^3.0
- Twig ^3.21

Installation
------------

[](#installation)

```
composer require inwebo/paginator-bundle
```

The bundle registers itself automatically via Symfony Flex. No manual kernel registration needed.

Usage
-----

[](#usage)

There are two ways to add pagination to a repository: the **trait approach** (recommended, no forced inheritance) and the **abstract class approach** (full opinionated stack).

---

### Approach 1 — Trait (recommended)

[](#approach-1--trait-recommended)

Use `PaginationRepositoryTrait` on any existing repository. No change to the class hierarchy is required.

```
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Inwebo\PaginatorBundle\Doctrine\PaginationRepositoryTrait;

class PostRepository extends ServiceEntityRepository
{
    use PaginationRepositoryTrait;
}
```

Then in a controller:

```
// Simple page-based pagination
$paginator = $repository->paginate(
    $repository->createQueryBuilder('p')->where('p.published = true'),
    page: $page,
    pageSize: 20,
);

// Reverse-chronological (newest first, requires a count resolver)
$qb = $repository->createQueryBuilder('p');
$paginator = $repository->paginated(
    $qb,
    pageSize: 20,
    lastPageFirst: true,
    countResolver: static fn (): int =>
        (int) (clone $qb)
            ->select('COUNT(DISTINCT(p.id))')
            ->getQuery()
            ->getSingleScalarResult(),
);
$paginator->paginate(null); // null → resolves to the last page automatically
```

---

### Approach 2 — Abstract classes (opinionated stack)

[](#approach-2--abstract-classes-opinionated-stack)

Extend `AbstractQueryBuilder` and `AbstractServiceEntityRepository` for a more integrated experience. The `ALIAS` constant drives all generated SQL fragments.

#### Query builder

[](#query-builder)

```
use Inwebo\PaginatorBundle\Doctrine\AbstractQueryBuilder;

class PostQueryBuilder extends AbstractQueryBuilder
{
    public const string ALIAS = 'p';

    public function __construct(\Doctrine\ORM\EntityManager $em)
    {
        parent::__construct($em);
        $this->from(Post::class, self::ALIAS);
    }

    public function published(): static
    {
        return $this->andWhere(self::ALIAS . '.publishedAt IS NOT NULL');
    }
}
```

#### Repository

[](#repository)

```
use Inwebo\PaginatorBundle\Doctrine\AbstractServiceEntityRepository;

/**
 * @template-extends AbstractServiceEntityRepository
 */
class PostRepository extends AbstractServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Post::class);
    }

    public function createQueryBuilder($alias, $indexBy = null): PostQueryBuilder
    {
        return (new PostQueryBuilder($this->getEntityManager()))->findAll();
    }
}
```

#### Controller

[](#controller)

```
// Simple pagination
$paginator = $repository->createQueryBuilder('p')
    ->published()
    ->orderByCreatedAtDesc()
    ->paginate($page);

// Reverse-chronological
$paginator = $repository->createQueryBuilder('p')
    ->published()
    ->paginated(pageSize: 20, lastPageFirst: true)
    ->paginate(null);
```

`AbstractServiceEntityRepository` includes `PaginationRepositoryTrait`, so both `.paginate()` and `.paginated()` are also available directly on the repository (not only on the query builder).

---

### Rendering in Twig

[](#rendering-in-twig)

Include the built-in template, passing the `Paginated` object as `paginator`:

```
{% include '@InweboPaginator/paginated/pages.html.twig' with { paginator: paginator } %}
```

The template renders a `` with navigation buttons (first, previous, numbered pages, next, last). Navigation buttons are hidden when there is only one page.

### Iterating over results

[](#iterating-over-results)

`Paginated` is not itself iterable — after calling `paginate()`, iterate over `getResults()` to display the current page's items:

```
$paginator = $repository->paginate($qb, page: $page);

foreach ($paginator->getResults() as $post) {
    echo $post->getTitle();
}
```

The same works directly in Twig, alongside the pagination navigation template:

```

    {% for post in paginator.results %}
        {{ post.title }}
    {% endfor %}

{% include '@InweboPaginator/paginated/pages.html.twig' with { paginator: paginator } %}
```

Use `getReverseIterator()` instead of `getResults()` to walk the current page back to front (e.g. a chat or activity log where the newest item of the page should appear first):

```
{% for message in paginator.reverseIterator %}
    {{ message.content }}
{% endfor %}
```

#### Route naming convention

[](#route-naming-convention)

The item template builds pagination URLs from the current route. It appends `_paginated` to the route name if not already present. Declare your paginated route accordingly:

```
// config/routes/post.php
$routes->add('app_post_index_paginated', '/posts/{page}')
    ->controller(PostController::class)
    ->defaults(['page' => 1]);
```

---

### `Paginated` API

[](#paginated-api)

MethodDescription`paginate(int $page): self`Execute the query and set the current page`getCurrentPage(): int`Current page number`getLastPage(): int`Total number of pages`getCountPages(): int`Alias for `getLastPage()``hasPreviousPage(): bool`Whether a previous page exists`hasNextPage(): bool`Whether a next page exists`getPreviousPage(): int`Previous page number (min 1)`getNextPage(): int`Next page number (max last)`hasToPaginate(): bool`Whether results exceed one page`getNumResults(): int`Total result count`getResults(): \Traversable`Current page results`getReverseIterator(): \Traversable`Iterate current page in reverse order### Twig functions

[](#twig-functions)

FunctionDescription`pagination_bounds(paginator)`Returns a `PaginationBounds` DTO with `start`/`end` for the visible page window (max 11 pages)`pagination_page(page, content, title, isActive, forceDisplay)`Renders a single `` page link---

Development
-----------

[](#development)

```
# Run tests
composer phpunit

# Code style
composer php-cs-fixer

# Static analysis (PHPStan level 10)
composer phpstan
```

Credits
-------

[](#credits)

This bundle extracts and generalizes pagination code originally written by [inwebo](https://github.com/inwebo) for internal projects, packaged here for reuse across applications. Claude (Anthropic) assisted with the extraction, decoupling from the original application code, tests, documentation, and CI/CD setup.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance90

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

48d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/845359?v=4)[Inwebo Veritas](/maintainers/inwebo)[@inwebo](https://github.com/inwebo)

---

Top Contributors

[![inwebo](https://avatars.githubusercontent.com/u/845359?v=4)](https://github.com/inwebo "inwebo (3 commits)")

---

Tags

symfonybundletwigdoctrinepagination

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/inwebo-paginator-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/inwebo-paginator-bundle/health.svg)](https://phpackages.com/packages/inwebo-paginator-bundle)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k18.3M431](/packages/easycorp-easyadmin-bundle)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

12017.1k](/packages/rcsofttech-audit-trail-bundle)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k6.0M780](/packages/sylius-sylius)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1617.3k16](/packages/2lenet-crudit-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M236](/packages/sulu-sulu)[pimcore/pimcore

Content &amp; Product Management Framework (CMS/PIM/E-Commerce)

3.8k3.9M536](/packages/pimcore-pimcore)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
