定制 inwebo/paginator-bundle 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

inwebo/paginator-bundle

Composer 安装命令:

composer require inwebo/paginator-bundle

包简介

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

README 文档

README

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

Requirements

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

Installation

composer require inwebo/paginator-bundle

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

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)

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)

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

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

use Inwebo\PaginatorBundle\Doctrine\AbstractServiceEntityRepository;

/**
 * @template-extends AbstractServiceEntityRepository<Post>
 */
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

// 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

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

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

The template renders a <ul aria-label="pagination"> with navigation buttons (first, previous, numbered pages, next, last). Navigation buttons are hidden when there is only one page.

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:

<ul>
    {% for post in paginator.results %}
        <li>{{ post.title }}</li>
    {% endfor %}
</ul>

{% 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 %}
    <li>{{ message.content }}</li>
{% endfor %}

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

Method Description
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

Function Description
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 <li> page link

Development

# Run tests
composer phpunit

# Code style
composer php-cs-fixer

# Static analysis (PHPStan level 10)
composer phpstan

Credits

This bundle extracts and generalizes pagination code originally written by 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.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-3.0-or-later
  • 更新时间: 2026-07-06

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固