Skip to content

Commit

Permalink
docs(controllers): move from core to symfony and update (#2093)
Browse files Browse the repository at this point in the history
  • Loading branch information
vinceAmstoutz authored Dec 9, 2024
1 parent 78d3384 commit d0e780e
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 17 deletions.
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

0 comments on commit d0e780e

Please sign in to comment.