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

Bor*_*éec 0 php annotations symfony symfony4

我正在构建一个 web 应用程序,Symfony因为现在我不得不为我构建的每个新控制器重复一个特定的模式。

例如我有这个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){}
 }
Run Code Online (Sandbox Code Playgroud)

因此,每个控制器必须重新定义@Route@ParamConverter@Security这是非常多余。

我试图创建一个LoggedController定义每个注释的,然后 makeController扩展那个LoggedController,但这不起作用。

有没有解决方案,或者我应该在每次创建一个Controller需要实现它的新注解时继续复制/粘贴这些注解?

编辑:我添加Company实体声明:

/**
 * @ORM\Entity(repositoryClass="App\Repository\CompanyRepository")
 */
 class Company
 {
   /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;
Run Code Online (Sandbox Code Playgroud)

fxb*_*xbt 5

长话短说,你可以,但在每个控制器中复制你的注释会容易得多。

但是,如果您无论如何都不想这样做,这里有一些解决方案。


路由

这是最简单的。您可以在config/routes/annotations.yaml文件中。

如果您使用的是默认配置,则可以尝试以下操作:

# Default controllers
controllers:
    resource: ../../src/Controller/
    type: annotation

# Company controllers
company_controllers:
    resource: ../../src/Controller/Company/
    type: annotation
    prefix: /pro/{uniqid}
Run Code Online (Sandbox Code Playgroud)

您的所有路线现在都将开始,/pro/{uniqid}您可以@Route从控制器中删除注释。


参数转换器

您可以创建自己的ParamConverter。每次Company在操作方法中使用类型时,都会使用uniqid属性将其转换为匹配的实体。

像这样的东西:

// 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个,你可以删除 @ParamConverter从控制器中注释。

安全

你不能使用 access_control 部分 security.yaml文件因为尚不支持自定义函数。

否则,这样的事情可能会很好:

security:
    ...

    access_control:
        -
            path: ^/pro
            allow_if: "is_granted(constant('App\\Security\\Voter\\CompanyVoter::VIEW'), company)"
Run Code Online (Sandbox Code Playgroud)

(注意:它已在Symfony 4.1修复但我还不知道它会如何工作)。

相反,您可以使用订阅者监听kernel.request内核事件:

<?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',
        );
    }
}
Run Code Online (Sandbox Code Playgroud)