PHPackages                             wwwision/relay-pagination - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. wwwision/relay-pagination

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

wwwision/relay-pagination
=========================

Simple pagination implementing the Cursor Connections Specification, see https://relay.dev/graphql/connections.htm

1.2.1(3y ago)35.6k↓100%1MITPHPPHP &gt;= 8.1

Since Jun 7Pushed 3y ago1 watchersCompare

[ Source](https://github.com/bwaidelich/relay-pagination)[ Packagist](https://packagist.org/packages/wwwision/relay-pagination)[ GitHub Sponsors](https://github.com/sponsors/bwaidelich)[ Fund](https://www.paypal.me/bwaidelich)[ RSS](/packages/wwwision-relay-pagination/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (6)Dependencies (2)Versions (6)Used By (1)

Relay Cursor Pagination
=======================

[](#relay-cursor-pagination)

Simple pagination implementing the Cursor Connections Specification, see

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

[](#installation)

Install via [composer](https://getcomposer.org/):

```
composer require wwwision/relay-pagination

```

Usage
-----

[](#usage)

```
$loader = # ... instance of \Wwwision\RelayPagination\Loader\Loader
$resultsPerPage = 5; // edges to load per page

$paginator = new Paginator($loader);
$firstPage = $paginator->first($resultsPerPage);

foreach ($firstPage as $edge) {
    // $edge->cursor; contains the cursor string
    // $edge->node; contains the payload
}

// $firstPage->pageInfo; contains an object with pagination information
```

### Next page(s)

[](#next-pages)

Every `connection` contains information about next and previous pages. To navigate to a succeeding page, you can use the `endCursor` of the previous connection as input for the `after` argument:

```
if ($firstPage->pageInfo->hasNextPage) {
  $secondPage = $paginator->first($resultsPerPage, $firstPage->pageInfo->endCursor);
  // ...
}
```

### Backwards navigation

[](#backwards-navigation)

To navigate to a *preceeding* page, the `startCursor` can be passed to the `last()` method likewise:

```
if ($secondPage->pageInfo->hasPreviousPage) {
  $firstPage = $paginator->last($resultsPerPage, $secondPage->pageInfo->startCursor);
  // ...
}
```

### Reversed order

[](#reversed-order)

The ordering of edges is the same whether forward or backward navigation is used (as defined in the [specification](https://relay.dev/graphql/connections.htm#sec-Edge-order)). To navigate through results in *reversed* order, the `reversed()` method of the `Paginator` can be used:

```
$loader = new ArrayLoader(range('a', 'e'));
$paginator = (new Paginator($loader))->reversed();

$page1 = $paginator->first(3);

Assert::same(['e', 'd', 'c'], array_map(fn($edge) => $edge->node, $page1->toArray()));
Assert::false($page1->pageInfo->hasPreviousPage);
Assert::true($page1->pageInfo->hasNextPage);

$page2 = $paginator->first(3, $page1->pageInfo->endCursor);
Assert::same(['b', 'a'], array_map(fn($edge) => $edge->node, $page2->toArray()));
Assert::true($page2->pageInfo->hasPreviousPage);
Assert::false($page2->pageInfo->hasNextPage);
```

Loaders
-------

[](#loaders)

This package comes with four adapters (aka "loaders"):

### ArrayLoader

[](#arrayloader)

The `ArrayLoader` allows to paginate arbitrary arrays. **Note:** This loader is mainly meant for testing and debugging purposes and it should not be used for large datasets because the whole array has to be loaded into memory, obviously.

#### Usage

[](#usage-1)

```
$arrayLoader = new ArrayLoader($arbitraryArray);
```

**Note:** The specified array can be associative, but the keys will be lost during pagination since the ArrayLoader only works with the array values in order to guarantee deterministic ordering.

### CallbackLoader

[](#callbackloader)

The `CallbackLoader` invokes closures to determine pagination results. **Note:** This loader is mainly meant for rapid development, testing &amp; debugging purposes. Usually you'd want to create a custom implementation of the Loader interface instead

#### Usage

[](#usage-2)

```
$callbackLoader = new CallbackLoader(
    fn(int $limit, string $startCursor = null) => Edges::fromRawArray(...),
    fn(int $limit, string $endCursor = null) => Edges::fromRawArray(...)
);
```

### DbalLoader

[](#dballoader)

The `DbalLoader` allows to paginate arbitrary DBAL results. **Note:** This loader requires the `doctrine/dbal` package to be installed:

```
composer require doctrine/dbal

```

#### Usage

[](#usage-3)

```
$queryBuilder = (new QueryBuilder($dbalConnection))
  ->select('*')
  ->from('some_table');
$dbalLoader = new DbalLoader($queryBuilder, 'id');
```

### OrmLoader

[](#ormloader)

The `OrmLoader` allows to paginate arbitrary Doctrine ORM results. **Note:** This loader requires the `doctrine/orm` package to be installed:

```
composer require doctrine/orm

```

#### Usage

[](#usage-4)

```
$queryBuilder = $entityManager->createQueryBuilder()
    ->select('e')
    ->from(SomeEntity::class, 'e');
$ormLoader = new OrmLoader($queryBuilder, 'id');
```

Convert nodes
-------------

[](#convert-nodes)

A `node` is the only untyped property in this package since the loaders define the structure &amp; type of nodes. For the `DbalLoader` a node is an associative array containing the raw result from the corresponding database row for example.

In order to make it easier to re-use loaders a `Node Converter` can be specified that is applied to all results before it is returned:

```
$paginator = (new Paginator($someLoader))
  ->withNodeConverter(fn(string $node) => json_decode($node));
```

The above example expects the nodes to contain a valid JSON string. The same mechanism can be used in order to convert database results to a dedicated domain model instance:

```
$paginator = (new Paginator($dbalLoader))
  ->withNodeConverter(fn(array $row) => MyModel::fromDatabaseRow($row));
```

Examples
--------

[](#examples)

See [examples](examples) and [tests](tests) folders

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity24

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity64

Established project with proven stability

 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

Every ~154 days

Total

5

Last Release

1184d ago

Major Versions

0.1.0 → 1.0.02021-06-07

PHP version history (2 changes)0.1.0PHP &gt;= 7.4

1.2.1PHP &gt;= 8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/307571?v=4)[Bastian Waidelich](/maintainers/bwaidelich)[@bwaidelich](https://github.com/bwaidelich)

---

Top Contributors

[![bwaidelich](https://avatars.githubusercontent.com/u/307571?v=4)](https://github.com/bwaidelich "bwaidelich (20 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/wwwision-relay-pagination/health.svg)

```
[![Health](https://phpackages.com/badges/wwwision-relay-pagination/health.svg)](https://phpackages.com/packages/wwwision-relay-pagination)
```

###  Alternatives

[phpdocumentor/reflection-docblock

With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.

9.4k722.2M1.2k](/packages/phpdocumentor-reflection-docblock)[icanhazstring/composer-unused

Show unused packages by scanning your code

1.7k7.0M188](/packages/icanhazstring-composer-unused)[symplify/monorepo-builder

Not only Composer tools to build a Monorepo.

5205.3M82](/packages/symplify-monorepo-builder)[phpdocumentor/reflection

Reflection library to do Static Analysis for PHP Projects

12521.4M109](/packages/phpdocumentor-reflection)[sylius/fixtures-bundle

Configurable fixtures for Symfony applications.

517.7M12](/packages/sylius-fixtures-bundle)[sylius/promotion

Flexible promotion management for PHP applications.

28477.8k9](/packages/sylius-promotion)

PHPackages © 2026

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