Skip to content

docs(controllers): move from core to symfony and update #2093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions core/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ Those extensions points are taken into account both by the REST and [GraphQL](gr
The following tables summarizes which extension point to use depending on what you want to do:

| Extension Point | Usage |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [State Providers](state-providers.md) | adapters for custom persistence layers, virtual fields, custom hydration |
| [Denormalizers](serialization.md) | post-process objects created from the payload sent in the HTTP request body |
| [Voters](security.md#hooking-custom-permission-checks-using-voters) | custom authorization logic |
| [Symfony Voters](../symfony/security.md#hooking-custom-permission-checks-using-voters) | custom authorization logic |
| [Laravel Policies](../laravel/security.md#policies) | custom authorization logic |
| [Validation constraints](validation.md) | custom validation logic |
| [State Processors](state-processors) | custom business logic and computations to trigger before or after persistence (ex: mail, call to an external API...) |
| [Normalizers](serialization.md#decorating-a-serializer-and-adding-extra-data) | customize the resource sent to the client (add fields in JSON documents, encode codes, dates...) |
Expand All @@ -22,16 +23,16 @@ The following tables summarizes which extension point to use depending on what y

## Doctrine Specific Extension Points

| Extension Point | Usage |
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [Extensions](extensions.md) | Access to the query builder to change the DQL query |
| [Filters](filters.md#doctrine-orm-and-mongodb-odm-filters) | Add filters documentations (OpenAPI, GraphQL, Hydra) and automatically apply them to the DQL query |
| Extension Point | Usage |
|--------------------------------|----------------------------------------------------------------------------------------------------|
| [Extensions](extensions.md) | Access to the query builder to change the DQL query |
| [Filters](doctrine-filters.md) | Add filters documentations (OpenAPI, GraphQL, Hydra) and automatically apply them to the DQL query |

## Leveraging the Built-in Infrastructure Using Composition

While most API Platform classes are marked as `final`, built-in services are straightforward to reuse and customize [using composition](https://en.wikipedia.org/wiki/Composition_over_inheritance).

For instance, if you want to send a mail after a resource has been persisted, but still want to benefit from the native Doctrine ORM [state processor](state-processors.md), use [the decorator design pattern](https://en.wikipedia.org/wiki/Decorator_pattern#PHP) to wrap the native state processor in your own class sending the mail, as demonstrated in [this example](state-processors.md#decorating-the-built-in-state-processors).
For instance, if you want to send a mail after a resource has been persisted, but still want to benefit from the native Doctrine ORM [state processor](state-processors.md), use [the decorator design pattern](https://en.wikipedia.org/wiki/Decorator_pattern#PHP) to wrap the native state processor in your own class sending the mail, as demonstrated in [this example](../core/state-processors.md#creating-a-custom-state-processor).

To replace existing API Platform services with your decorators, [check out how to decorate services](https://symfony.com/doc/current/service_container/service_decoration.html).

Expand Down
2 changes: 1 addition & 1 deletion outline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ chapters:
- user
- jwt
- file-upload
- controllers
- title: "API Platform for Laravel"
path: laravel
items:
Expand Down Expand Up @@ -64,7 +65,6 @@ chapters:
- identifiers
- mongodb
- elasticsearch
- controllers
- events
- jwt
- form-data
Expand Down
18 changes: 10 additions & 8 deletions core/controllers.md → symfony/controllers.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Creating Custom Operations and Controllers
# Creating Custom Operations and Symfony Controllers

Note: using custom controllers with API Platform is **discouraged**. Also, GraphQL is **not supported**.
[For most use cases, better extension points, working both with REST and GraphQL, are available](design.md).
> [!NOTE]
> Using custom Symfony controllers with API Platform is **discouraged**. Also, GraphQL is **not supported**.
> [For most use cases, better extension points, working both with REST and GraphQL, are available](../core/design.md).
> We recommend to use [System providers and processors](../core/extending.md#system-providers-and-processors) to extend API Platform internals.

API Platform can leverage the Symfony routing system to register custom operations related to custom controllers. Such custom
controllers can be any valid [Symfony controller](https://symfony.com/doc/current/controller.html), including standard
Symfony controllers extending the [`Symfony\Bundle\FrameworkBundle\Controller\AbstractController`](http://api.symfony.com/4.1/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.html)
Symfony controllers extending the [`Symfony\Bundle\FrameworkBundle\Controller\AbstractController`](https://symfony.com/doc/current/controller.html#the-base-controller-class-services)
helper class.

To enable this feature use `use_symfony_listeners: true` in your `api_platform` configuration file:
Expand Down Expand Up @@ -39,7 +41,7 @@ If your resource has any identifier, this operation will look like `/books/{id}`
Those routes are not exposed from any documentation (for instance OpenAPI), but are anyway declared on the Symfony routing and always return a HTTP 404.

If you create a custom operation, you will probably want to properly document it.
See the [OpenAPI](openapi.md) part of the documentation to do so.
See the [OpenAPI](../core/openapi.md) part of the documentation to do so.

First, let's create your custom operation:

Expand Down Expand Up @@ -90,7 +92,7 @@ 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#built-in-event-listeners) of API Platform. It will be
In this case, the entity will pass through [all built-in event listeners](../core/events.md#built-in-event-listeners) 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.

Expand Down Expand Up @@ -165,7 +167,7 @@ Complex use cases may lead you to create multiple custom operations.

In such a case, you will probably create the same amount of custom controllers while you may not need to perform custom logic inside.

To avoid that, API Platform provides the `ApiPlatform\Action\PlaceholderAction` which behaves the same when using the [built-in operations](operations.md#operations).
To avoid that, API Platform provides the `ApiPlatform\Action\PlaceholderAction` which behaves the same when using the [built-in operations](../core/operations.md#operations).

You just need to set the `controller` attribute with this class. Here, the previous example updated:

Expand Down Expand Up @@ -370,7 +372,7 @@ resources:

</code-selector>

This way, it will skip the `ReadListener`. You can do the same for some other built-in listeners. See [Built-in Event Listeners](events.md#built-in-event-listeners)
This way, it will skip the `ReadListener`. You can do the same for some other built-in listeners. See [Built-in Event Listeners](../core/events.md#built-in-event-listeners)
for more information.

In your custom controller, the `__invoke()` method parameter should be called the same as the entity identifier.
Expand Down
2 changes: 1 addition & 1 deletion symfony/migrate-from-fosrestbundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Same as above.

**In API Platform**

Even though this is not recommended, API Platform allows you to [create custom controllers](../core/controllers.md) and declare them in your entity's `ApiResource` attribute.
Even though this is not recommended, API Platform allows you to [create custom controllers](controllers.md) and declare them in your entity's `ApiResource` attribute.

You can use them as you migrate from FOSRestBundle, but you should consider [switching to Symfony Messenger](../core/messenger.md) as it will give you more benefits, such as compatibility with both REST and GraphQL and better performances of your API on big tasks.

Expand Down
Loading