In my project, I have a BaseController class which implements several methods used by all of my controllers.
Now I wanted to add @QueryParam for that base class. My class looks like this:
class DoctrineRESTQueryController extends FOSRestController
{
/**
* @QueryParam(name="start",default=0)
* @QueryParam(name="limit",default=null)
*/
public function getQueryResponseAction (ParamFetcher $paramFetcher) {
}
}
Now I have my actual controller which extends from my base controller:
class DefaultController extends DoctrineRESTQueryController {
/**
* Retrieves all SI Prefixes in the database
*
* @Routing\Route("/siprefix", defaults={"method" = "get","_format" = "json"})
* @Routing\Method({"GET"})
* @ApiDoc(output="array<PartKeepr\SiPrefixBundle\Entity\SiPrefix>")
*
* @View()
*
* {@inheritdoc}
*/
public function getQueryResponseAction(ParamFetcher $paramFetcher) {
$paramFetcher->get("start");
}
}
Unfortunately, Symfony2 doesn't seem to inherit the @QueryParam annotations from the superclass, because the call to $paramFetcher->get("start") results in an exception "No @QueryParam/@RequestParam configuration for parameter 'start'".
Is there any way to make annotation inheritance work, or any other solution so I don't have to add @QueryParam to each of my controllers?
There is no feature in FosRestBundle
for this, so you must override parts of it for what you need, more specifically the method getParamsFromMethod
in class FOSRestBundle/Request/ParamReader
(see source code here).
This can be done through bundle inheritance.
First, subclass FOSRestBundle\Request\ParamReader
in one of your bundles, e.g. YourSite\RestBundle\Request\MyParamReader
and override getParamsFromMethod
by loading the annotations of the parent method and merging them with those of the current one:
namespace YourSite\RestBundle\Request\MyParamReader;
use FOSRestBundle\Request\ParamReader;
class MyParamReader extends ParamReader
{
public function getParamsFromMethod(\ReflectionMethod $method)
{
$parentParams = array();
$params = parent::getParamsFromMethod($method);
// This loads the annotations of the parent method
$declaringClass = $method->getDeclaringClass();
$parentClass = $declaringClass->getParentClass();
if ($parentClass && $parentClass->hasMethod($method->getShortName())) {
$parentMethod = $parentClass->getMethod($method->getShortName());
$parentParams = parent::getParamsFromMethod($parentMethod);
}
return array_merge($params, $parentParams);
}
}
You can modify the code to handle deep inheritance hierarchies, if necessary.
Now you should tell FOSRestBundle
to use your YourSite\RestBundle\Request\MyParamReader
class instead of FOSRestBundle\Request\ParamReader
. You need to override the service definition, where the parameter reader is listed as a dependency. This is where bundle overriding/inheritance comes into play, see this Symfony2 article.
The service definition is located in the Resources/config/request.xml
file (see source code here), FOSRestBundle\Request\ParamReader
is a dependency of FOS\RestBundle\Request\ParamFetcher
.
So you must override the Resources/config/request.xml
file. To do this, following the above article, register your bundle and declare FOSRestBundle
as its parent:
namespace YourSite\RestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class YourSiteRestBundle extends Bundle
{
public function getParent()
{
return 'FOSRestBundle';
}
}
Create file YourSite\RestBundle\Resources\config\request.xml
to add YourSite\RestBundle\Request\MyParamReader
as a dependency:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_rest.request.param_fetcher.class">FOS\RestBundle\Request\ParamFetcher</parameter>
<parameter key="your_site_rest.request.param_fetcher.reader.class">YourSite\RestBundle\Request\MyParamReader</parameter>
</parameters>
<services>
<service id="fos_rest.request.param_fetcher" class="%fos_rest.request.param_fetcher.class%" scope="request">
<argument type="service" id="your_site.request.param_fetcher.reader"/>
<argument type="service" id="request"/>
<argument type="service" id="fos_rest.violation_formatter"/>
<argument type="service" id="validator" on-invalid="null"/>
</service>
<service id="your_site.request.param_fetcher.reader" class="%your_site_rest.request.param_fetcher.reader.class%">
<argument type="service" id="annotation_reader"/>
</service>
</services>
</container>
This is untested, but it should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With