companue/auto-paginate

Laravel package for automatic API pagination with infinite scroll support. Provides reusable traits and helpers for consistent paginated responses across all API endpoints.

Installs: 4

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/companue/auto-paginate

1.0.1 2025-12-14 08:38 UTC

This package is auto-updated.

Last update: 2025-12-14 10:09:53 UTC


README

Latest Version on Packagist Total Downloads

A Laravel package that provides automatic API pagination with infinite scroll support. Add pagination to any controller endpoint with just one line of code!

Features

  • One-Line Integration - Add pagination with a single method call
  • Automatic Scroll Detection - Perfect for infinite scroll UIs
  • Consistent Response Format - Standardized across all endpoints
  • Backward Compatible - Supports both paginated and non-paginated responses
  • Flexible - Works with Query Builders and Collections
  • Type Safe - Full PHP type hints and return types
  • Zero Configuration - Works out of the box

Installation

Install via Composer:

composer require companue/auto-paginate

The package will automatically register its service provider.

Quick Installation

Run the installation command for an interactive setup:

php artisan auto-paginate:install

This will guide you through two installation options:

  1. Publish AutoPaginatedController (Recommended) - Extend your controllers from it
  2. Update base Controller.php - Inject trait directly into the base controller

Manual Installation Options

Option 1: Publish AutoPaginatedController (Recommended)

Publish the AutoPaginatedController to your project:

php artisan vendor:publish --tag=auto-paginate-controller

Or use the command with the default option:

php artisan auto-paginate:install

This creates app/Http/Controllers/API/AutoPaginatedController.php with pagination methods built-in.

Then update your API controllers to extend it:

use App\Http\Controllers\API\AutoPaginatedController;

class YourController extends AutoPaginatedController
{
    public function index(Request $request)
    {
        $query = YourModel::query();
        return response()->json(
            $this->indexResponse($query, YourResource::class, $request)
        );
    }
}

Alternative: Use Trait Directly

If you prefer not to use AutoPaginatedController, add the trait to your controllers:

use Companue\AutoPaginate\Traits\PaginatesQueries;

class YourController extends Controller
{
    use PaginatesQueries;
    
    // Same usage as above
}

Option 2: Update Base Controller (Direct Injection)

Inject the trait directly into your base Controller.php:

php artisan auto-paginate:install --base-controller

This will:

  • Create a backup of your Controller.php at app/Http/Controllers/Controller.php.backup
  • Add use Companue\AutoPaginate\Traits\PaginatesQueries; import statement
  • Add PaginatesQueries to your existing trait list in the Controller class

Before:

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

After:

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Companue\AutoPaginate\Traits\PaginatesQueries;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests, PaginatesQueries;
}

After installation, all your controllers will have pagination methods without extending anything:

class YourController extends Controller
{
    public function index(Request $request)
    {
        $query = YourModel::query();
        return response()->json(
            $this->indexResponse($query, YourResource::class, $request)
        );
    }
}

Usage

Basic Usage

Add the trait to your base controller or any specific controller:

use Companue\AutoPaginate\Traits\PaginatesQueries;

class Controller extends BaseController
{
    use PaginatesQueries;
}

Then in your controller methods:

public function index(Request $request)
{
    $query = YourModel::query()->orderBy('created_at', 'desc');
    
    return response()->json(
        $this->indexResponse($query, YourResource::class, $request)
    );
}

That's it! Your endpoint now supports pagination.

With Filters

public function index(Request $request)
{
    $query = YourModel::query();
    
    // Apply filters
    if ($request->has('status')) {
        $query->where('status', $request->get('status'));
    }
    
    // Apply ordering
    $query->orderBy('created_at', 'desc');
    
    // Return with automatic pagination
    return response()->json(
        $this->indexResponse($query, YourResource::class, $request)
    );
}

Custom Page Size

public function index(Request $request)
{
    $query = YourModel::query();
    
    // Apply pagination with custom default (20 items per page)
    $paginator = $this->applyPagination($query, $request, 20);
    
    return response()->json(
        $this->paginatedResponse($paginator, YourResource::class)
    );
}

Without Resource Transformation

public function index(Request $request)
{
    $query = YourModel::query();
    
    // Pass null to return raw data without resource transformation
    return response()->json(
        $this->indexResponse($query, null, $request)
    );
}

Paginating Collections

public function index(Request $request)
{
    // Get and process collection
    $items = YourModel::query()
        ->get()
        ->map(function($item) {
            $item->calculated_field = $item->price * 1.1;
            return $item;
        });
    
    // Apply pagination to the collection
    $paginator = $this->applyPagination($items, $request);
    
    return response()->json(
        $this->paginatedResponse($paginator, YourResource::class)
    );
}

API Requests

Paginated Requests

GET /api/items?page=1
GET /api/items?page=2&per_page=20
GET /api/items?filter=active&page=1&per_page=10

Non-Paginated Requests

GET /api/items?paginate=false

Response Format

Paginated Response

{
  "data": [
    { "id": 1, "name": "Item 1" },
    { "id": 2, "name": "Item 2" }
  ],
  "pagination": {
    "current_page": 1,
    "last_page": 5,
    "per_page": 15,
    "total": 75,
    "from": 1,
    "to": 15,
    "has_more": true
  }
}

Non-Paginated Response

[
  { "id": 1, "name": "Item 1" },
  { "id": 2, "name": "Item 2" }
]

Available Methods

indexResponse($query, $resourceClass = null, $request = null)

Smart method that automatically handles pagination based on request parameters.

Parameters:

  • $query - Query Builder or Collection
  • $resourceClass - Optional resource class for transformation
  • $request - Optional request object (defaults to request())

Returns: Array with data and pagination info, or raw collection

applyPagination($query, $request = null, $defaultPerPage = 15)

Manually apply pagination to a query or collection.

Parameters:

  • $query - Query Builder or Collection
  • $request - Optional request object
  • $defaultPerPage - Default items per page (default: 15, max: 100)

Returns: LengthAwarePaginator

paginatedResponse($paginator, $resourceClass = null)

Build standardized paginated response from a paginator.

Parameters:

  • $paginator - LengthAwarePaginator instance
  • $resourceClass - Optional resource class

Returns: Array with data and pagination metadata

shouldPaginate($request = null)

Check if pagination should be applied based on request.

Parameters:

  • $request - Optional request object

Returns: Boolean

Configuration

The package works with sensible defaults:

  • Default page size: 15 items
  • Maximum page size: 100 items
  • Pagination parameter: page
  • Page size parameter: per_page

Frontend Integration

Perfect for infinite scroll implementations:

// React/Vue example
const loadMore = () => {
  if (pagination.has_more && !loading) {
    fetchData({ page: pagination.current_page + 1 });
  }
};

Migration from Manual Pagination

Before:

$perPage = $request->get('per_page', 15);
$records = YourModel::query()->paginate($perPage);
$items = YourResource::collection($records->items());

return response()->json([
    'data' => $items,
    'pagination' => [
        'current_page' => $records->currentPage(),
        'last_page' => $records->lastPage(),
        // ... more fields
    ]
]);

After:

$query = YourModel::query();

return response()->json(
    $this->indexResponse($query, YourResource::class, $request)
);

Testing

composer test

Requirements

  • PHP 8.1 or higher
  • Laravel 10.0, 11.0, or 12.0

License

The MIT License (MIT). Please see License File for more information.

Credits

Support

For support, please open an issue on GitHub or contact mimalefdal@gmail.com