Symfony4 расширяет контроллер аннотацией маршрута - PullRequest
0 голосов
/ 07 мая 2018

Я создаю веб-приложение с 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){}
 }

Таким образом, каждый контроллер должен переопределить @Route, @ParamConverter и @Security, что крайне избыточно.

Я попытался создать LoggedController, который определяет каждую аннотацию, а затем сделать Controller продлевает это LoggedController, но это не работает.

Есть ли решение или я должен продолжать копировать / вставлять эти аннотации каждый раз, когда я создаю новый Controller, который должен его реализовать?

РЕДАКТИРОВАТЬ: Я добавляю объявление Company объекта:

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

1 Ответ

0 голосов
/ 08 мая 2018

Короче говоря, вы можете, но будет намного проще дублировать ваши аннотации на каждом контроллере.

Но если вы все равно не хотите этого делать, вот несколько решений.


Маршрутизация

Это самый простой. Вы можете определить глобальный префикс в файле 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}

Все ваши маршруты теперь начинаются с /pro/{uniqid}, и вы можете удалить аннотацию @Route с вашего контроллера.


ParamConverter

Вы можете создать свой собственный 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;
    }
}

С этим вы можете удалить аннотацию @ParamConverter с вашего контроллера.

Безопасность

Нельзя использовать раздел access_control файла security.yaml, поскольку пользовательские функции еще не поддерживаются.

В противном случае что-то вроде этого могло бы быть хорошим:

security:
    ...

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

(Примечание: это было исправлено в 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',
        );
    }
}
...