Skip to content

Latest commit

 

History

History
145 lines (115 loc) · 3.82 KB

File metadata and controls

145 lines (115 loc) · 3.82 KB

Pagination

The bundle provides built-in support for paginated collection endpoints with a standardized response format.

The Paginator Interface

namespace OpenSolid\Core\Domain\Repository;

/**
 * @template T of object
 */
interface Paginator extends \Traversable, \Countable
{
    public function getCurrentPage(): int;
    public function getItemsPerPage(): int;
    public function getLastPage(): int;
    public function getTotalItems(): int;
}

SelectablePaginator

A concrete implementation that works with Doctrine's Selectable interface (e.g. Doctrine Collections, Repositories):

use OpenSolid\Core\Domain\Repository\SelectablePaginator;

$paginator = new SelectablePaginator(
    selectable: $repository, // Any Selectable & Countable
    currentPage: $query->page,
    itemsPerPage: $query->itemsPerPage,
);

It supports an optional mapper to transform entities into view objects:

$paginator = new SelectablePaginator(
    selectable: $repository,
    currentPage: $query->page,
    itemsPerPage: $query->itemsPerPage,
    mapper: fn(Product $product) => new ProductView($product),
);

Controller Setup

Use #[GetCollection] with a Paginator return type:

use OpenSolid\Api\Routing\Attribute\GetCollection;
use OpenSolid\Core\Domain\Repository\Paginator;
use OpenSolid\Core\Domain\Repository\SelectablePaginator;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;

#[GetCollection(
    path: '/products',
    name: 'api_find_products',
    description: 'Find Products',
    tags: ['Product'],
)]
final readonly class FindProductsController
{
    /**
     * @return Paginator<ProductView>
     */
    public function __invoke(#[MapQueryString] ?FindProductsQuery $query = null): Paginator
    {
        $query ??= new FindProductsQuery();

        return new SelectablePaginator(
            selectable: $this->repository,
            currentPage: $query->page,
            itemsPerPage: $query->itemsPerPage,
        );
    }
}

Query Parameters with PaginationParams

The PaginationParams trait provides standard page and itemsPerPage query parameters with OpenAPI annotations:

use OpenApi\Attributes as OA;
use OpenSolid\Api\Controller\Model\Paginator\PaginationParams;

final class FindProductsQuery
{
    use PaginationParams; // Adds page (default: 1) and itemsPerPage (default: 20)

    #[OA\QueryParameter(description: 'Filter by product name')]
    public ?string $name = null;
}

The trait defines:

Parameter Type Default Constraints
page integer 1 minimum: 1
itemsPerPage integer 20 minimum: 1, maximum: 100

Response Format

When a controller returns a Paginator and the route has pagination enabled, the ApiPaginationDecorator wraps the result in a PageResponse:

{
  "items": [
    { "id": "...", "name": "Product A", "price": { "amount": 100, "currency": "USD" } },
    { "id": "...", "name": "Product B", "price": { "amount": 200, "currency": "EUR" } }
  ],
  "totalItems": 42
}

OpenAPI Schema

The AugmentOperations processor detects the Paginator<T> return type and generates a paginated schema:

responses:
  '200':
    content:
      application/json:
        schema:
          type: object
          properties:
            items:
              type: array
              description: The list of items
              items:
                $ref: '#/components/schemas/ProductView'
            totalItems:
              type: integer
              description: The total number of items
              example: 1

This schema is generated by PaginatorSchema and requires:

  1. The return type is Paginator<T> (with a @return docblock)
  2. The _api_pagination route default is true (set by #[GetCollection])