Skip to content

Instantly share code, notes, and snippets.

@adrianb93
Last active November 27, 2018 11:55
Show Gist options
  • Save adrianb93/fc728488ea1e70af7a2dcde647ef2a4c to your computer and use it in GitHub Desktop.
Save adrianb93/fc728488ea1e70af7a2dcde647ef2a4c to your computer and use it in GitHub Desktop.
Pagination Query Pipeline
<?php
if (! function_exists('pipe')) {
function pipe($passable)
{
return app(\App\Pipeline::class)->send($passable);
}
}
if (! function_exists('paginate')) {
function paginate($query, array $filterPipeline = [])
{
return \App\Paginator::make($query, $filterPipeline)->paginate();
}
}
<?php
namespace App;
class Pipeline extends \Illuminate\Pipeline\Pipeline
{
public function get()
{
return $this->then(function ($passable) {
return $passable;
});
}
}
<?php
namespace App;
class Paginator
{
protected $query;
protected $filterPipeline;
public function __construct($query, array $filterPipeline = [])
{
$this->query = $query;
$this->filterPipeline = $filterPipeline;
}
public static function make($query, array $filterPipeline = [])
{
return new static($query, $filterPipeline);
}
public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
$paginator = pipe($this->query)->through($this->filters())->get()->paginate(
$this->perPage($perPage),
$columns,
$pageName,
$page
);
collect( request()->query() )
->reject(function ($value, $key) use ($pageName) {
return in_array($key, [$pageName, 'per_page']);
})
->each(function ($value, $key) use ($paginator) {
$paginator->appends($key, $value);
});
return $paginator;
}
private function filters()
{
return collect($this->filterPipeline)->filter(function ($value, $key) {
return is_integer($key) ?: request()->has($key);
})->filter()->all();
}
/**
* Get the number of models to return per page.
*
* Extended to check the request first.
*
* @return int
*/
protected function perPage($perPage = null)
{
if (app()->runningInConsole()) {
return $this->closestPerPageAllowed(
$perPage ?: $this->query->getModel()->getPerPage()
);
}
$sessionKey = 'pagination.per_page.'.request()->path();
$perPage = $perPage ?? request('per_page', session($sessionKey, $perPage));
return $this->closestPerPageAllowed(
session($sessionKey, tap($perPage, function ($perPage) use ($sessionKey) {
session()->put($sessionKey, $perPage);
}))
);
}
/**
* Get the closest permitted per page value allowed. This prevents users
* requesting unreasonable page sizes.
*
* @param integer $perPage
* @return integer
*/
protected function closestPerPageAllowed($perPage)
{
$closest = null;
$perPage = abs($perPage);
foreach ($this->getAllowedPerPageValues() as $value) {
$value = abs($value);
if ($closest === null || abs($perPage - $closest) > abs($value - $perPage)) {
$closest = $value;
}
}
return (int) $closest ?: 1;
}
/**
* The valid values allowed for per page. Keep public for views.
*
* @return array
*/
public function getAllowedPerPageValues()
{
$default = [10, 25, 50, 100];
$model = $this->query->getModel();
if (method_exists($model, 'getAllowedPerPageValues')) {
return $model->getAllowedPerPageValues() ?: $default;
}
return $model->allowedPerPageValues ?? $default;
}
}
<?php
namespace App\Http\Controllers;
use App\Models\Company;
use App\Filters\SortFilter;
use App\Filters\SearchFilter;
use App\Http\Controllers\Controller;
class ExampleController extends Controller
{
public function index()
{
return view('companies.index', [
'companies' => paginate(Company::query(), [
SortFilter::class,
SearchFilter::class,
function ($query, $next) {
$query->where('enabled', true);
return $next($query);
},
'if_this_key_is_present_in_the_request' => function ($query, $next) {
$query->doThis();
return $next($query);
},
]),
]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment