Drupal 8 parameters upcasting for REST resources

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 and may already know how to implement a custom parameters-upcasting for Routes & Controllers.

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

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.

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

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

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.

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

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


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

Drupal API Doc (16 April 2019). Parameter upcasting in routes.
Retrieved from drupal.org/docs/8/api

Drupal API Doc (17 February 2020). Using parameters in routes.
Retrieved from drupal.org/docs/8/api

Drupal API Doc (29 October 2018). Custom REST Resources.
Retrieved from drupal.org/docs/8/api

Drupal API Doc (16 April 2019). Implementing custom parameter converters.
Retrieved from drupal.org/docs/8/api

Drupal Core Issue Queue (29 Apr 2020). Make it possible to link to an entity by UUID.
Retrieved from drupal.org/project/drupal/issues

Drupal Core Issue Queue (16 April 2019). Upcast request arguments/attributes to full objects.
Retrieved from drupal.org/project/drupal/issues