Drupal 8 parameters upcasting for REST resources
Tags: php, drupal, rest, api
Published: 2020-06-24 13:51:20

What is parameters-upcasting? How does it work for REST resources? How can you implement it? 

By reading those lines, I presume readers already know how to [create a custom REST resources](https://www.drupal.org/docs/8/api/restful-web-services-api/custom-rest-resources) and may already know how to implement a custom [parameters-upcasting for Routes & Controllers](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/parameter-upcasting-in-routes).

This article will give you an **in-depth** how-to of **upcasting** for Drupal 8 REST resources. 

We will **briefly** explore the theory and when parameters-upcasting should be used.

> Any fool can write code that a computer can understand. Good programmers write code that humans can understand.<br> - Martin Fowler

During my exploration of Drupal 8 and REST resources, I discover many articles explaining the parameters-upcasting paradigm for the Routes/Controllers system but none for the REST resource.
Let's fix this, shall we?

## What is upcasting?



Sometimes your routes may contain one or more named parameters (also called a "slug" or sometimes a "path parameter").
 Those variables will then be available in the Controller method by their name (here `$node`).
 
{% gist https://gist.github.com/WengerK/b257f00d784362bd143e739aded144d2 file=example.routing.yml %}

> In most PHP code, the name of the variable does not matter but here it does: the name of the method argument must match the slug. Conversely, if the method argument name matches the name of the slug, the parameter will be passed in, irrespective of the order of arguments.

Then it would be awesome to have a sub-system capable of converting those parameters into an actual object according to our need into our Controller(s) or REST Resources.

{% gist https://gist.github.com/WengerK/b257f00d784362bd143e739aded144d2 file=foobarController.php %}

This feature already exists into Drupal 8's route framework. It's called **Parameters Upcasting** (may also be called **parameters converters**). 

 The `ParamConverter` system takes care of converting those parameters to an object instance automatically.

## Why should I implement parameter(s) upcasting ?

By using Parameters upcasting, you will keep logic out of the controllers/resources, that's what we call **decoupling**.
A powerful tool we have for making change easier and code more maintainable is decoupling. When we say two pieces of code are “decoupled”, we mean a change in one usually doesn't require a change in the other. When you change some feature, the fewer places in code you have to touch, the easier it is.

> In programming the hard part isn’t solving problems, but deciding what problems to solve.<br> - Paul Graham

Parameter upcasting may also improve the readability and robustness of you code by encouraging Type hinting of Controller/Resources methods.

Finally, by decoupling logic, you will be able to let Drupal check and validate the provided entity for you in one single place.

Well, Drupal 8 lands with many built-in parameter converters:

  - `Drupal\Core\ParamConver\EntityConverter` : Parameter converter for upcasting entity IDs to full objects.
  - `Drupal\Core\ParamConver\EntityRevisionParamConverter` : Parameter converter for upcasting entity revision IDs to full objects.
  - `Drupal\Core\ParamConver\MenuLinkPluginConverter`: Parameter converter for upcasting menu entity ids to full objects.
  - `Drupal\ctools\ParamConverter\TempstoreConverter`: Parameter converter for pulling entities out of the tempstore.
  This is particularly useful when building non-wizard forms (like dialogs) that operate on data in the wizard and 
  getting the route access properly.
  - `Drupal\jsonapi\ParamConverter\EntityUuidConverter`: Parameter converter for upcasting entity UUIDs to full objects.
  Be aware JSON:API maintains no PHP API since its API is the HTTP API. This class may change at any time and this will break any dependencies on it.

## How can I implement upcasting for REST resources ?

> There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.<br> — C.A.R. Hoare

First, you will need to create a custom `ParameterConverter`.

Parameter converters are being managed by the `ParamConverterManager`. To implement new parameter converters, you should implement the `ParamConverterInterface` interface.

{% gist https://gist.github.com/WengerK/b257f00d784362bd143e739aded144d2 file=FooBarConverter.php %}

As Parameter converters are services, you will need to add an entry to your `example.services.yml` to declare a new `ParamConverter`.

{% gist https://gist.github.com/WengerK/b257f00d784362bd143e739aded144d2 file=example.services.yml %}

In contrast to standard routes, REST Resources uses Plugin annotations to specify the route URL. Therefore, if your custom `ParamConverter` requires specific route option(s), you will not be able to [configure your parameters upcasting as usual](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes).

Indeed, you will need to create and override the `FooBarConverter::getBaseRoute()` method and alter the route(s) with those specific option(s).

{% gist https://gist.github.com/WengerK/b257f00d784362bd143e739aded144d2 file=DemoResource.php %}

In this example, my named parameter is called `foobar_placeholder` and the first argument on the callback should be named the same as the slug name (`foobar_placeholder`).

<hr>
## Sources

For the most curious of you, here are some sources of additional information that inspired the creation of this article.

**Drupal API Doc** (16 April 2019). How upcasting parameters works.
Retrieved from [drupal.org/docs/8/api](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/how-upcasting-parameters-works)

**Drupal API Doc** (16 April 2019). Parameter upcasting in routes.
Retrieved from [drupal.org/docs/8/api](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/parameter-upcasting-in-routes)

**Drupal API Doc** (17 February 2020). Using parameters in routes.
Retrieved from [drupal.org/docs/8/api](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes)

**Drupal API Doc** (29 October 2018). Custom REST Resources.
Retrieved from [drupal.org/docs/8/api](https://www.drupal.org/docs/8/api/restful-web-services-api/custom-rest-resources)

**Drupal API Doc** (16 April 2019). Implementing custom parameter converters.
Retrieved from [drupal.org/docs/8/api](https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/implementing-custom-parameter-converters)

**Drupal Core Issue Queue** (29 Apr 2020). Make it possible to link to an entity by UUID.
Retrieved from [drupal.org/project/drupal/issues](https://www.drupal.org/node/2353611)

**Drupal Core Issue Queue** (16 April 2019). Upcast request arguments/attributes to full objects.
Retrieved from [drupal.org/project/drupal/issues](https://www.drupal.org/project/drupal/issues/1798214)