Skip to main content

Minimal Resource

namespace App\Resource;

use apivalk\apivalk\Documentation\OpenAPI\Object\TagObject;
use apivalk\apivalk\Documentation\Property\FloatProperty;
use apivalk\apivalk\Documentation\Property\StringProperty;
use apivalk\apivalk\Resource\AbstractResource;

class AnimalResource extends AbstractResource
{
    public function getName(): string
    {
        return 'animal';
    }

    public function excludeFromMode(string $mode): array
    {
        if ($mode === self::MODE_LIST) {
            return ['weight'];
        }

        return [];
    }

    protected function init(): void
    {
        $this->addProperty(new StringProperty('animal_uuid', 'Unique identifier of the animal'));
        $this->addProperty(new StringProperty('name', 'Animal name'));
        $this->addProperty(new StringProperty('type', 'Animal type'));
        $this->addProperty((new FloatProperty('weight', 'Weight in kg'))->setIsRequired(false));
    }
}
The resource is a pure data shape — no URL knowledge, no routing. URLs live in controllers.

Hook Reference

init(): void (required)

Declare all data properties with $this->addProperty(...). This includes the resource identifier — it is a regular property like any other, declared first by convention. The constructor is final — all setup happens here.

getName(): string (required)

Singular resource name. Used in descriptions (“Create animal”) and OpenAPI response names.

getPluralName(): string (optional)

Defaults to getName() . 's'. Override for irregular plurals:
public function getPluralName(): string
{
    return 'children';
}

excludeFromMode(string $mode): array (required)

Return property names to exclude from a given mode. Typical uses:
  • Hide password from every response.
  • Hide weight from list responses only.
  • Exclude animal_uuid from create request bodies (the server generates the identifier; use MODE_CREATE to suppress it from the body documentation).
Modes are constants on AbstractResource: MODE_CREATE, MODE_VIEW, MODE_UPDATE, MODE_DELETE, MODE_LIST.

availableFilters(): FilterInterface[]

Declare filters exposed on the list endpoint:
public function availableFilters(): array
{
    return [
        EnumFilter::equals(new EnumProperty('status', 'Status', ['active', 'archived'])),
        StringFilter::contains(new StringProperty('name', 'Name contains')),
    ];
}
Each filter becomes a typed query parameter with full OpenAPI documentation and is accessible in the controller as $request->filtering()->status.

availableSortings(): Sort[]

Declare sortable fields for the list endpoint:
public function availableSortings(): array
{
    return [
        Sort::asc('name'),
        Sort::desc('created_at'),
    ];
}
Any ?order_by=... field not in this list is rejected by the validation middleware with a 422.

tags(): TagObject[]

OpenAPI tags for grouping operations.
public function tags(): array
{
    return [new TagObject('Animals', 'Animal management')];
}

Working with Instances

Controllers build resource instances from requests and arrays:
$resource = AnimalResource::byRequest($request);   // from request body only
$resource = AnimalResource::byArray($row);         // from a database row
For view / update / delete, the identifier is a path parameter declared in getRoute(). Read it directly from the request and set it on the resource if needed:
$resource = AnimalResource::byRequest($request);
$resource->animal_uuid = $request->path()->animal_uuid;
Serialize per mode:
$resource->toArray(AbstractResource::MODE_LIST);   // respects excludeFromMode()
Fields are accessed as dynamic properties:
$resource->name = 'Leo';
echo $resource->name;
With the docblock generator run, these also autocomplete in your IDE.