# Pagination Controller Adoption Guide

This guide is for developers adding pagination to a new list controller that should use the pagination work already built in this repo.

## What Already Exists

Backend building blocks:

- `app/Http/Requests/PaginationRequest.php`
- `app/Services/Pagination/PaginationService.php`
- `app/Http/Resources/PaginationResource.php`

Frontend shared list pieces:

- `resources/js/Components/Layouts/CrudLayout.vue`
- `resources/js/Components/Base/Datatable.vue`
- `resources/js/composables/useQuery.ts`
- `resources/js/types/index.ts`

Reference implementation:

- `app/Http/Controllers/SubscriptionHistoryController.php`
- `resources/js/Pages/History.vue`

## The Contract

New paginated controllers must return the standard shape:

- `data`
- `meta.current_page`
- `meta.last_page`
- `meta.per_page`
- `meta.total`
- `links.next`
- `links.prev`

New frontend list screens should consume the same `data` and `meta` shape through `CrudLayout`.

## Backend Adoption Steps

Use these steps when adding pagination to a new controller.

### 1. Create or reuse a list controller action

Start with a controller action that returns a list of records.

The action should:

- build the base Eloquent query
- apply any existing filter logic
- apply any existing sort logic
- paginate the result
- wrap the paginator in `PaginationResource`

### 2. Type-hint `PaginationRequest`

In the controller action, accept `App\Http\Requests\PaginationRequest`.

Use its helpers instead of reading raw query parameters:

- `getPage()`
- `getPerPage()`
- `getSort()`
- `getFilters()`

This keeps request normalization consistent across controllers.

### 3. Apply existing filters and sorting before pagination

Keep your current filter and sort behavior.

Do not add new filter syntax or new sort syntax.

The intended pattern is:

1. Build the query
2. Apply existing sort behavior
3. Apply existing filter behavior
4. Paginate
5. Wrap the result

### 4. Paginate with `PaginationService`

Call `PaginationService::paginate()` with:

- the prepared Eloquent builder
- the normalized page number
- the normalized page size

The service should be used only for pagination execution. It should not become a second filter/sort layer.

### 5. Wrap the result in `PaginationResource`

Return the paginator through `PaginationResource`.

This is what preserves the standard response shape expected by the frontend.

### 6. Append any computed fields after pagination

If your records need derived fields, append them after you have the paginator collection.

That matches the existing pattern in `SubscriptionHistoryController`.

## Example Controller Flow

Use this sequence as the model for a new controller:

1. Accept `PaginationRequest`
2. Build the query
3. Apply existing sorting
4. Apply existing filtering
5. Call `PaginationService`
6. Append computed fields, if needed
7. Return `PaginationResource`

## Frontend Adoption Steps

If the new controller feeds a new shared list screen, wire the frontend like this.

### 1. Pass the standard response shape into `CrudLayout`

Your page component should pass:

- `data`
- `meta`
- `emptyMessage`
- any extra filters

### 2. Let `CrudLayout` handle page changes

Do not add a separate pagination state system in the page component.

`CrudLayout` already:

- reads `meta`
- computes the first row offset
- sends `page` and `per_page` through `useQuery`
- preserves the shared filter and sort behavior

### 3. Keep using the shared table wrapper

`Base/Datatable.vue` already understands pagination props and page events.

New screens should use the shared layout rather than building their own pagination controls.

## Example New Controller Checklist

When adding pagination to a new controller, confirm the following:

- The action accepts `PaginationRequest`
- The query still uses the existing filter and sort flow
- The query is passed to `PaginationService`
- The response is wrapped in `PaginationResource`
- The frontend page passes `meta` into `CrudLayout`
- Pagination controls appear only when `meta` exists

## Common Mistakes

- Reading `page` and `per_page` directly from the request in the controller
- Reimplementing filter or sort parsing inside the pagination service
- Returning a raw paginator instead of `PaginationResource`
- Appending derived fields before pagination instead of after it
- Creating a custom pagination composable on the frontend
- Duplicating pagination controls in the page component instead of using `CrudLayout`

## Minimal New Controller Flow

For a new list controller, the shape should be close to this:

```php
public function index(PaginationRequest $request)
{
    $query = YourModel::query();

    // keep your existing filtering and sorting here

    $paginator = $this->paginationService->paginate(
        $query,
        $request->getPage(),
        $request->getPerPage()
    );

    return inertia('YourPage', [
        'yourProp' => (new PaginationResource($paginator))->toArray($request),
    ]);
}
```

## File References

- Backend request normalization: [PaginationRequest.php](/home/mbadr2200/VOM/vom_subscription/app/Http/Requests/PaginationRequest.php)
- Backend paginator: [PaginationService.php](/home/mbadr2200/VOM/vom_subscription/app/Services/Pagination/PaginationService.php)
- Backend response wrapper: [PaginationResource.php](/home/mbadr2200/VOM/vom_subscription/app/Http/Resources/PaginationResource.php)
- Shared list layout: [CrudLayout.vue](/home/mbadr2200/VOM/vom_subscription/resources/js/Components/Layouts/CrudLayout.vue)
- Shared table wrapper: [Datatable.vue](/home/mbadr2200/VOM/vom_subscription/resources/js/Components/Base/Datatable.vue)
- Query helper: [useQuery.ts](/home/mbadr2200/VOM/vom_subscription/resources/js/composables/useQuery.ts)
- Reference controller: [SubscriptionHistoryController.php](/home/mbadr2200/VOM/vom_subscription/app/Http/Controllers/SubscriptionHistoryController.php)
- Reference page: [History.vue](/home/mbadr2200/VOM/vom_subscription/resources/js/Pages/History.vue)
