Skip to content

Instantly share code, notes, and snippets.

@simonhamp
Last active November 1, 2024 18:28
Show Gist options
  • Save simonhamp/549e8821946e2c40a617c85d2cf5af5e to your computer and use it in GitHub Desktop.
Save simonhamp/549e8821946e2c40a617c85d2cf5af5e to your computer and use it in GitHub Desktop.
A pageable Collection implementation for Laravel

Paginated Collections in Laravel

Use Case

Laravel provides pagination out of the box for Eloquent Collections, but you can't use that by default on ordinary Collections.

Collections do have the forPage() method, but it's more low-level, so it doesn't generate pagination links.

So you have to create a LengthAwarePaginator instance. But what if you want the behaviour to be the same as an Eloquent collection? Then use this macro!

The benefit of this is that the syntax and output is almost identical to the Eloquent Collection paginate() method and so it can (relatively) easily be swapped out for an Eloquent Collection when testing.

Installation

Feel free to copy the most relevant code into your project. You're free to use and adapt as you need.

Usage

There are 2 approaches below. Which one you use is up to you, but you don't need both. I personally prefer the macro method as I feel it's cleaner and works well with minimal effort, but it's not so good at working with your IDE (code hints etc) and can feel a little detached in some cases.

The macro way

If you prefer, add the Collection macro to a Service Provider. That way you can call paginate() on any collection:

collect([ ... ])->paginate( 20 );

See AppServiceProvider.php for a sample implementation.

The subclass way

Where you want a "pageable" collection that is distinct from the standard Illuminate\Support\Collection, implement a copy of Collection.php in your application and simply replace your use Illuminate\Support\Collection statements at the top of your dependent files with use App\Support\Collection:

- use Illuminate\Support\Collection
+ use App\Support\Collection;

$collection = (new Collection([ ... ]))->paginate(20);

Note that this approach won't work with the collect() helper function.

More Collection goodies

Spatie have create an awesome Composer package of loads of useful Collection macros. Go check it out!

<?php
namespace App\Providers;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
/**
* Paginate a standard Laravel Collection.
*
* @param int $perPage
* @param int $total
* @param int $page
* @param string $pageName
* @return array
*/
Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page'): LengthAwarePaginator {
$page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
return new LengthAwarePaginator(
$this->forPage($page, $perPage)->values(),
$total ?: $this->count(),
$perPage,
$page,
[
'path' => LengthAwarePaginator::resolveCurrentPath(),
'pageName' => $pageName,
]
);
});
}
}
<?php
namespace App\Support;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection as BaseCollection;
class Collection extends BaseCollection
{
public function paginate($perPage, $total = null, $page = null, $pageName = 'page'): LengthAwarePaginator
{
$page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
return new LengthAwarePaginator(
$this->forPage($page, $perPage)->values(),
$total ?: $this->count(),
$perPage,
$page,
[
'path' => LengthAwarePaginator::resolveCurrentPath(),
'pageName' => $pageName,
]
);
}
}
@jd-bowling
Copy link

Just what I've been looking for. This will allow me to search custom model attributes in my paginated table.

@devmount
Copy link

Wow, thank you @simonhamp! This really helped me out on collections containing different eloquent models 😍

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