Skip to content

Instantly share code, notes, and snippets.

@davzie
Created June 18, 2019 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davzie/9989780850e06e7de5bf81622d9abc4b to your computer and use it in GitHub Desktop.
Save davzie/9989780850e06e7de5bf81622d9abc4b to your computer and use it in GitHub Desktop.
Eloquent / Fractal Pagination Station
<?php
namespace App\Pagination;
use Illuminate\Database\Eloquent\Builder;
use League\Fractal\TransformerAbstract;
class EloquentPaginationStation extends PaginationStation
{
/**
* @param TransformerAbstract $transformerAbstract - The transformer for the collection, should already be
* instantiated
* @param Builder $builderWithoutLimits - The Eloquent Query Builder
* @param string $rootScope - The Root URL For The Current Endpoint (ex:
* route('api.v1.threads.list') )
* @param array $meta
*
* @return array
*/
public function returnCollection(
TransformerAbstract $transformerAbstract,
Builder $builderWithoutLimits,
$rootScope,
$meta = []
)
{
if ($this->paginationShouldBeSkipped()) {
$totalBuilder = clone $builderWithoutLimits;
$results = $builderWithoutLimits->get();
} else {
$totalBuilder = clone $builderWithoutLimits;
$results = $builderWithoutLimits->offset($this->getOffsetFromRequest())->take($this->getLimitFromRequest())->get();
}
$resultSetCount = $results->count();
return fractal()
->collection($results)
->addMeta(['pagination' => $this->getEloquentPaginationArray($totalBuilder, $resultSetCount, $rootScope)] + $meta)
->transformWith($transformerAbstract)
->toArray();
}
/**
* @param Builder $builderWithoutLimits - The Eloquent Query Builder
* @param int $resultSetCount - The amount of results in this set
* @param string $rootScope - The Root URL For The Current Endpoint (ex: route('api.v1.threads.list')
* )
*
* @return array
*/
public function getEloquentPaginationArray(Builder $builderWithoutLimits, $resultSetCount, $rootScope)
{
$totalCount = $builderWithoutLimits->count();
return parent::getPaginationArray($totalCount, $resultSetCount, $rootScope);
}
}
<?php
namespace App\Pagination;
use Illuminate\Http\Request;
class PaginationStation
{
protected $defaultLimit = 80;
protected $maxLimit = 300;
/**
* The request object.
*
* @var Request
*/
protected $request;
protected $excludableParams = [
'filters'
];
/**
* PaginationStation constructor.
*
* @param Request $request
*/
public function __construct(Request $request)
{
$this->request = $request;
}
protected function paginationShouldBeSkipped(): bool
{
$paramsThatTriggerPaginationDisable = ['gimmeAllYouGot', 'show_all'];
foreach ($paramsThatTriggerPaginationDisable as $param) {
if ($this->request->has($param)) {
return true;
}
}
return false;
}
/**
* @param int $totalCount - The amount of items in the total result-set
* @param int $pageCount - The amount of items in the current result-set
* @param string $rootScope - The Root URL For The Current Endpoint (ex: route('api.v1.contacts.index') )
*
* @return array
*/
public function getPaginationArray($totalCount, $pageCount, $rootScope)
{
if ($this->paginationShouldBeSkipped()) {
$limit = $totalCount;
$offset = 0;
$pageCount = $totalCount;
$this->defaultLimit = $totalCount;
$this->maxLimit = $totalCount;
$last_link = null;
$next_link = null;
$previous_link = null;
$totalPages = 1;
$thisPage = 1;
} else {
$limit = $this->getLimitFromRequest();
$offset = $this->getOffsetFromRequest();
$last_link = $this->buildUrl($rootScope, ['offset' => $this->getLast($totalCount)]);
$next_link = $this->buildUrl($rootScope, ['offset' => $this->getNext($totalCount)]);
$previous_link = $this->buildUrl($rootScope, ['offset' => $this->getNext($totalCount)]);
$totalPages = $this->getTotalPages($totalCount);
$thisPage = $this->getThisPage($totalCount);
}
$paginationBlock = [
'total' => $totalCount,
'this_page' => $pageCount,
'per_page' => $limit,
'offset' => $offset,
'page_numbers' => [
'total_pages' => $totalPages,
'this_page' => $thisPage,
],
'_links' => [
'first' => $this->buildUrl($rootScope, ['offset' => 0]),
'last' => $last_link,
'next' => $next_link,
'previous' => $previous_link,
],
];
return $paginationBlock;
}
protected function getLimitFromRequest()
{
return $this->getLimit($this->request->only('limit'));
}
protected function getLimit(array $data)
{
if (!isset($data['limit']) || (int)$data['limit'] > $this->maxLimit || (int)$data['limit'] < 1) {
return $this->defaultLimit;
}
return (int)$data['limit'];
}
protected function getOffsetFromRequest()
{
return $this->getOffset($this->request->only('offset'));
}
protected function getOffset(array $data)
{
if (!isset($data['offset']) || (int)$data['offset'] < 1) {
return 0;
}
return (int)$data['offset'];
}
protected function getTotalPages($totalCount)
{
if ($this->paginationShouldBeSkipped()) {
return 1;
}
$limit = $this->getLimit($this->request->only('limit'));
$pagesPossible = ($totalCount / $limit);
if (floor($pagesPossible) != $pagesPossible) {
$pagesPossible = ceil($pagesPossible);
}
return (int)($pagesPossible);
}
protected function getThisPage($totalCount)
{
$limit = $this->getLimit($this->request->only('limit'));
$offset = $this->getOffset($this->request->only('offset'));
if (!$offset) {
return 1;
}
$currentPageNumber = floor($offset / $limit) + 1;
$totalPages = $this->getTotalPages($totalCount);
return $currentPageNumber >= $totalPages ? $totalPages : $currentPageNumber;
}
protected function buildUrl($rootScope, $overriding = [])
{
$getParams = array_merge($this->request->except($this->excludableParams), $overriding);
$rootScope = $rootScope . '?';
foreach ($getParams as $key => $val) {
if ($key == 'offset' && is_null($val)) {
return;
}
if ($key == 'offset' && $val < 1) {
continue;
}
$rootScope = $rootScope . $key . '=' . $val . '&';
}
return trim(trim($rootScope, '&'), '?');
}
protected function getLast($totalCount)
{
$limit = $this->getLimit($this->request->only('limit'));
$lastOffsetMultiplier = ($totalCount / $limit);
if ($lastOffsetMultiplier < 1) {
return 0;
}
if (floor($lastOffsetMultiplier) != $lastOffsetMultiplier) {
$lastOffsetMultiplier = floor($lastOffsetMultiplier);
}
return (int)($limit * $lastOffsetMultiplier);
}
protected function getNext($totalCount)
{
$limit = $this->getLimit($this->request->only('limit'));
$offset = $this->getOffset($this->request->only('offset'));
$newOffset = $offset + $limit;
if ($newOffset > $totalCount) {
return;
}
if ($newOffset > $this->getLast($totalCount)) {
return $this->getLast($totalCount);
}
return $newOffset;
}
protected function getPrevious($totalCount)
{
$limit = $this->getLimit($this->request->only('limit'));
$offset = $this->getOffset($this->request->only('offset'));
$newOffset = $offset - $limit;
if ($newOffset < 0) {
return;
}
if ($newOffset > $this->getLast($totalCount)) {
return $this->getLast($totalCount);
}
return $newOffset;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment