# Operations
API Platform Core relies on the concept of operations. Operations can be applied to a resource exposed by the API. From
an implementation point of view, an operation is a link between a resource, a route and its related controller.
API Platform automatically registers typical [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations
and describes them in the exposed documentation (Hydra and Swagger). It also creates and registers routes corresponding
to these operations in the Symfony routing system (if it is available).
The behavior of built-in operations is briefly presented in the [Getting started](getting-started.md#mapping-the-entities)
guide.
The list of enabled operations can be configured on a per resource basis. Creating custom operations on specific routes
is also possible.
There are two types of operations: collection operations and item operations.
Collection operations act on a collection of resources. By default two routes are implemented: `POST` and `GET`. Item
operations act on an individual resource. 3 default routes are defined `GET`, `PUT` and `DELETE` (`PATCH` is also supported
when [using the JSON API format](content-negotiation.md), as required by the specification).
When the `ApiPlatform\Core\Annotation\ApiResource` annotation is applied to an entity class, the following built-in CRUD
operations are automatically enabled:
*Collection operations*
Method | Mandatory | Description
-------|-----------|------------------------------------------
`GET` | yes | Retrieve the (paginated) list of elements
`POST` | no | Create a new element
*Item operations*
Method | Mandatory | Description
---------|-----------|------------------
`GET` | yes | Retrieve element
`PUT` | no | Update an element
`DELETE` | no | Delete an element
## Enabling and Disabling Operations
If no operation are specified, all default CRUD operations are automatically registered. It is also possible - and recommended
for large projects - to define operations explicitly.
Keep in mind that `collectionOperations` and `itemOperations` behave independently. For instance, if you don't explicitly
configure operations for `collectionOperations`, `GET` and `POST` operations will be automatically registered, even if you
explicitly configure `itemOperations`. The reverse is also true.
Operations can be configured using annotations, XML or YAML. In the following examples, we enable only the built-in operation
for the `GET` method for both `collectionOperations` and `itemOperations` to create a readonly endpoint.
`itemOperations` and `collectionOperations` are arrays containing a list of operation. Each operation is defined by a key
corresponding to the name of the operation that can be anything you want and an array of properties as value. If an
empty list of operations is provided, all operations are disabled.
If the operation's name match a supported HTTP methods (`GET`, `POST`, `PUT` or `DELETE`), the corresponding `method` property
will be automatically added.
```php
```
API Platform Core is smart enough to automatically register the applicable Symfony route referencing a built-in CRUD action
just by specifying the method name as key, or by checking the explicitly configured HTTP method.
## Configuring Operations
The URL, the HTTP method and the Hydra context passed to documentation generators of operations is easy to configure.
In the next example, both `GET` and `PUT` operations are registered with custom URLs. Those will override the default generated
URLs. In addition to that, we replace the Hydra context for the `PUT` operation, and require the `id` parameter to be an integer.
```php
GET
/grimoire/{id}
\d+
brown
brown
https
{subdomain}.api-platform.com
PUT
/grimoire/{id}/update
bar
\d+
```
In all the previous examples, you can safely remove the `method` because the method name always match the operation name.
### Prefixing All Routes of All Operations
Sometimes it's also useful to put a whole resource into its own "namespace" regarding the URI. Let's say you want to
put everything that's related to a `Book` into the `library` so that URIs become `library/book/{id}`. In that case
you don't need to override all the operations to set the path but configure the `route_prefix` attribute for the whole entity instead:
```php
id;
}
}
```
```php
id;
}
}
```
Alternatively, you can use the YAML configuration format:
```yaml
# api/config/api_platform/resources.yaml
App\Entity\Answer: ~
App\Entity\Question:
properties:
answer:
subresource:
resourceClass: 'App\Entity\Answer'
collection: false
```
Note that all we had to do is to set up `@ApiSubresource` on the `Question::answer` relation. Because the `answer` is a to-one relation, we know that this subresource is an item. Therefore the response will look like this:
```json
{
"@context": "/contexts/Answer",
"@id": "/answers/42",
"@type": "Answer",
"id": 42,
"content": "Life, the Universe, and Everything",
"question": "/questions/42"
}
```
If you put the subresource on a relation that is to-many, you will retrieve a collection.
Last but not least, Subresources can be nested, such that `/questions/42/answer/comments` will get the collection of comments for the answer to question 42.
You may want custom groups on subresources. Because a subresource is nothing more than a collection operation, you can set `normalization_context` or `denormalization_context` on that operation. To do so, you need to override `collectionOperations`. Based on the above operation, because we retrieve an answer, we need to alter it's configuration:
```php
GET
foobar
```
In the previous examples, the `method` attribute is mandatory, because the operation name doesn't match a supported HTTP
method.
Note that the operation name, here `api_questions_answer_get_subresource`, is the important keyword.
It'll be automatically set to `$resources_$subresource(s)_get_subresource`. To find the correct operation name you
may use `bin/console debug:router`.
### Control the Path of Subresources
You can control the path of subresources with the `path` option of the `subresourceOperations` parameter:
```php
bookPublishingHandler = $bookPublishingHandler;
}
public function __invoke(Book $data): Book
{
$this->bookPublishingHandler->handle($data);
return $data;
}
}
```
This custom operation behaves exactly like the built-in operation: it returns a JSON-LD document corresponding to the id
passed in the URL.
Here we consider that [autowiring](https://symfony.com/doc/current/service_container/autowiring.html) is enabled for
controller classes (the default when using the API Platform distribution).
This action will be automatically registered as a service (the service name is the same as the class name:
`App\Controller\CreateBookPublication`).
API Platform automatically retrieves the appropriate PHP entity using the data provider then deserializes user data in it,
and for `POST` and `PUT` requests updates the entity with data provided by the user.
**Warning: the `__invoke()` method parameter [MUST be called `$data`](https://symfony.com/doc/current/components/http_kernel.html#getting-the-controller-arguments)**, otherwise, it will not be filled correctly!
Services (`$myService` here) are automatically injected thanks to the autowiring feature. You can type-hint any service
you need and it will be autowired too.
The `__invoke` method of the action is called when the matching route is hit. It can return either an instance of
`Symfony\Component\HttpFoundation\Response` (that will be displayed to the client immediately by the Symfony kernel) or,
like in this example, an instance of an entity mapped as a resource (or a collection of instances for collection operations).
In this case, the entity will pass through [all built-in event listeners](events.md) of API Platform. It will be
automatically validated, persisted and serialized in JSON-LD. Then the Symfony kernel will send the resulting document to
the client.
The routing has not been configured yet because we will add it at the resource configuration level:
```php
POST
/books/{id}/publication
App\Controller\CreateBookPublication
```
It is mandatory to set the `method`, `path` and `controller` attributes. They allow API platform to configure the routing path and
the associated controller respectively.
#### Serialization Groups
You may want different serialization groups for your custom operations. Just configure the proper `normalization_context` and/or `denormalization_context`in your operation:
```php
POST
/books/{id}/publication
App\Controller\CreateBookPublication
publication
```
#### Entity Retrieval
If you want to bypass the automatic retrieval of the entity in your custom operation, you can set the parameter
`_api_receive` to `false` in the `defaults` attribute:
```php
POST
/books/{id}/publication
App\Controller\CreateBookPublication
false
```
This way, it will skip the `Read`, `Deserialize` and `Validate` listeners (see [the event system](events.md) for more
information).
### Alternative Method
There is another way to create a custom operation. However, we do not encourage its use. Indeed, this one disperses
the configuration at the same time in the routing and the resource configuration.
The `post_publication` operation references the Symfony route named `book_post_publication`.
Since version 2.3, you can also use the route name as operation name by convention, as shown in the following example
for `book_post_discontinuation` when neither `method` nor `route_name` attributes are specified.
First, let's create your resource configuration:
```php
book_post_publication
```
API Platform will automatically map this `post_publication` operation to the route `book_post_publication`. Let's create a custom action
and its related route using annotations:
```php
bookPublishingHandler = $bookPublishingHandler;
}
/**
* @Route(
* name="book_post_publication",
* path="/books/{id}/publication",
* methods={"POST"},
* defaults={
* "_api_resource_class"=Book::class,
* "_api_item_operation_name"="post_publication"
* }
* )
*/
public function __invoke(Book $data): Book
{
$this->bookPublishingHandler->handle($data);
return $data;
}
}
```
It is mandatory to set `_api_resource_class` and `_api_item_operation_name` (or `_api_collection_operation_name` for a collection
operation) in the parameters of the route (`defaults` key). It allows API Platform to work with the Symfony routing system.
Alternatively, you can also use a traditional Symfony controller and YAML or XML route declarations. The following example does
the exact same thing as the previous example:
```php
handle($data);
}
}
```
```yaml
# api/config/routes.yaml
book_post_publication:
path: /books/{id}/publication
methods: ['POST']
defaults:
_controller: App\Controller\BookController::createPublication
_api_resource_class: App\Entity\Book
_api_item_operation_name: post_publication
```