Rapid pagination without using OFFSET
- PHP:
^8.0
- Laravel:
^9.0 || ^10.0 || ^11.0
- lampager/lampager:
^0.4
composer require lampager/lampager-laravel
Register service provider.
config/app.php
:
/*
* Package Service Providers...
*/
Lampager\Laravel\MacroServiceProvider::class,
Then you can chain ->lampager()
method from Query Builder, Eloquent Builder and Relation.
$cursor = [
'id' => 3,
'created_at' => '2017-01-10 00:00:00',
'updated_at' => '2017-01-20 00:00:00',
];
$result = App\Post::whereUserId(1)
->lampager()
->forward()
->limit(5)
->orderByDesc('updated_at') // ORDER BY `updated_at` DESC, `created_at` DESC, `id` DESC
->orderByDesc('created_at')
->orderByDesc('id')
->seekable()
->paginate($cursor)
->toJson(JSON_PRETTY_PRINT);
It will run the optimized query.
(
SELECT * FROM `posts`
WHERE `user_id` = 1
AND (
`updated_at` = '2017-01-20 00:00:00' AND `created_at` = '2017-01-10 00:00:00' AND `id` > 3
OR
`updated_at` = '2017-01-20 00:00:00' AND `created_at` > '2017-01-10 00:00:00'
OR
`updated_at` > '2017-01-20 00:00:00'
)
ORDER BY `updated_at` ASC, `created_at` ASC, `id` ASC
LIMIT 1
) UNION ALL (
SELECT * FROM `posts`
WHERE `user_id` = 1
AND (
`updated_at` = '2017-01-20 00:00:00' AND `created_at` = '2017-01-10 00:00:00' AND `id` <= 3
OR
`updated_at` = '2017-01-20 00:00:00' AND `created_at` < '2017-01-10 00:00:00'
OR
`updated_at` < '2017-01-20 00:00:00'
)
ORDER BY `updated_at` DESC, `created_at` DESC, `id` DESC
LIMIT 6
)
And you'll get
{
"records": [
{
"id": 3,
"user_id": 1,
"text": "foo",
"created_at": "2017-01-10 00:00:00",
"updated_at": "2017-01-20 00:00:00"
},
{
"id": 5,
"user_id": 1,
"text": "bar",
"created_at": "2017-01-05 00:00:00",
"updated_at": "2017-01-20 00:00:00"
},
{
"id": 4,
"user_id": 1,
"text": "baz",
"created_at": "2017-01-05 00:00:00",
"updated_at": "2017-01-20 00:00:00"
},
{
"id": 2,
"user_id": 1,
"text": "qux",
"created_at": "2017-01-17 00:00:00",
"updated_at": "2017-01-18 00:00:00"
},
{
"id": 1,
"user_id": 1,
"text": "quux",
"created_at": "2017-01-16 00:00:00",
"updated_at": "2017-01-18 00:00:00"
}
],
"has_previous": false,
"previous_cursor": null,
"has_next": true,
"next_cursor": {
"updated_at": "2017-01-18 00:00:00",
"created_at": "2017-01-14 00:00:00",
"id": 6
}
}
Lampager supports Laravel's API Resources.
Use helper traits on Resource and ResourceCollection.
use Illuminate\Http\Resources\Json\JsonResource;
use Lampager\Laravel\LampagerResourceTrait;
class PostResource extends JsonResource
{
use LampagerResourceTrait;
}
use Illuminate\Http\Resources\Json\ResourceCollection;
use Lampager\Laravel\LampagerResourceCollectionTrait;
class PostResourceCollection extends ResourceCollection
{
use LampagerResourceCollectionTrait;
}
$posts = App\Post::lampager()
->orderByDesc('id')
->paginate();
return new PostResourceCollection($posts);
{
"data": [/* ... */],
"has_previous": false,
"previous_cursor": null,
"has_next": true,
"next_cursor": {/* ... */}
}
Note: See also lampager/lampager.
Name | Type | Parent Class | Description |
---|---|---|---|
Lampager\Laravel\Paginator |
Class | Lampager\Paginator |
Fluent factory implementation for Laravel |
Lampager\Laravel\Processor |
Class | Lampager\AbstractProcessor |
Processor implementation for Laravel |
Lampager\Laravel\PaginationResult |
Class | Lampager\PaginationResult |
PaginationResult implementation for Laravel |
Lampager\Laravel\MacroServiceProvider |
Class | Illuminate\Support\ServiceProvider |
Enable macros chainable from QueryBuilder, ElqouentBuilder and Relation |
Lampager\Laravel\LampagerResourceTrait |
Trait | Support for Laravel JsonResource | |
Lampager\Laravel\LampagerResourceCollectionTrait |
Trait | Support for Laravel ResourceCollection |
Paginator
, Processor
and PaginationResult
are macroable.
Note: See also lampager/lampager.
Create a new paginator instance.
If you use Laravel macros, however, you don't need to directly instantiate.
static Paginator create(QueryBuilder|EloquentBuilder|Relation $builder): static
Paginator::__construct(QueryBuilder|EloquentBuilder|Relation $builder)
QueryBuilder
means\Illuminate\Database\Query\Builder
EloquentBuilder
means\Illuminate\Database\Eloquent\Builder
Relation
means\Illuminate\Database\Eloquent\Relation
Transform Lampager Query into Illuminate builder.
Paginator::transform(Query $query): QueryBuilder|EloquentBuilder|Relation
Perform configure + transform.
Paginator::build(\Lampager\Contracts\Cursor|array $cursor = []): QueryBuilder|EloquentBuilder|Relation
Perform configure + transform + process.
Paginator::paginate(\Lampager\Contracts\Cursor|array $cursor = []): \Lampager\Laravel\PaginationResult
(mixed)
$cursor
An associative array that contains$column => $value
or an object that implements\Lampager\Contracts\Cursor
. It must be all-or-nothing.- For initial page, omit this parameter or pass empty array.
- For subsequent pages, pass all parameters. Partial parameters are not allowd.
e.g.
(Default format when using \Illuminate\Database\Eloquent\Builder
)
object(Lampager\Laravel\PaginationResult)#1 (5) {
["records"]=>
object(Illuminate\Database\Eloquent\Collection)#2 (1) {
["items":protected]=>
array(5) {
[0]=>
object(App\Post)#2 (26) { ... }
[1]=>
object(App\Post)#3 (26) { ... }
[2]=>
object(App\Post)#4 (26) { ... }
[3]=>
object(App\Post)#5 (26) { ... }
[4]=>
object(App\Post)#6 (26) { ... }
}
}
["hasPrevious"]=>
bool(false)
["previousCursor"]=>
NULL
["hasNext"]=>
bool(true)
["nextCursor"]=>
array(2) {
["updated_at"]=>
string(19) "2017-01-18 00:00:00"
["created_at"]=>
string(19) "2017-01-14 00:00:00"
["id"]=>
int(6)
}
}
Invoke Processor methods.
Paginator::useFormatter(Formatter|callable $formatter): $this
Paginator::restoreFormatter(): $this
Paginator::process(\Lampager\Query $query, \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection $rows): \Lampager\Laravel\PaginationResult
Convert the object into array.
IMPORTANT: camelCase
properties are converted into snake_case
form.
PaginationResult::toArray(): array
PaginationResult::jsonSerialize(): array
Call macro or Collection methods.
PaginationResult::__call(string $name, array $args): mixed
e.g.
PaginationResult::macro('foo', function () {
return ...;
});
$foo = $result->foo();
$first = $result->first();