duanchi19820419 2018-05-07 18:36
浏览 133
已采纳

Symfony4使用路径注释扩展控制器

I'm building a webapp with Symfony and since now I had to repeat a specific pattern for each new controller I built.

For example I have this AdminController :

/**
 * @Route("/pro/{uniqid}")
 * @ParamConverter("company", options={"mapping":{"uniqid" = "uniqid"}})
 * @Security("is_granted(constant('App\\Security\\Voter\\CompanyVoter::VIEW'), company)")
 * @package App\Controller
 */
 class AdminController extends Controller
 {
    /**
     * @Route("/admin/users/", name="users")
     * @return \Symfony\Component\HttpFoundation\Response
     */
     public function users(Company $company){}
 }

So, each controller must redefine @Route, @ParamConverter and @Security that is extremely redundant.

I tried to create a LoggedController that define every annotation, then make Controller extends that LoggedController, but that does not work.

Is there a solution or should I continue to copy/paste these Annotation each time I create a new Controller that needs to implement it ?

EDIT : I add the declaration of Company entity :

/**
 * @ORM\Entity(repositoryClass="App\Repository\CompanyRepository")
 */
 class Company
 {
   /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;
  • 写回答

1条回答 默认 最新

  • doupuchao4256 2018-05-08 13:20
    关注

    Long story short, you can but it will be a lot easier to duplicate your annotations in every controller.

    But if you wan't to do this anyway, here are some solutions.


    Routing

    This is the easy one. You can define a global prefix in the config/routes/annotations.yaml file.

    If you're using the default config, you can try something like this:

    # Default controllers
    controllers:
        resource: ../../src/Controller/
        type: annotation
    
    # Company controllers
    company_controllers:
        resource: ../../src/Controller/Company/
        type: annotation
        prefix: /pro/{uniqid}
    

    All your routes will now start with /pro/{uniqid} and you can remove the @Route annotation from your controller.


    ParamConverter

    You can create your own ParamConverter. Everytime you'll use a Company type in an action method, it'll be converted to the matching entity using the uniqid attribute.

    Something like this:

    // src/ParamConverter/CompanyConverter.php
    <?php
    
    namespace App\ParamConverter;
    
    use App\Entity\Company;
    use Doctrine\ORM\EntityManagerInterface;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
    use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
    use Symfony\Component\HttpFoundation\Request;
    
    class CompanyConverter implements ParamConverterInterface
    {
        const CONVERTER_ATTRIBUTE = 'uniqid';
    
        /**
         * @var EntityManagerInterface
         */
        private $entityManager;
    
        /**
         * CompanyConverter constructor.
         *
         * @param EntityManagerInterface $entityManager
         */
        public function __construct(EntityManagerInterface $entityManager)
        {
            $this->entityManager = $entityManager;
        }
    
        /**
         * @inheritdoc
         */
        public function apply(Request $request, ParamConverter $configuration)
        {
            $uniqid = $request->attributes->get(self::CONVERTER_ATTRIBUTE);
    
            $company = $this->entityManager->getRepository(Company::class)->findOneBy(['uniqid' => $uniqid]);
    
            $request->attributes->set($configuration->getName(), $company);
        }
    
        /**
         * @inheritdoc
         */
        function supports(ParamConverter $configuration)
        {
            return $configuration->getClass() === Company::class;
        }
    }
    

    With this, you can remove the @ParamConverter annotation from your controller.

    Security

    You can't use the access_control section of the security.yaml file since custom functions are not yet supported.

    Otherwise, something like this could have been nice:

    security:
        ...
    
        access_control:
            -
                path: ^/pro
                allow_if: "is_granted(constant('App\\Security\\Voter\\CompanyVoter::VIEW'), company)"
    

    (Note: It was fixed in Symfony 4.1 but i don't know yet how it will work).

    Instead, you can use a subscriber listening on the kernel.request kernel event:

    <?php
    
    namespace App\Subscriber;
    
    use App\Entity\Company;
    use App\Security\CompanyVoter;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
    use Symfony\Component\HttpKernel\KernelEvents;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
    
    class SecurityListener implements EventSubscriberInterface
    {
        /**
         * @var AuthorizationCheckerInterface
         */
        private $authorizationChecker;
    
        /**
         * @var EntityManagerInterface
         */
        private $entityManager;
    
        /**
         * @param AuthorizationCheckerInterface $authorizationChecker
         * @param EntityManagerInterface $entityManagerInterface
         */
        public function __construct(AuthorizationCheckerInterface $authorizationChecker, EntityManagerInterface $entityManager)
        {
            $this->authorizationChecker = $authorizationChecker;
            $this->entityManager = $entityManager;
        }
    
        /**
         * @param GetResponseEvent $event
         */
        public function onKernelRequest(GetResponseEvent $event)
        {
            $request = $event->getRequest();
    
            if (!$uniqid = $request->attributes->get('uniqid')) {
                return;
            }
    
            $company = $this->entityManager->getRepository(Company::class)->findOneBy(['titre' => $uniqid]);
    
            if (!$this->authorizationChecker->isGranted(CompanyVoter::VIEW, $company)) {
                throw new AccessDeniedHttpException();
            }
        }
    
        /**
         * @return array
         */
        public static function getSubscribedEvents()
        {
            return array(
                KernelEvents::REQUEST => 'onKernelRequest',
            );
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 linux驱动,linux应用,多线程
  • ¥20 我要一个分身加定位两个功能的安卓app
  • ¥15 基于FOC驱动器,如何实现卡丁车下坡无阻力的遛坡的效果
  • ¥15 IAR程序莫名变量多重定义
  • ¥15 (标签-UDP|关键词-client)
  • ¥15 关于库卡officelite无法与虚拟机通讯的问题
  • ¥15 目标检测项目无法读取视频
  • ¥15 GEO datasets中基因芯片数据仅仅提供了normalized signal如何进行差异分析
  • ¥100 求采集电商背景音乐的方法
  • ¥15 数学建模竞赛求指导帮助