> ## 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.

# Defining a Resource

> Every CRUD surface starts with a single class extending `AbstractResource`. This page walks through each hook.

## Minimal Resource

```php theme={null}
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:

```php theme={null}
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:

```php theme={null}
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:

```php theme={null}
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.

```php theme={null}
public function tags(): array
{
    return [new TagObject('Animals', 'Animal management')];
}
```

## Working with Instances

Controllers build resource instances from requests and arrays:

```php theme={null}
$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:

```php theme={null}
$resource = AnimalResource::byRequest($request);
$resource->animal_uuid = $request->path()->animal_uuid;
```

Serialize per mode:

```php theme={null}
$resource->toArray(AbstractResource::MODE_LIST);   // respects excludeFromMode()
```

Fields are accessed as dynamic properties:

```php theme={null}
$resource->name = 'Leo';
echo $resource->name;
```

With the [docblock generator](/documentation/docblock-generator#resource-docblock-generation) run, these also autocomplete in your IDE.
