| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- <?php
- namespace Illuminate\Pagination;
- use ArrayAccess;
- use Closure;
- use Exception;
- use Illuminate\Contracts\Support\Htmlable;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\Relations\Pivot;
- use Illuminate\Http\Resources\Json\JsonResource;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Collection;
- use Illuminate\Support\Str;
- use Illuminate\Support\Traits\ForwardsCalls;
- use Illuminate\Support\Traits\Tappable;
- use Stringable;
- use Traversable;
- /**
- * @mixin \Illuminate\Support\Collection
- */
- abstract class AbstractCursorPaginator implements Htmlable, Stringable
- {
- use ForwardsCalls, Tappable;
- /**
- * All of the items being paginated.
- *
- * @var \Illuminate\Support\Collection
- */
- protected $items;
- /**
- * The number of items to be shown per page.
- *
- * @var int
- */
- protected $perPage;
- /**
- * The base path to assign to all URLs.
- *
- * @var string
- */
- protected $path = '/';
- /**
- * The query parameters to add to all URLs.
- *
- * @var array
- */
- protected $query = [];
- /**
- * The URL fragment to add to all URLs.
- *
- * @var string|null
- */
- protected $fragment;
- /**
- * The cursor string variable used to store the page.
- *
- * @var string
- */
- protected $cursorName = 'cursor';
- /**
- * The current cursor.
- *
- * @var \Illuminate\Pagination\Cursor|null
- */
- protected $cursor;
- /**
- * The paginator parameters for the cursor.
- *
- * @var array
- */
- protected $parameters;
- /**
- * The paginator options.
- *
- * @var array
- */
- protected $options;
- /**
- * The current cursor resolver callback.
- *
- * @var \Closure
- */
- protected static $currentCursorResolver;
- /**
- * Get the URL for a given cursor.
- *
- * @param \Illuminate\Pagination\Cursor|null $cursor
- * @return string
- */
- public function url($cursor)
- {
- // 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 = is_null($cursor) ? [] : [$this->cursorName => $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();
- }
- }
|