AbstractCursorPaginator.php000064400000035676147536115330012114 0ustar00cursorName => $cursor->encode()]; if (count($this->query) > 0) { $parameters = array_merge($this->query, $parameters); } return $this->path() .(str_contains($this->path(), '?') ? '&' : '?') .Arr::query($parameters) .$this->buildFragment(); } /** * Get the URL for the previous page. * * @return string|null */ public function previousPageUrl() { if (is_null($previousCursor = $this->previousCursor())) { return null; } return $this->url($previousCursor); } /** * The URL for the next page, or null. * * @return string|null */ public function nextPageUrl() { if (is_null($nextCursor = $this->nextCursor())) { return null; } return $this->url($nextCursor); } /** * Get the "cursor" that points to the previous set of items. * * @return \Illuminate\Pagination\Cursor|null */ public function previousCursor() { if (is_null($this->cursor) || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore)) { return null; } if ($this->items->isEmpty()) { return null; } return $this->getCursorForItem($this->items->first(), false); } /** * Get the "cursor" that points to the next set of items. * * @return \Illuminate\Pagination\Cursor|null */ public function nextCursor() { if ((is_null($this->cursor) && ! $this->hasMore) || (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && ! $this->hasMore)) { return null; } if ($this->items->isEmpty()) { return null; } return $this->getCursorForItem($this->items->last(), true); } /** * Get a cursor instance for the given item. * * @param \ArrayAccess|\stdClass $item * @param bool $isNext * @return \Illuminate\Pagination\Cursor */ public function getCursorForItem($item, $isNext = true) { return new Cursor($this->getParametersForItem($item), $isNext); } /** * Get the cursor parameters for a given object. * * @param \ArrayAccess|\stdClass $item * @return array * * @throws \Exception */ public function getParametersForItem($item) { return collect($this->parameters) ->filter() ->flip() ->map(function ($_, $parameterName) use ($item) { if ($item instanceof JsonResource) { $item = $item->resource; } if ($item instanceof Model && ! is_null($parameter = $this->getPivotParameterForItem($item, $parameterName))) { return $parameter; } elseif ($item instanceof ArrayAccess || is_array($item)) { return $this->ensureParameterIsPrimitive( $item[$parameterName] ?? $item[Str::afterLast($parameterName, '.')] ); } elseif (is_object($item)) { return $this->ensureParameterIsPrimitive( $item->{$parameterName} ?? $item->{Str::afterLast($parameterName, '.')} ); } throw new Exception('Only arrays and objects are supported when cursor paginating items.'); })->toArray(); } /** * Get the cursor parameter value from a pivot model if applicable. * * @param \ArrayAccess|\stdClass $item * @param string $parameterName * @return string|null */ protected function getPivotParameterForItem($item, $parameterName) { $table = Str::beforeLast($parameterName, '.'); foreach ($item->getRelations() as $relation) { if ($relation instanceof Pivot && $relation->getTable() === $table) { return $this->ensureParameterIsPrimitive( $relation->getAttribute(Str::afterLast($parameterName, '.')) ); } } } /** * Ensure the parameter is a primitive type. * * This can resolve issues that arise the developer uses a value object for an attribute. * * @param mixed $parameter * @return mixed */ protected function ensureParameterIsPrimitive($parameter) { return is_object($parameter) && method_exists($parameter, '__toString') ? (string) $parameter : $parameter; } /** * Get / set the URL fragment to be appended to URLs. * * @param string|null $fragment * @return $this|string|null */ public function fragment($fragment = null) { if (is_null($fragment)) { return $this->fragment; } $this->fragment = $fragment; return $this; } /** * Add a set of query string values to the paginator. * * @param array|string|null $key * @param string|null $value * @return $this */ public function appends($key, $value = null) { if (is_null($key)) { return $this; } if (is_array($key)) { return $this->appendArray($key); } return $this->addQuery($key, $value); } /** * Add an array of query string values. * * @param array $keys * @return $this */ protected function appendArray(array $keys) { foreach ($keys as $key => $value) { $this->addQuery($key, $value); } return $this; } /** * Add all current query string values to the paginator. * * @return $this */ public function withQueryString() { if (! is_null($query = Paginator::resolveQueryString())) { return $this->appends($query); } return $this; } /** * Add a query string value to the paginator. * * @param string $key * @param string $value * @return $this */ protected function addQuery($key, $value) { if ($key !== $this->cursorName) { $this->query[$key] = $value; } return $this; } /** * Build the full fragment portion of a URL. * * @return string */ protected function buildFragment() { return $this->fragment ? '#'.$this->fragment : ''; } /** * Load a set of relationships onto the mixed relationship collection. * * @param string $relation * @param array $relations * @return $this */ public function loadMorph($relation, $relations) { $this->getCollection()->loadMorph($relation, $relations); return $this; } /** * Load a set of relationship counts onto the mixed relationship collection. * * @param string $relation * @param array $relations * @return $this */ public function loadMorphCount($relation, $relations) { $this->getCollection()->loadMorphCount($relation, $relations); return $this; } /** * Get the slice of items being paginated. * * @return array */ public function items() { return $this->items->all(); } /** * Transform each item in the slice of items using a callback. * * @param callable $callback * @return $this */ public function through(callable $callback) { $this->items->transform($callback); return $this; } /** * Get the number of items shown per page. * * @return int */ public function perPage() { return $this->perPage; } /** * Get the current cursor being paginated. * * @return \Illuminate\Pagination\Cursor|null */ public function cursor() { return $this->cursor; } /** * Get the query string variable used to store the cursor. * * @return string */ public function getCursorName() { return $this->cursorName; } /** * Set the query string variable used to store the cursor. * * @param string $name * @return $this */ public function setCursorName($name) { $this->cursorName = $name; return $this; } /** * Set the base path to assign to all URLs. * * @param string $path * @return $this */ public function withPath($path) { return $this->setPath($path); } /** * Set the base path to assign to all URLs. * * @param string $path * @return $this */ public function setPath($path) { $this->path = $path; return $this; } /** * Get the base path for paginator generated URLs. * * @return string|null */ public function path() { return $this->path; } /** * Resolve the current cursor or return the default value. * * @param string $cursorName * @return \Illuminate\Pagination\Cursor|null */ public static function resolveCurrentCursor($cursorName = 'cursor', $default = null) { if (isset(static::$currentCursorResolver)) { return call_user_func(static::$currentCursorResolver, $cursorName); } return $default; } /** * Set the current cursor resolver callback. * * @param \Closure $resolver * @return void */ public static function currentCursorResolver(Closure $resolver) { static::$currentCursorResolver = $resolver; } /** * Get an instance of the view factory from the resolver. * * @return \Illuminate\Contracts\View\Factory */ public static function viewFactory() { return Paginator::viewFactory(); } /** * Get an iterator for the items. * * @return \ArrayIterator */ public function getIterator(): Traversable { return $this->items->getIterator(); } /** * Determine if the list of items is empty. * * @return bool */ public function isEmpty() { return $this->items->isEmpty(); } /** * Determine if the list of items is not empty. * * @return bool */ public function isNotEmpty() { return $this->items->isNotEmpty(); } /** * Get the number of items for the current page. * * @return int */ public function count(): int { return $this->items->count(); } /** * Get the paginator's underlying collection. * * @return \Illuminate\Support\Collection */ public function getCollection() { return $this->items; } /** * Set the paginator's underlying collection. * * @param \Illuminate\Support\Collection $collection * @return $this */ public function setCollection(Collection $collection) { $this->items = $collection; return $this; } /** * Get the paginator options. * * @return array */ public function getOptions() { return $this->options; } /** * Determine if the given item exists. * * @param mixed $key * @return bool */ public function offsetExists($key): bool { return $this->items->has($key); } /** * Get the item at the given offset. * * @param mixed $key * @return mixed */ public function offsetGet($key): mixed { return $this->items->get($key); } /** * Set the item at the given offset. * * @param mixed $key * @param mixed $value * @return void */ public function offsetSet($key, $value): void { $this->items->put($key, $value); } /** * Unset the item at the given key. * * @param mixed $key * @return void */ public function offsetUnset($key): void { $this->items->forget($key); } /** * Render the contents of the paginator to HTML. * * @return string */ public function toHtml() { return (string) $this->render(); } /** * Make dynamic calls into the collection. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { return $this->forwardCallTo($this->getCollection(), $method, $parameters); } /** * Render the contents of the paginator when casting to a string. * * @return string */ public function __toString() { return (string) $this->render(); } } AbstractPaginator.php000064400000041104147536115330010675 0ustar00= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false; } /** * Get the URL for the previous page. * * @return string|null */ public function previousPageUrl() { if ($this->currentPage() > 1) { return $this->url($this->currentPage() - 1); } } /** * Create a range of pagination URLs. * * @param int $start * @param int $end * @return array */ public function getUrlRange($start, $end) { return collect(range($start, $end))->mapWithKeys(function ($page) { return [$page => $this->url($page)]; })->all(); } /** * Get the URL for a given page number. * * @param int $page * @return string */ public function url($page) { if ($page <= 0) { $page = 1; } // If we have any extra query string key / value pairs that need to be added // onto the URL, we will put them in query string form and then attach it // to the URL. This allows for extra information like sortings storage. $parameters = [$this->pageName => $page]; if (count($this->query) > 0) { $parameters = array_merge($this->query, $parameters); } return $this->path() .(str_contains($this->path(), '?') ? '&' : '?') .Arr::query($parameters) .$this->buildFragment(); } /** * Get / set the URL fragment to be appended to URLs. * * @param string|null $fragment * @return $this|string|null */ public function fragment($fragment = null) { if (is_null($fragment)) { return $this->fragment; } $this->fragment = $fragment; return $this; } /** * Add a set of query string values to the paginator. * * @param array|string|null $key * @param string|null $value * @return $this */ public function appends($key, $value = null) { if (is_null($key)) { return $this; } if (is_array($key)) { return $this->appendArray($key); } return $this->addQuery($key, $value); } /** * Add an array of query string values. * * @param array $keys * @return $this */ protected function appendArray(array $keys) { foreach ($keys as $key => $value) { $this->addQuery($key, $value); } return $this; } /** * Add all current query string values to the paginator. * * @return $this */ public function withQueryString() { if (isset(static::$queryStringResolver)) { return $this->appends(call_user_func(static::$queryStringResolver)); } return $this; } /** * Add a query string value to the paginator. * * @param string $key * @param string $value * @return $this */ protected function addQuery($key, $value) { if ($key !== $this->pageName) { $this->query[$key] = $value; } return $this; } /** * Build the full fragment portion of a URL. * * @return string */ protected function buildFragment() { return $this->fragment ? '#'.$this->fragment : ''; } /** * Load a set of relationships onto the mixed relationship collection. * * @param string $relation * @param array $relations * @return $this */ public function loadMorph($relation, $relations) { $this->getCollection()->loadMorph($relation, $relations); return $this; } /** * Load a set of relationship counts onto the mixed relationship collection. * * @param string $relation * @param array $relations * @return $this */ public function loadMorphCount($relation, $relations) { $this->getCollection()->loadMorphCount($relation, $relations); return $this; } /** * Get the slice of items being paginated. * * @return array */ public function items() { return $this->items->all(); } /** * Get the number of the first item in the slice. * * @return int|null */ public function firstItem() { return count($this->items) > 0 ? ($this->currentPage - 1) * $this->perPage + 1 : null; } /** * Get the number of the last item in the slice. * * @return int|null */ public function lastItem() { return count($this->items) > 0 ? $this->firstItem() + $this->count() - 1 : null; } /** * Transform each item in the slice of items using a callback. * * @param callable $callback * @return $this */ public function through(callable $callback) { $this->items->transform($callback); return $this; } /** * Get the number of items shown per page. * * @return int */ public function perPage() { return $this->perPage; } /** * Determine if there are enough items to split into multiple pages. * * @return bool */ public function hasPages() { return $this->currentPage() != 1 || $this->hasMorePages(); } /** * Determine if the paginator is on the first page. * * @return bool */ public function onFirstPage() { return $this->currentPage() <= 1; } /** * Determine if the paginator is on the last page. * * @return bool */ public function onLastPage() { return ! $this->hasMorePages(); } /** * Get the current page. * * @return int */ public function currentPage() { return $this->currentPage; } /** * Get the query string variable used to store the page. * * @return string */ public function getPageName() { return $this->pageName; } /** * Set the query string variable used to store the page. * * @param string $name * @return $this */ public function setPageName($name) { $this->pageName = $name; return $this; } /** * Set the base path to assign to all URLs. * * @param string $path * @return $this */ public function withPath($path) { return $this->setPath($path); } /** * Set the base path to assign to all URLs. * * @param string $path * @return $this */ public function setPath($path) { $this->path = $path; return $this; } /** * Set the number of links to display on each side of current page link. * * @param int $count * @return $this */ public function onEachSide($count) { $this->onEachSide = $count; return $this; } /** * Get the base path for paginator generated URLs. * * @return string|null */ public function path() { return $this->path; } /** * Resolve the current request path or return the default value. * * @param string $default * @return string */ public static function resolveCurrentPath($default = '/') { if (isset(static::$currentPathResolver)) { return call_user_func(static::$currentPathResolver); } return $default; } /** * Set the current request path resolver callback. * * @param \Closure $resolver * @return void */ public static function currentPathResolver(Closure $resolver) { static::$currentPathResolver = $resolver; } /** * Resolve the current page or return the default value. * * @param string $pageName * @param int $default * @return int */ public static function resolveCurrentPage($pageName = 'page', $default = 1) { if (isset(static::$currentPageResolver)) { return (int) call_user_func(static::$currentPageResolver, $pageName); } return $default; } /** * Set the current page resolver callback. * * @param \Closure $resolver * @return void */ public static function currentPageResolver(Closure $resolver) { static::$currentPageResolver = $resolver; } /** * Resolve the query string or return the default value. * * @param string|array|null $default * @return string */ public static function resolveQueryString($default = null) { if (isset(static::$queryStringResolver)) { return (static::$queryStringResolver)(); } return $default; } /** * Set with query string resolver callback. * * @param \Closure $resolver * @return void */ public static function queryStringResolver(Closure $resolver) { static::$queryStringResolver = $resolver; } /** * Get an instance of the view factory from the resolver. * * @return \Illuminate\Contracts\View\Factory */ public static function viewFactory() { return call_user_func(static::$viewFactoryResolver); } /** * Set the view factory resolver callback. * * @param \Closure $resolver * @return void */ public static function viewFactoryResolver(Closure $resolver) { static::$viewFactoryResolver = $resolver; } /** * Set the default pagination view. * * @param string $view * @return void */ public static function defaultView($view) { static::$defaultView = $view; } /** * Set the default "simple" pagination view. * * @param string $view * @return void */ public static function defaultSimpleView($view) { static::$defaultSimpleView = $view; } /** * Indicate that Tailwind styling should be used for generated links. * * @return void */ public static function useTailwind() { static::defaultView('pagination::tailwind'); static::defaultSimpleView('pagination::simple-tailwind'); } /** * Indicate that Bootstrap 4 styling should be used for generated links. * * @return void */ public static function useBootstrap() { static::useBootstrapFour(); } /** * Indicate that Bootstrap 3 styling should be used for generated links. * * @return void */ public static function useBootstrapThree() { static::defaultView('pagination::default'); static::defaultSimpleView('pagination::simple-default'); } /** * Indicate that Bootstrap 4 styling should be used for generated links. * * @return void */ public static function useBootstrapFour() { static::defaultView('pagination::bootstrap-4'); static::defaultSimpleView('pagination::simple-bootstrap-4'); } /** * Indicate that Bootstrap 5 styling should be used for generated links. * * @return void */ public static function useBootstrapFive() { static::defaultView('pagination::bootstrap-5'); static::defaultSimpleView('pagination::simple-bootstrap-5'); } /** * Get an iterator for the items. * * @return \ArrayIterator */ public function getIterator(): Traversable { return $this->items->getIterator(); } /** * Determine if the list of items is empty. * * @return bool */ public function isEmpty() { return $this->items->isEmpty(); } /** * Determine if the list of items is not empty. * * @return bool */ public function isNotEmpty() { return $this->items->isNotEmpty(); } /** * Get the number of items for the current page. * * @return int */ public function count(): int { return $this->items->count(); } /** * Get the paginator's underlying collection. * * @return \Illuminate\Support\Collection */ public function getCollection() { return $this->items; } /** * Set the paginator's underlying collection. * * @param \Illuminate\Support\Collection $collection * @return $this */ public function setCollection(Collection $collection) { $this->items = $collection; return $this; } /** * Get the paginator options. * * @return array */ public function getOptions() { return $this->options; } /** * Determine if the given item exists. * * @param mixed $key * @return bool */ public function offsetExists($key): bool { return $this->items->has($key); } /** * Get the item at the given offset. * * @param mixed $key * @return mixed */ public function offsetGet($key): mixed { return $this->items->get($key); } /** * Set the item at the given offset. * * @param mixed $key * @param mixed $value * @return void */ public function offsetSet($key, $value): void { $this->items->put($key, $value); } /** * Unset the item at the given key. * * @param mixed $key * @return void */ public function offsetUnset($key): void { $this->items->forget($key); } /** * Render the contents of the paginator to HTML. * * @return string */ public function toHtml() { return (string) $this->render(); } /** * Make dynamic calls into the collection. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { return $this->forwardCallTo($this->getCollection(), $method, $parameters); } /** * Render the contents of the paginator when casting to a string. * * @return string */ public function __toString() { return (string) $this->render(); } } Cursor.php000064400000006236147536115330006551 0ustar00parameters = $parameters; $this->pointsToNextItems = $pointsToNextItems; } /** * Get the given parameter from the cursor. * * @param string $parameterName * @return string|null * * @throws \UnexpectedValueException */ public function parameter(string $parameterName) { if (! array_key_exists($parameterName, $this->parameters)) { throw new UnexpectedValueException("Unable to find parameter [{$parameterName}] in pagination item."); } return $this->parameters[$parameterName]; } /** * Get the given parameters from the cursor. * * @param array $parameterNames * @return array */ public function parameters(array $parameterNames) { return collect($parameterNames)->map(function ($parameterName) { return $this->parameter($parameterName); })->toArray(); } /** * Determine whether the cursor points to the next set of items. * * @return bool */ public function pointsToNextItems() { return $this->pointsToNextItems; } /** * Determine whether the cursor points to the previous set of items. * * @return bool */ public function pointsToPreviousItems() { return ! $this->pointsToNextItems; } /** * Get the array representation of the cursor. * * @return array */ public function toArray() { return array_merge($this->parameters, [ '_pointsToNextItems' => $this->pointsToNextItems, ]); } /** * Get the encoded string representation of the cursor to construct a URL. * * @return string */ public function encode() { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($this->toArray()))); } /** * Get a cursor instance from the encoded string representation. * * @param string|null $encodedString * @return static|null */ public static function fromEncoded($encodedString) { if (! is_string($encodedString)) { return null; } $parameters = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $encodedString)), true); if (json_last_error() !== JSON_ERROR_NONE) { return null; } $pointsToNextItems = $parameters['_pointsToNextItems']; unset($parameters['_pointsToNextItems']); return new static($parameters, $pointsToNextItems); } } CursorPaginator.php000064400000010706147536115330010413 0ustar00options = $options; foreach ($options as $key => $value) { $this->{$key} = $value; } $this->perPage = (int) $perPage; $this->cursor = $cursor; $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path; $this->setItems($items); } /** * Set the items for the paginator. * * @param mixed $items * @return void */ protected function setItems($items) { $this->items = $items instanceof Collection ? $items : Collection::make($items); $this->hasMore = $this->items->count() > $this->perPage; $this->items = $this->items->slice(0, $this->perPage); if (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems()) { $this->items = $this->items->reverse()->values(); } } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return \Illuminate\Contracts\Support\Htmlable */ public function links($view = null, $data = []) { return $this->render($view, $data); } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return \Illuminate\Contracts\Support\Htmlable */ public function render($view = null, $data = []) { return static::viewFactory()->make($view ?: Paginator::$defaultSimpleView, array_merge($data, [ 'paginator' => $this, ])); } /** * Determine if there are more items in the data source. * * @return bool */ public function hasMorePages() { return (is_null($this->cursor) && $this->hasMore) || (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && $this->hasMore) || (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems()); } /** * Determine if there are enough items to split into multiple pages. * * @return bool */ public function hasPages() { return ! $this->onFirstPage() || $this->hasMorePages(); } /** * Determine if the paginator is on the first page. * * @return bool */ public function onFirstPage() { return is_null($this->cursor) || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore); } /** * Determine if the paginator is on the last page. * * @return bool */ public function onLastPage() { return ! $this->hasMorePages(); } /** * Get the instance as an array. * * @return array */ public function toArray() { return [ 'data' => $this->items->toArray(), 'path' => $this->path(), 'per_page' => $this->perPage(), 'next_cursor' => $this->nextCursor()?->encode(), 'next_page_url' => $this->nextPageUrl(), 'prev_cursor' => $this->previousCursor()?->encode(), 'prev_page_url' => $this->previousPageUrl(), ]; } /** * Convert the object into something JSON serializable. * * @return array */ public function jsonSerialize(): array { return $this->toArray(); } /** * Convert the object to its JSON representation. * * @param int $options * @return string */ public function toJson($options = 0) { return json_encode($this->jsonSerialize(), $options); } } LICENSE.md000064400000002063147536115330006161 0ustar00The MIT License (MIT) Copyright (c) Taylor Otwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. LengthAwarePaginator.php000064400000014047147536115330011341 0ustar00options = $options; foreach ($options as $key => $value) { $this->{$key} = $value; } $this->total = $total; $this->perPage = (int) $perPage; $this->lastPage = max((int) ceil($total / $perPage), 1); $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path; $this->currentPage = $this->setCurrentPage($currentPage, $this->pageName); $this->items = $items instanceof Collection ? $items : Collection::make($items); } /** * Get the current page for the request. * * @param int $currentPage * @param string $pageName * @return int */ protected function setCurrentPage($currentPage, $pageName) { $currentPage = $currentPage ?: static::resolveCurrentPage($pageName); return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1; } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return \Illuminate\Contracts\Support\Htmlable */ public function links($view = null, $data = []) { return $this->render($view, $data); } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return \Illuminate\Contracts\Support\Htmlable */ public function render($view = null, $data = []) { return static::viewFactory()->make($view ?: static::$defaultView, array_merge($data, [ 'paginator' => $this, 'elements' => $this->elements(), ])); } /** * Get the paginator links as a collection (for JSON responses). * * @return \Illuminate\Support\Collection */ public function linkCollection() { return collect($this->elements())->flatMap(function ($item) { if (! is_array($item)) { return [['url' => null, 'label' => '...', 'active' => false]]; } return collect($item)->map(function ($url, $page) { return [ 'url' => $url, 'label' => (string) $page, 'active' => $this->currentPage() === $page, ]; }); })->prepend([ 'url' => $this->previousPageUrl(), 'label' => function_exists('__') ? __('pagination.previous') : 'Previous', 'active' => false, ])->push([ 'url' => $this->nextPageUrl(), 'label' => function_exists('__') ? __('pagination.next') : 'Next', 'active' => false, ]); } /** * Get the array of elements to pass to the view. * * @return array */ protected function elements() { $window = UrlWindow::make($this); return array_filter([ $window['first'], is_array($window['slider']) ? '...' : null, $window['slider'], is_array($window['last']) ? '...' : null, $window['last'], ]); } /** * Get the total number of items being paginated. * * @return int */ public function total() { return $this->total; } /** * Determine if there are more items in the data source. * * @return bool */ public function hasMorePages() { return $this->currentPage() < $this->lastPage(); } /** * Get the URL for the next page. * * @return string|null */ public function nextPageUrl() { if ($this->hasMorePages()) { return $this->url($this->currentPage() + 1); } } /** * Get the last page. * * @return int */ public function lastPage() { return $this->lastPage; } /** * Get the instance as an array. * * @return array */ public function toArray() { return [ 'current_page' => $this->currentPage(), 'data' => $this->items->toArray(), 'first_page_url' => $this->url(1), 'from' => $this->firstItem(), 'last_page' => $this->lastPage(), 'last_page_url' => $this->url($this->lastPage()), 'links' => $this->linkCollection()->toArray(), 'next_page_url' => $this->nextPageUrl(), 'path' => $this->path(), 'per_page' => $this->perPage(), 'prev_page_url' => $this->previousPageUrl(), 'to' => $this->lastItem(), 'total' => $this->total(), ]; } /** * Convert the object into something JSON serializable. * * @return array */ public function jsonSerialize(): array { return $this->toArray(); } /** * Convert the object to its JSON representation. * * @param int $options * @return string */ public function toJson($options = 0) { return json_encode($this->jsonSerialize(), $options); } } PaginationServiceProvider.php000075500000001344147536115330012417 0ustar00loadViewsFrom(__DIR__.'/resources/views', 'pagination'); if ($this->app->runningInConsole()) { $this->publishes([ __DIR__.'/resources/views' => $this->app->resourcePath('views/vendor/pagination'), ], 'laravel-pagination'); } } /** * Register the service provider. * * @return void */ public function register() { PaginationState::resolveUsing($this->app); } } PaginationState.php000064400000002013147536115330010353 0ustar00 $app['view']); Paginator::currentPathResolver(fn () => $app['request']->url()); Paginator::currentPageResolver(function ($pageName = 'page') use ($app) { $page = $app['request']->input($pageName); if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return (int) $page; } return 1; }); Paginator::queryStringResolver(fn () => $app['request']->query()); CursorPaginator::currentCursorResolver(function ($cursorName = 'cursor') use ($app) { return Cursor::fromEncoded($app['request']->input($cursorName)); }); } } Paginator.php000064400000010426147536115330007214 0ustar00options = $options; foreach ($options as $key => $value) { $this->{$key} = $value; } $this->perPage = $perPage; $this->currentPage = $this->setCurrentPage($currentPage); $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path; $this->setItems($items); } /** * Get the current page for the request. * * @param int $currentPage * @return int */ protected function setCurrentPage($currentPage) { $currentPage = $currentPage ?: static::resolveCurrentPage(); return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1; } /** * Set the items for the paginator. * * @param mixed $items * @return void */ protected function setItems($items) { $this->items = $items instanceof Collection ? $items : Collection::make($items); $this->hasMore = $this->items->count() > $this->perPage; $this->items = $this->items->slice(0, $this->perPage); } /** * Get the URL for the next page. * * @return string|null */ public function nextPageUrl() { if ($this->hasMorePages()) { return $this->url($this->currentPage() + 1); } } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return string */ public function links($view = null, $data = []) { return $this->render($view, $data); } /** * Render the paginator using the given view. * * @param string|null $view * @param array $data * @return \Illuminate\Contracts\Support\Htmlable */ public function render($view = null, $data = []) { return static::viewFactory()->make($view ?: static::$defaultSimpleView, array_merge($data, [ 'paginator' => $this, ])); } /** * Manually indicate that the paginator does have more pages. * * @param bool $hasMore * @return $this */ public function hasMorePagesWhen($hasMore = true) { $this->hasMore = $hasMore; return $this; } /** * Determine if there are more items in the data source. * * @return bool */ public function hasMorePages() { return $this->hasMore; } /** * Get the instance as an array. * * @return array */ public function toArray() { return [ 'current_page' => $this->currentPage(), 'data' => $this->items->toArray(), 'first_page_url' => $this->url(1), 'from' => $this->firstItem(), 'next_page_url' => $this->nextPageUrl(), 'path' => $this->path(), 'per_page' => $this->perPage(), 'prev_page_url' => $this->previousPageUrl(), 'to' => $this->lastItem(), ]; } /** * Convert the object into something JSON serializable. * * @return array */ public function jsonSerialize(): array { return $this->toArray(); } /** * Convert the object to its JSON representation. * * @param int $options * @return string */ public function toJson($options = 0) { return json_encode($this->jsonSerialize(), $options); } } UrlWindow.php000064400000013155147536115330007224 0ustar00paginator = $paginator; } /** * Create a new URL window instance. * * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator * @return array */ public static function make(PaginatorContract $paginator) { return (new static($paginator))->get(); } /** * Get the window of URLs to be shown. * * @return array */ public function get() { $onEachSide = $this->paginator->onEachSide; if ($this->paginator->lastPage() < ($onEachSide * 2) + 8) { return $this->getSmallSlider(); } return $this->getUrlSlider($onEachSide); } /** * Get the slider of URLs there are not enough pages to slide. * * @return array */ protected function getSmallSlider() { return [ 'first' => $this->paginator->getUrlRange(1, $this->lastPage()), 'slider' => null, 'last' => null, ]; } /** * Create a URL slider links. * * @param int $onEachSide * @return array */ protected function getUrlSlider($onEachSide) { $window = $onEachSide + 4; if (! $this->hasPages()) { return ['first' => null, 'slider' => null, 'last' => null]; } // If the current page is very close to the beginning of the page range, we will // just render the beginning of the page range, followed by the last 2 of the // links in this list, since we will not have room to create a full slider. if ($this->currentPage() <= $window) { return $this->getSliderTooCloseToBeginning($window, $onEachSide); } // If the current page is close to the ending of the page range we will just get // this first couple pages, followed by a larger window of these ending pages // since we're too close to the end of the list to create a full on slider. elseif ($this->currentPage() > ($this->lastPage() - $window)) { return $this->getSliderTooCloseToEnding($window, $onEachSide); } // If we have enough room on both sides of the current page to build a slider we // will surround it with both the beginning and ending caps, with this window // of pages in the middle providing a Google style sliding paginator setup. return $this->getFullSlider($onEachSide); } /** * Get the slider of URLs when too close to the beginning of the window. * * @param int $window * @param int $onEachSide * @return array */ protected function getSliderTooCloseToBeginning($window, $onEachSide) { return [ 'first' => $this->paginator->getUrlRange(1, $window + $onEachSide), 'slider' => null, 'last' => $this->getFinish(), ]; } /** * Get the slider of URLs when too close to the ending of the window. * * @param int $window * @param int $onEachSide * @return array */ protected function getSliderTooCloseToEnding($window, $onEachSide) { $last = $this->paginator->getUrlRange( $this->lastPage() - ($window + ($onEachSide - 1)), $this->lastPage() ); return [ 'first' => $this->getStart(), 'slider' => null, 'last' => $last, ]; } /** * Get the slider of URLs when a full slider can be made. * * @param int $onEachSide * @return array */ protected function getFullSlider($onEachSide) { return [ 'first' => $this->getStart(), 'slider' => $this->getAdjacentUrlRange($onEachSide), 'last' => $this->getFinish(), ]; } /** * Get the page range for the current page window. * * @param int $onEachSide * @return array */ public function getAdjacentUrlRange($onEachSide) { return $this->paginator->getUrlRange( $this->currentPage() - $onEachSide, $this->currentPage() + $onEachSide ); } /** * Get the starting URLs of a pagination slider. * * @return array */ public function getStart() { return $this->paginator->getUrlRange(1, 2); } /** * Get the ending URLs of a pagination slider. * * @return array */ public function getFinish() { return $this->paginator->getUrlRange( $this->lastPage() - 1, $this->lastPage() ); } /** * Determine if the underlying paginator being presented has pages to show. * * @return bool */ public function hasPages() { return $this->paginator->lastPage() > 1; } /** * Get the current page from the paginator. * * @return int */ protected function currentPage() { return $this->paginator->currentPage(); } /** * Get the last page from the paginator. * * @return int */ protected function lastPage() { return $this->paginator->lastPage(); } } composer.json000075500000001604147536115330007302 0ustar00{ "name": "illuminate/pagination", "description": "The Illuminate Pagination package.", "license": "MIT", "homepage": "https://laravel.com", "support": { "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, "authors": [ { "name": "Taylor Otwell", "email": "taylor@laravel.com" } ], "require": { "php": "^8.2", "ext-filter": "*", "illuminate/collections": "^11.0", "illuminate/contracts": "^11.0", "illuminate/support": "^11.0" }, "autoload": { "psr-4": { "Illuminate\\Pagination\\": "" } }, "extra": { "branch-alias": { "dev-master": "11.x-dev" } }, "config": { "sort-packages": true }, "minimum-stability": "dev" } resources/views/bootstrap-4.blade.php000064400000004044147536115330013662 0ustar00@if ($paginator->hasPages()) @endif resources/views/bootstrap-5.blade.php000064400000010300147536115330013653 0ustar00@if ($paginator->hasPages()) @endif resources/views/default.blade.php000064400000003510147536115330013125 0ustar00@if ($paginator->hasPages()) @endif resources/views/semantic-ui.blade.php000064400000003222147536115330013717 0ustar00@if ($paginator->hasPages()) @endif resources/views/simple-bootstrap-4.blade.php000064400000002035147536115330015147 0ustar00@if ($paginator->hasPages()) @endif resources/views/simple-bootstrap-5.blade.php000064400000002224147536115330015150 0ustar00@if ($paginator->hasPages()) @endif resources/views/simple-default.blade.php000064400000001405147536115330014415 0ustar00@if ($paginator->hasPages()) @endif resources/views/simple-tailwind.blade.php000064400000004041147536115330014603 0ustar00@if ($paginator->hasPages()) @endif resources/views/tailwind.blade.php000064400000021017147536115330013316 0ustar00@if ($paginator->hasPages()) @endif