> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apivalk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Controller

> In Apivalk, controllers are the entry point for your business logic. Following the Single Responsibility Principle, each controller class should handle exactly one HTTP route and method.

## Creating a Controller

Every controller must extend `AbstractApivalkController` and implement four key methods:

1. `getRoute()`: Defines the path and HTTP method.
2. `getRequestClass()`: Specifies the `AbstractApivalkRequest` class used for this endpoint.
3. `getResponseClasses()`: Lists the possible `AbstractApivalkResponse` classes this controller can return.
4. `__invoke()`: The main execution logic.

### Example

```php theme={null}
namespace App\Http\Controller\Pet;

use apivalk\apivalk\Http\Controller\AbstractApivalkController;
use apivalk\apivalk\Http\Request\ApivalkRequestInterface;
use apivalk\apivalk\Http\Response\AbstractApivalkResponse;
use apivalk\apivalk\Router\Route\Route;
use apivalk\apivalk\Http\Method\GetMethod;
use App\Http\Request\Pet\GetPetRequest;
use App\Http\Response\Pet\GetPetResponse;
use App\Http\Response\Common\NotFoundResponse;

class GetPetController extends AbstractApivalkController
{
    public static function getRoute(): Route
    {
        return new Route('/pet/{id}', new GetMethod());
    }

    public static function getRequestClass(): string
    {
        return GetPetRequest::class;
    }

    public static function getResponseClasses(): array
    {
        return [
            GetPetResponse::class,
            NotFoundResponse::class,
        ];
    }

    public function __invoke(ApivalkRequestInterface $request): AbstractApivalkResponse
    {
        // $request is already populated and validated!
        $id = $request->path()->id;

        // Access populated sorting. SortBag is iterable — iterate directly.
        // The bag is always populated: if the user does not provide order_by,
        // it contains the default sorting from the route. Iteration order is
        // user-requested sorts first (in submission order), then route defaults
        // as tiebreakers — perfect for building an ORDER BY clause.
        foreach ($request->sorting() as $field => $sort) {
            $direction = $sort->isAsc() ? 'ASC' : 'DESC';
        }

        // Or directly access a specific field via the magic getter. If the user
        // did not pass order_by for status, this returns the route's default Sort.
        // Use $sort->isRequested() to discriminate user intent from defaults.
        $isAsc = $request->sorting()->status->isAsc();

        $pet = $this->petRepository->find($id);

        if (!$pet) {
            return new NotFoundResponse('Pet not found');
        }

        return new GetPetResponse($pet);
    }
}
```

## Dependency Injection

If you [configured a PSR-11 container](https://dopodev.mintlify.app/configuration#dependency-injection-psr-11-container), you can use constructor injection:

```php theme={null}
public function __construct(PetRepository $petRepository)
{
    $this->petRepository = $petRepository;
}
```

## Route Discovery

Apivalk uses `ClassLocator` to scan your controller directory. You don't need to register routes manually; the framework discovers them automatically by calling `getRoute()` on any class extending `AbstractApivalkController`.

## Pagination

For endpoints returning lists of data, see the [Pagination](/http/pagination) guide.

## Resource Controllers (CRUD)

If your endpoint follows a standard RESTful CRUD pattern against a single entity, you don't need to author a controller class like the one above by hand. Apivalk ships five abstract base controllers — `AbstractCreateResourceController`, `AbstractViewResourceController`, `AbstractUpdateResourceController`, `AbstractDeleteResourceController`, `AbstractListResourceController` — that derive their route, request class, response classes, and OpenAPI documentation from an `AbstractResource` declaration.

A minimal list controller looks like this:

```php theme={null}
use apivalk\apivalk\Http\Controller\Resource\AbstractListResourceController;
use App\Resource\AnimalResource;

/**
 * @extends AbstractListResourceController<AnimalResource>
 */
final class ListAnimalController extends AbstractListResourceController
{
    public static function getResourceClass(): string
    {
        return AnimalResource::class;
    }

    public function __invoke(ApivalkRequestInterface $request): AbstractApivalkResponse
    {
        // fetch, paginate, return ResourceListResponse
    }
}
```

See the full [Resources](/resources/index) section for how to declare a resource and wire up all five CRUD endpoints.
