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 Plugin Laravel Package

saloonphp/pagination-plugin

Adds paginated response support to SaloonPHP. Provides a PaginationPlugin with helpful abstractions to iterate through pages and results when working with APIs that return paginated data, keeping pagination logic out of your connectors and requests.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package

    composer require saloonphp/pagination-plugin
    

    Ensure your saloon/saloon version is v3+ (check composer.json).

  2. Register the Plugin In your connector’s extend() method:

    use Saloon\Contracts\Connector;
    use Saloon\Pagination\PaginationPlugin;
    
    public function extend(): void
    {
        $this->withPlugin(new PaginationPlugin());
    }
    
  3. Define a Pagination Strategy For a standard page/limit API (e.g., GitHub):

    use Saloon\Pagination\Strategies\PageLimitStrategy;
    
    $this->withPaginationStrategy(new PageLimitStrategy(
        pageParam: 'page',
        limitParam: 'per_page',
        responseKey: 'data'
    ));
    
  4. First Use Case: Fetch All Paginated Data

    $response = $this->call(new GetUsersRequest());
    $users = $response->paginate()->all(); // Returns a flattened array
    

Where to Look First

  • Documentation (if available; otherwise, check the src folder for PaginationPlugin and Strategies).
  • Example Connectors: Browse the Saloon ecosystem for real-world usage (e.g., saloonphp/github-connector).
  • Tests: The package’s test suite (tests/ folder) demonstrates edge cases like cursor-based pagination or async chunking.

Implementation Patterns

Core Workflows

1. Standard Page/Limit Pagination

// Define in connector
$this->withPaginationStrategy(new PageLimitStrategy(
    pageParam: 'page',
    limitParam: 'per_page',
    responseKey: 'items'
));

// Fetch paginated data
$paginator = $this->call(new GetPostsRequest())->paginate();
$posts = $paginator->all(); // Flattens all pages
$posts = $paginator->take(50); // Limits to 50 items

2. Cursor-Based Pagination

$this->withPaginationStrategy(new CursorStrategy(
    cursorParam: 'cursor',
    responseKey: 'edges',
    cursorKey: 'cursor',
    dataKey: 'node'
));

$paginator = $this->call(new GetCommentsRequest())->paginate();
$comments = $paginator->each(fn ($comment) => $this->process($comment));

3. Link Header Pagination

$this->withPaginationStrategy(new LinkHeaderStrategy(
    linkHeaderKey: 'Link',
    nextLinkPattern: '/rel="next"'
));

4. Async Chunking (Laravel Queues)

$paginator = $this->call(new GetLargeDatasetRequest())->paginate();
$paginator->chunk(100, function (array $chunk) {
    ProcessLargeChunkJob::dispatch($chunk);
});

Integration Tips

Laravel-Specific Patterns

  • Cache Paginated Results:

    $users = Cache::remember('api_users_all', now()->addHours(1), function () {
        return $this->call(new GetUsersRequest())->paginate()->all();
    });
    
  • Queue Async Processing:

    $paginator->each(function ($item) {
        ProcessItemJob::dispatch($item);
    });
    
  • Laravel Collections Integration:

    $collection = collect($paginator->all())->map(...);
    

Custom Strategies

Extend PaginationStrategy for niche APIs:

class CustomStrategy extends PaginationStrategy
{
    public function getNextPageData(array $response): ?array
    {
        return $response['meta']['next_page'] ?? null;
    }

    public function getPageParam(): string
    {
        return 'offset';
    }
}

Testing

Mock paginated responses in Pest:

$mock = $this->mockConnector()
    ->shouldReceive('call')
    ->once()
    ->andReturn(new Response(
        body: ['data' => [['id' => 1]], 'links' => ['next' => '/page/2']]
    ));

$paginator = $mock->paginate();
$paginator->assertHasNextPage();

Gotchas and Tips

Pitfalls

  1. Infinite Loop Detection

    • Issue: The plugin auto-detects infinite loops by checksumming request bodies. For async operations (e.g., queues), this can cause false positives.
    • Fix: Disable detection in the strategy:
      $strategy = new PageLimitStrategy(/* ... */);
      $strategy->disableLoopDetection();
      
    • Alternative: Use rewind() manually if needed (reset checksums):
      $paginator->rewind();
      
  2. Async Memory Leaks

    • Issue: Async methods (each(), chunk()) may hold large datasets in memory if not processed immediately.
    • Fix: Use Laravel Queues or chunk processing:
      $paginator->chunk(1000, fn ($chunk) => $this->processChunk($chunk));
      
  3. Deprecated Properties

    • Issue: Older versions used page property (now deprecated). Update to startPage:
      // Old (deprecated)
      $paginator->setPage(2);
      
      // New
      $paginator->setStartPage(2);
      
  4. Cursor Strategy Quirks

    • Issue: Some APIs return cursors in nested structures (e.g., data.cursor). Ensure cursorKey is correct.
    • Fix: Debug with dd($paginator->getLastResponse()) to inspect the response structure.
  5. PHP 8.5+ Type Safety

    • Issue: Fluent methods may throw type errors in PHP 8.5+ if return types are strict.
    • Fix: Update to v2.2.1+ for fixed return type hints:
      $paginator->take(10); // Now explicitly returns `Paginator`
      

Debugging Tips

  1. Inspect Responses

    $response = $this->call(new GetDataRequest());
    dd($response->paginate()->getLastResponse()); // Debug raw response
    
  2. Enable Verbose Logging

    $this->withPlugin(new PaginationPlugin([
        'debug' => true,
    ]));
    
  3. Checksum Mismatches

    • If loops are detected incorrectly, reset checksums:
      $paginator->resetChecksums();
      
  4. Performance Profiling

    • Use Laravel Debugbar to monitor:
      • Number of API calls ($paginator->getCallCount()).
      • Memory usage during chunking.

Extension Points

  1. Custom Pagination Strategies Extend Saloon\Pagination\PaginationStrategy to support:

    • GraphQL-style pagination (after, before).
    • Offset-based APIs (e.g., offset=0&limit=100).
  2. Event Hooks Listen to pagination events (e.g., PaginatorBeforeFetch):

    $this->withPlugin(new PaginationPlugin([
        'events' => [
            PaginatorBeforeFetch::class => fn ($paginator) => $this->log($paginator),
        ],
    ]));
    
  3. Middleware Integration Add request/response middleware to modify pagination behavior:

    $this->withPlugin(new PaginationPlugin([
        'middleware' => [
            new AddCustomHeaderMiddleware(),
        ],
    ]));
    
  4. Laravel Service Provider Register the plugin globally for all connectors:

    // app/Providers/SaloonServiceProvider.php
    public function register(): void
    {
        Saloon::extend(function ($connector) {
            $connector->withPlugin(new PaginationPlugin());
        });
    }
    

Pro Tips

  • Reuse Strategies: Define strategies in a central config file (e.g., config/pagination.php) and inject them dynamically.
  • Hybrid Pagination: Combine strategies (e.g., PageLimitStrategy + LinkHeaderStrategy) for APIs with fallback mechanisms.
  • Rate Limiting: Use Saloon’s RateLimiter with pagination to avoid hitting API limits:
    $this->withRateLimiter(new SaloonRateLimiter(1000)); // 1000 requests/min
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport