Using PHP8 attributes with Symfony Value Resolvers
Version 8 of PHP was released just over 2 months ago, and among the most important changes there is the introduction of attributes, which will allow you to create with native syntax what has been achieved up to now with PHPDoc and Doctrine annotations.
The Symfony framework makes extensive use of them, for example to specify routing directly in controllers and validation rules in entities. Version 5.2 already supports PHP8 attributes in both cases.
Symfony Value Resolvers are a mechanism that allows, for example, to obtain as arguments of an action:
- services registered in the application container
- session object or logged in user
- request or a single request attribute
exploiting the metadata of the arguments themselves, including the type and name, but also the PHP8 attribute, if specified.
You can implement your own Value Resolver writing a class that implements the ArgumentValueResolverInterface interface and, if necessary, declaring it as a service to specify a priority, since multiple Value Resolvers can support the resolution of the same argument, and in that case the order of evaluation matters.
The ArgumentValueResolverInterface interface consists of two methods, supports and resolve, the first checks if a Value Resolver is able to handle an argument, the second performs the resolution. Both methods receive the HTTP request and an object containing the argument metadata.
Translate the request into a DTO
If we are writing a REST service where requests and responses are encoded in JSON we might want our request to be automatically translated into a DTO. We can achieve this by defining our own Value Resolver, using for example the Symfony serializer component to translate the request body.
But it does not always include all the information needed to process the request, for example the URI could contain the id of the resource to be modified.
Value Resolvers and PHP8 attributes
If variable names in the routing rule do not always coincide with the property names of our DTOs, we can define a suitable PHP8 attribute, for example:
and use it within our controller, in the arguments of an action:
while the implementation of the resolve method of our Value Resolver takes advantage of the fact that among the metadata of the argument it is also possible to retrieve the PHP8 attribute, if defined:
A slightly more complex use case is the one that involves the translation of our data into a doctrine object. Using reflections it is possible to know the type of the object in the DTO and use the corresponding repository to obtain the object by making a call to the findOneBy method, always using the information present in the invoked URI.