Symfony2 注释继承
Symfony2 Annotation Inheritance
在我的项目中,我有一个 BaseController class,它实现了我所有控制器使用的几种方法。
现在我想为该基础添加 @QueryParam class。我的 class 看起来像这样:
class DoctrineRESTQueryController extends FOSRestController
{
/**
* @QueryParam(name="start",default=0)
* @QueryParam(name="limit",default=null)
*/
public function getQueryResponseAction (ParamFetcher $paramFetcher) {
}
}
现在我有了从基本控制器延伸出来的实际控制器:
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");
}
}
不幸的是,Symfony2 似乎没有从 superclass 继承 @QueryParam 注释,因为调用 $paramFetcher->get("start") 导致异常 "No @QueryParam/@RequestParam configuration for parameter 'start'".
是否有任何方法可以使注释继承起作用,或者任何其他解决方案,这样我就不必将 @QueryParam 添加到我的每个控制器?
FosRestBundle
中没有此功能,因此您必须根据需要重写部分内容,更具体地说 class FOSRestBundle/Request/ParamReader
中的方法 getParamsFromMethod
(参见 source code here)。
这可以通过包继承来完成。
首先,subclass FOSRestBundle\Request\ParamReader
在您的一个捆绑包中,例如YourSite\RestBundle\Request\MyParamReader
并通过加载父方法的注释并将它们与当前方法的注释合并来覆盖 getParamsFromMethod
:
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);
}
}
如有必要,您可以修改代码以处理深层继承层次结构。
现在你应该告诉 FOSRestBundle
使用你的 YourSite\RestBundle\Request\MyParamReader
class 而不是 FOSRestBundle\Request\ParamReader
。您需要覆盖服务定义,其中参数 reader 被列为依赖项。这是 bundle overriding/inheritance 发挥作用的地方,参见 this Symfony2 article.
服务定义位于Resources/config/request.xml
文件中(参见source code here),FOSRestBundle\Request\ParamReader
是FOS\RestBundle\Request\ParamFetcher
的依赖项。
因此您必须覆盖 Resources/config/request.xml
文件。为此,按照上面的文章,注册您的包并将 FOSRestBundle
声明为其父项:
namespace YourSite\RestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class YourSiteRestBundle extends Bundle
{
public function getParent()
{
return 'FOSRestBundle';
}
}
创建文件 YourSite\RestBundle\Resources\config\request.xml
以将 YourSite\RestBundle\Request\MyParamReader
添加为依赖项:
<?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>
这未经测试,但应该可以。
很好的解决方案,谢谢!也许有人在 symfony 3 中寻找相同的 .yaml 配置,以下应该有效:
parameters:
fos_rest.request.param_fetcher.class: FOS\RestBundle\Request\ParamFetcher
your_site_rest.request.param_fetcher.reader.class: YourSiteBundle\Request\MyParamReader
services:
fos_rest.request.param_fetcher:
class: %fos_rest.request.param_fetcher.class%
arguments: ['@service_container', '@your_site.request.param_fetcher.reader', '@request_stack', '@?validator']
scope: request
your_site.request.param_fetcher.reader:
class: %your_site.request.param_fetcher.reader.class%
arguments: ['@annotation_reader']
在我的项目中,我有一个 BaseController class,它实现了我所有控制器使用的几种方法。
现在我想为该基础添加 @QueryParam class。我的 class 看起来像这样:
class DoctrineRESTQueryController extends FOSRestController
{
/**
* @QueryParam(name="start",default=0)
* @QueryParam(name="limit",default=null)
*/
public function getQueryResponseAction (ParamFetcher $paramFetcher) {
}
}
现在我有了从基本控制器延伸出来的实际控制器:
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");
}
}
不幸的是,Symfony2 似乎没有从 superclass 继承 @QueryParam 注释,因为调用 $paramFetcher->get("start") 导致异常 "No @QueryParam/@RequestParam configuration for parameter 'start'".
是否有任何方法可以使注释继承起作用,或者任何其他解决方案,这样我就不必将 @QueryParam 添加到我的每个控制器?
FosRestBundle
中没有此功能,因此您必须根据需要重写部分内容,更具体地说 class FOSRestBundle/Request/ParamReader
中的方法 getParamsFromMethod
(参见 source code here)。
这可以通过包继承来完成。
首先,subclass FOSRestBundle\Request\ParamReader
在您的一个捆绑包中,例如YourSite\RestBundle\Request\MyParamReader
并通过加载父方法的注释并将它们与当前方法的注释合并来覆盖 getParamsFromMethod
:
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);
}
}
如有必要,您可以修改代码以处理深层继承层次结构。
现在你应该告诉 FOSRestBundle
使用你的 YourSite\RestBundle\Request\MyParamReader
class 而不是 FOSRestBundle\Request\ParamReader
。您需要覆盖服务定义,其中参数 reader 被列为依赖项。这是 bundle overriding/inheritance 发挥作用的地方,参见 this Symfony2 article.
服务定义位于Resources/config/request.xml
文件中(参见source code here),FOSRestBundle\Request\ParamReader
是FOS\RestBundle\Request\ParamFetcher
的依赖项。
因此您必须覆盖 Resources/config/request.xml
文件。为此,按照上面的文章,注册您的包并将 FOSRestBundle
声明为其父项:
namespace YourSite\RestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class YourSiteRestBundle extends Bundle
{
public function getParent()
{
return 'FOSRestBundle';
}
}
创建文件 YourSite\RestBundle\Resources\config\request.xml
以将 YourSite\RestBundle\Request\MyParamReader
添加为依赖项:
<?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>
这未经测试,但应该可以。
很好的解决方案,谢谢!也许有人在 symfony 3 中寻找相同的 .yaml 配置,以下应该有效:
parameters:
fos_rest.request.param_fetcher.class: FOS\RestBundle\Request\ParamFetcher
your_site_rest.request.param_fetcher.reader.class: YourSiteBundle\Request\MyParamReader
services:
fos_rest.request.param_fetcher:
class: %fos_rest.request.param_fetcher.class%
arguments: ['@service_container', '@your_site.request.param_fetcher.reader', '@request_stack', '@?validator']
scope: request
your_site.request.param_fetcher.reader:
class: %your_site.request.param_fetcher.reader.class%
arguments: ['@annotation_reader']