Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pagination Laravel Package

spiral/pagination

Spiral Pagination Toolkit provides lightweight, framework-agnostic pagination primitives for PHP apps. Build and pass around page limits/offsets and related metadata cleanly, with strong type safety, tests, and Psalm support.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package:

    composer require spiral/pagination
    

    Ensure your composer.json specifies PHP ^8.1 or higher.

  2. Understand Core Interfaces: The package provides three primary interfaces:

    • PageInterface: Represents a single page of data (e.g., getItems(), getTotal()).
    • PaginatorInterface: Manages pagination logic (e.g., getPage(), getPerPage()).
    • CursorPaginatorInterface: For cursor-based pagination (e.g., getCursor()).
  3. First Use Case: Wrap a Collection Create a simple paginator for an in-memory collection (e.g., API responses or non-DB data):

    use Spiral\Pagination\PageInterface;
    use Spiral\Pagination\PaginatorInterface;
    
    class SimplePaginator implements PaginatorInterface
    {
        private array $items;
        private int $perPage;
        private int $currentPage;
    
        public function __construct(array $items, int $perPage, int $currentPage = 1)
        {
            $this->items = $items;
            $this->perPage = $perPage;
            $this->currentPage = $currentPage;
        }
    
        public function getPage(): int
        {
            return $this->currentPage;
        }
    
        public function getPerPage(): int
        {
            return $this->perPage;
        }
    
        public function getTotal(): int
        {
            return count($this->items);
        }
    
        public function getItems(): array
        {
            $start = ($this->currentPage - 1) * $this->perPage;
            return array_slice($this->items, $start, $this->perPage);
        }
    
        public function getPageCount(): int
        {
            return ceil($this->getTotal() / $this->perPage);
        }
    }
    

    Usage:

    $data = range(1, 100);
    $paginator = new SimplePaginator($data, 10, 1);
    $page = $paginator->getItems(); // Returns [1, 2, ..., 10]
    
  4. Where to Look First:

    • Interfaces: src/PageInterface.php, src/PaginatorInterface.php, src/CursorPaginatorInterface.php.
    • Tests: tests/ for usage examples (though minimal).
    • README: Focus on the "Product Decisions" section for design intent.

Implementation Patterns

Common Workflows

1. Database Pagination (Eloquent/Query Builder)

Use the interfaces to standardize pagination across ORM and raw queries:

use Spiral\Pagination\PaginatorInterface;

class EloquentPaginator implements PaginatorInterface
{
    private \Illuminate\Database\Eloquent\Builder $query;
    private int $perPage;
    private int $currentPage;

    public function __construct(\Illuminate\Database\Eloquent\Builder $query, int $perPage, int $currentPage)
    {
        $this->query = $query;
        $this->perPage = $perPage;
        $this->currentPage = $currentPage;
    }

    public function getItems(): array
    {
        return $this->query->skip(($this->currentPage - 1) * $this->perPage)
                            ->take($this->perPage)
                            ->get()
                            ->toArray();
    }

    public function getTotal(): int
    {
        return $this->query->count();
    }

    // Implement other PaginatorInterface methods...
}

Integration with Laravel Controllers:

public function index(Request $request)
{
    $perPage = $request->input('per_page', 15);
    $currentPage = $request->input('page', 1);

    $query = User::query();
    $paginator = new EloquentPaginator($query, $perPage, $currentPage);

    return response()->json([
        'data' => $paginator->getItems(),
        'pagination' => [
            'total' => $paginator->getTotal(),
            'per_page' => $paginator->getPerPage(),
            'current_page' => $paginator->getPage(),
            'last_page' => $paginator->getPageCount(),
        ]
    ]);
}

2. Cursor-Based Pagination

Implement CursorPaginatorInterface for APIs needing efficient cursor-based pagination (e.g., GraphQL, infinite scroll):

use Spiral\Pagination\CursorPaginatorInterface;

class CursorPaginator implements CursorPaginatorInterface
{
    private array $items;
    private string $cursor;
    private int $perPage;

    public function __construct(array $items, string $cursor, int $perPage)
    {
        $this->items = $items;
        $this->cursor = $cursor;
        $this->perPage = $perPage;
    }

    public function getCursor(): string
    {
        return $this->cursor;
    }

    public function getItems(): array
    {
        // Logic to fetch items after cursor (e.g., from DB)
        return array_slice($this->items, 0, $this->perPage);
    }

    public function getNextCursor(): ?string
    {
        // Return next cursor or null if no more items
        return count($this->items) > $this->perPage ? 'next_cursor_value' : null;
    }
}

3. API Response Standardization

Create a response DTO to standardize pagination across all API endpoints:

use Spiral\Pagination\PageInterface;

class PaginatedResponse
{
    public function __construct(private PageInterface $page)
    {
    }

    public function toArray(): array
    {
        return [
            'data' => $this->page->getItems(),
            'meta' => [
                'pagination' => [
                    'total' => $this->page->getTotal(),
                    'per_page' => $this->page->getPerPage(),
                    'current_page' => $this->page->getPage(),
                    'last_page' => $this->page->getPageCount(),
                ]
            ]
        ];
    }
}

Usage:

$response = new PaginatedResponse($paginator);
return response()->json($response->toArray());

4. Service Layer Abstraction

Decouple pagination logic from controllers by creating a pagination service:

class PaginationService
{
    public function paginateCollection(array $collection, int $perPage, int $page = 1): PageInterface
    {
        return new SimplePaginator($collection, $perPage, $page);
    }

    public function paginateQuery(\Illuminate\Database\Eloquent\Builder $query, int $perPage, int $page = 1): PageInterface
    {
        return new EloquentPaginator($query, $perPage, $page);
    }
}

Controller Usage:

public function show(Request $request)
{
    $service = new PaginationService();
    $paginator = $service->paginateQuery(User::query(), 10, $request->page);
    return response()->json($paginator->getItems());
}

Integration Tips

  1. Leverage Laravel’s Service Container: Bind your paginators as singletons or resolve them dynamically:

    $app->bind(PaginationService::class, function ($app) {
        return new PaginationService();
    });
    
  2. Compose with Laravel’s Pagination: Adapt Laravel’s LengthAwarePaginator to PaginatorInterface:

    use Spiral\Pagination\PaginatorInterface;
    use Illuminate\Pagination\LengthAwarePaginator;
    
    class LaravelPaginatorAdapter implements PaginatorInterface
    {
        public function __construct(private LengthAwarePaginator $paginator)
        {
        }
    
        public function getItems(): array
        {
            return $this->paginator->items();
        }
    
        public function getTotal(): int
        {
            return $this->paginator->total();
        }
    
        // Implement other methods...
    }
    
  3. Testing: Mock PageInterface in unit tests:

    $mockPage = $this->createMock(PageInterface::class);
    $mockPage->method('getItems')->willReturn([1, 2, 3]);
    $mockPage->method('getTotal')->willReturn(100);
    
  4. Frontend Integration: Return consistent JSON structures for React/Vue:

    return response()->json([
        'data' => $paginator->getItems(),
        'pagination' => [
            'total' => $paginator->getTotal(),
            'per_page' => $paginator->getPerPage(),
            'current_page' => $paginator->getPage(),
            'next_cursor' => $paginator instanceof CursorPaginatorInterface ? $paginator->getNextCursor() : null,
        ]
    ]);
    

Gotchas and Tips

Pitfalls

  1. No Concrete Implementations:

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui