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.
Install the Package
composer require saloonphp/pagination-plugin
Extend Saloon with the Plugin
In your AppServiceProvider or connector setup:
use Saloon\Saloon;
use SaloonHttp\Plugins\PaginationPlugin;
public function boot()
{
Saloon::extend('pagination', function () {
return new PaginationPlugin();
});
}
First Use Case: Basic Pagination
Use the paginate() method on a Saloon connector:
$connector = new GitHubConnector();
$paginatedUsers = $connector->paginate('users')->all();
Or process items directly:
$connector->paginate('users')->each(function ($user) {
// Process each user
});
Key Configuration
Specify pagination strategy (default: PageLimitStrategy):
$connector->withPaginationStrategy(new CursorStrategy());
$connector->paginate()
->page(2) // Start from page 2
->limit(50) // Fetch 50 items per page
->all(); // Get all items
$connector->withPaginationStrategy(new CursorStrategy())
->paginate()
->each(function ($item) {
// Process item
});
$connector->paginate()
->chunk(100, function ($items) {
ProcessItemsJob::dispatch($items);
});
$connector->paginate()
->cacheFor(3600) // Cache for 1 hour
->all();
class CustomStrategy implements PaginationStrategy
{
public function getNextCursor(array $response): ?string
{
return $response['next_cursor'] ?? null;
}
public function buildRequest(array $params): array
{
return [
'cursor' => $params['cursor'] ?? null,
'limit' => $params['limit'] ?? 30,
];
}
}
$connector->withPaginationStrategy(new CustomStrategy());
Bind the plugin globally in AppServiceProvider:
$this->app->singleton(PaginationPlugin::class, function () {
return new PaginationPlugin();
});
Use Saloon’s MockConnector:
$mock = new MockConnector();
$mock->shouldReceive('send')
->andReturn(
new PaginatedResponse(
['user1', 'user2'],
'next_cursor'
)
);
$paginator = $mock->paginate();
$paginator->assertCount(2);
Combine with Saloon’s RetryPlugin:
$connector->withPlugins([
new RetryPlugin(),
new PaginationPlugin(),
]);
Catch pagination-specific exceptions:
try {
$paginator->all();
} catch (PaginationException $e) {
Log::error('Pagination failed: ' . $e->getMessage());
}
Infinite Loop Detection
$connector->paginate()->disableLoopDetection();
Cursor Pagination Edge Cases
null or empty strings).getNextCursor() in your strategy:
public function getNextCursor(array $response): ?string
{
return $response['pagination']['next'] ?? null;
}
Async Processing Quirks
->chunk()) processes items in batches but does not guarantee order.->each() with a queue job for ordered processing.Caching Conflicts
->cacheFor(60)) for volatile data.Type Safety in PHP < 8.5
self but may cause IDE warnings.static in your IDE or upgrade to PHP 8.5+.Log Paginated Requests Enable Saloon’s debug mode:
$connector->withDebug();
Inspect Paginator State Dump the paginator object to debug:
dd($connector->paginate()->getState());
Checksum Mismatches
If you see InfiniteLoopException, verify:
->disableLoopDetection() if false positives occur.Memory Issues with Large Datasets
Allowed memory exhausted during ->all().->chunk() with Laravel Queues:
$connector->paginate()->chunk(500, function ($items) {
ProcessItemsJob::dispatch($items);
});
Custom Pagination Strategies
Extend PaginationStrategy for non-standard APIs:
class GraphQLStrategy implements PaginationStrategy
{
public function getNextCursor(array $response): ?string
{
return $response['data']['pagination']['endCursor'] ?? null;
}
public function buildRequest(array $params): array
{
return [
'query' => '...',
'variables' => [
'cursor' => $params['cursor'] ?? null,
'first' => $params['limit'] ?? 20,
],
];
}
}
Middleware for Pagination Add logic before/after pagination:
$connector->paginate()
->before(function () {
Log::info('Starting pagination');
})
->after(function () {
Log::info('Pagination complete');
});
Laravel Event Integration Dispatch events during pagination:
$connector->paginate()
->onPage(function ($page) {
event(new PaginationPageFetched($page));
});
Override Default Config Modify the plugin’s defaults (e.g., max retries):
$plugin = new PaginationPlugin([
'max_retries' => 3,
'chunk_size' => 200,
]);
How can I help you explore Laravel packages today?