Search code examples
symfonyfosrestbundle

How to extend FOSRestBundle RequestBodyParamConverter?


I am new to Symfony (5.3) and would like to extend the RequestBodyParamConverter (FOSRestBundle 3.0.5) to create a REST api. Using @ParamConverter annotation with the RequestBodyParamConverter works fine. However, I would like to create a custom converter, which does the exact same job as RequestBodyParamConverter plus a little extra work.

My first guess was to simply extend RequestBodyParamConverter and provide my custom subclass in the @ParamConverter annotation. However, RequestBodyParamConverter is defined as final and thus cannot be extended...

Injecting RequestBodyParamConverter / fos_rest.request_body_converter into a custom converter class (see example below) also fails because the service cannot be found. I assume this is because it is defined a private?

So, my last idea was to create a RequestBodyParamConverter inside my custom converter class. While this works, I am not sure if this is the right way to solve this problem. This way RequestBodyParamConverter is created twice. This is nothing special of course, but is this the Symfony way to solve this or are there other solutions?

Example:

Inject RequestBodyParamConverter in custom converter class

class MyParamConverter implements ParamConverterInterface {
    protected $parentConverter;
    public function __construct(ParamConverterInterface $parentConverter) {
        $this->parentConverter = $parentConverter;
    }

    public function apply(Request $request, ParamConverter $configuration): bool {
        doExtraWork();
        return $this->parentConverter->apply(...);
    }
}

// config/services.yaml
My\Project\MyParamConverter:
    tags:
        - { name: request.param_converter, converter: my_converter.request_body }
    arguments:
        # both fails since service is not found
        $parentConverter: '@FOS\RestBundle\Request\RequestBodyParamConverter'

        # OR

        $parentConverter: '@fos_rest.request_body_converter'

Create RequestBodyParamConverter in custom converter class

class MyParamConverter implements ParamConverterInterface {
    protected $parentConverter;
    public function __construct(...parameters necessary to create converter...) {
        $this->parentConverter = new RequestBodyParamConverter(...);
    }

    ...
}

Solution

  • Symfony provide a way to decorate a registered service

    To use it you need the FOS service id registered in the container.

    To get it you can use this command

    symfony console debug:container --tag=request.param_converter
    

    Retrieve the Service ID of the service you want to override.

    Then you can configure your service to decorate FOS one

    My\Project\MyParamConverter:
        decorates: 'TheIdOf_FOS_ParamConverterService'
        arguments: [ '@My\Project\MyParamConverter.inner' ] # <-- this is the instance of fos service
    

    Maybe you'll need to add the tags to this declaration, I'm not sure.

    Let me know if you're facing an error.