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;
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',
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.