【问题标题】:Symfony2 Annotation InheritanceSymfony2注解继承
【发布时间】:2015-08-06 04:54:56
【问题描述】:

在我的项目中,我有一个 BaseController 类,它实现了我所有控制器使用的几种方法。

现在我想为该基类添加@QueryParam。我的班级是这样的:

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 似乎没有从超类继承 @QueryParam 注释,因为对 $paramFetcher->get("start") 的调用会导致异常“No @QueryParam/@RequestParam configuration for parameter 'start '"。

有什么方法可以使注解继承工作,或者任何其他解决方案,这样我就不必将@QueryParam 添加到我的每个控制器?

【问题讨论】:

    标签: php symfony annotations


    【解决方案1】:

    很好的解决方案,谢谢!也许有人在 symfony3 中寻找相同的 .yml 配置,以下应该可行:

    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']
    

    【讨论】:

      【解决方案2】:

      FosRestBundle 中没有为此提供的功能,因此您必须根据需要覆盖其中的部分功能,更具体地说是 FOSRestBundle/Request/ParamReader 类中的方法 getParamsFromMethod(请参阅 source code here)。

      这可以通过包继承来完成。

      首先,在你的一个包中子类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 类而不是FOSRestBundle\Request\ParamReader。您需要覆盖服务定义,其中参数读取器被列为依赖项。这就是捆绑包覆盖/继承发挥作用的地方,请参阅this Symfony2 article

      服务定义位于Resources/config/request.xml文件中(见source code here),FOSRestBundle\Request\ParamReaderFOS\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>
      

      这是未经测试的,但它应该可以工作。

      【讨论】:

      • 你太棒了!一个小错误修复:自定义阅读器应该检查父方法是否确实存在: if ($parentClass && $parentClass->hasMethod($method->getShortName())) { $parentMethod = $parentClass->getMethod($method-> getShortName()); $parentParams = parent::getParamsFromMethod($parentMethod); } 返回 array_merge($params, $parentParams); }
      猜你喜欢
      • 2023-03-17
      • 2011-11-02
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多