From a8edef13a3182321bb290f5c052c2ff18b177cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 5 Oct 2021 17:02:57 +0200 Subject: [PATCH] refactor centerType --- .../ChillMainBundle/Form/Type/CenterType.php | 128 +++++++++++------- .../DataTransformer/CenterTransformer.php | 63 +++++++-- .../Authorization/AuthorizationHelper.php | 11 +- .../AuthorizationHelperInterface.php | 32 +++++ .../ChillMainBundle/config/services.yaml | 5 - .../ChillMainBundle/config/services/form.yaml | 14 +- .../config/services/security.yaml | 1 + 7 files changed, 174 insertions(+), 80 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php diff --git a/src/Bundle/ChillMainBundle/Form/Type/CenterType.php b/src/Bundle/ChillMainBundle/Form/Type/CenterType.php index 14ac63e8c..ce2978ebf 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/CenterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/CenterType.php @@ -19,7 +19,11 @@ namespace Chill\MainBundle\Form\Type; +use Chill\MainBundle\Repository\CenterRepository; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -27,41 +31,32 @@ use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\Security\Core\Security; /** + * Pick a center + * + * For a given role and, eventually, scopes, show a dropdown (if more than + * one reachable center) or a HiddenType (if one or zero center). * * - * @author Julien Fastré */ class CenterType extends AbstractType { - /** - * The user linked with this type. - * - * @var \Chill\MainBundle\Entity\User - */ - protected $user; + protected AuthorizationHelperInterface $authorizationHelper; - /** - * associative array where keys are center.id and - * value are center objects - * - * @var Center[] - */ - protected $reachableCenters = array(); + protected Security $security; - /** - * - * @var CenterTransformer - */ - protected $transformer; + protected CenterRepository $centerRepository; - public function __construct(TokenStorageInterface $tokenStorage, - CenterTransformer $transformer) - { - $this->user = $tokenStorage->getToken()->getUser(); - $this->transformer = $transformer; - $this->prepareReachableCenterByUser(); + public function __construct( + AuthorizationHelperInterface $authorizationHelper, + Security $security, + CenterRepository $centerRepository + ) { + $this->authorizationHelper = $authorizationHelper; + $this->security = $security; + $this->centerRepository = $centerRepository; } /** @@ -72,6 +67,7 @@ class CenterType extends AbstractType * @return string * @throws \RuntimeException if the user is not associated with any center */ + /* public function getParent() { $nbReachableCenters = count($this->reachableCenters); @@ -82,6 +78,7 @@ class CenterType extends AbstractType return EntityType::class; } } + */ /** * configure default options, i.e. add choices if user can reach multiple @@ -91,13 +88,19 @@ class CenterType extends AbstractType */ public function configureOptions(OptionsResolver $resolver) { - if (count($this->reachableCenters) > 1) { - $resolver->setDefault('class', Center::class) - ->setDefault('choices', $this->reachableCenters) - ->setDefault('placeholder', 'Pick a center') - ; - } - + $resolver + ->setDefault('class', Center::class) + ->setRequired('role') + ->setAllowedTypes('role', [ 'string' ]) + ->setDefault('scopes', []) + ->setAllowedTypes('scopes', ['iterable']) + ->setDefault('choice_options', []) + ; + /* + ->setDefault('choices', $this->reachableCenters) + ->setDefault('placeholder', 'Pick a center') + ; + */ } /** @@ -108,28 +111,57 @@ class CenterType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { - if ($this->getParent() === HiddenType::class) { - $builder->addModelTransformer($this->transformer); + $centers = $this->getReachableCenters($options['role'], $options['scopes']); + dump($centers); + if (count($centers) <= 1) { + dump($centers); + $multiple = $options['choice_options']['multiple'] ?? false; + $builder->add('center', HiddenType::class); + $builder->get('center')->addModelTransformer( + new CenterTransformer($this->centerRepository, $multiple) + ); + } else { + $builder->add('center', EntityType::class, + \array_merge( + $options['choice_options'], + [ + 'class' => Center::class, + 'choices' => $centers + ] + ) + ); } + + $builder + ->addModelTransformer(new CallbackTransformer( + function($data) { + if (NULL === $data) { + return ['center' => null]; + } + return ['center' => $data]; + }, + function($data) { + return $data['center']; + } + )); } - /** - * populate reachableCenters as an associative array where - * keys are center.id and value are center entities. - * - */ - private function prepareReachableCenterByUser() + private function getReachableCenters(string $role, iterable $scopes): array { - $groupCenters = $this->user->getGroupCenters(); + if (0 < count($scopes)) { + $centers = []; - foreach ($groupCenters as $groupCenter) { - - $center = $groupCenter->getCenter(); - - if (!array_key_exists($center->getId(), - $this->reachableCenters)) { - $this->reachableCenters[$center->getId()] = $center; + foreach($scopes as $scope) { + foreach ($this->authorizationHelper + ->getReachableCenters($this->security->getUser(), $role, $scope) as $center) { + $centers[spl_object_hash($center)] = $center; + } } + + return \array_values($centers); + } else { + return $this->authorizationHelper + ->getReachableCenters($this->security->getUser(), $role); } } diff --git a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/CenterTransformer.php b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/CenterTransformer.php index b90ab5a46..88bc75e3a 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/CenterTransformer.php +++ b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/CenterTransformer.php @@ -20,36 +20,57 @@ namespace Chill\MainBundle\Form\Type\DataTransformer; use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Repository\CenterRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; class CenterTransformer implements DataTransformerInterface { - private EntityManagerInterface $em; + private CenterRepository $centerRepository; + private bool $multiple = false; - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + CenterRepository $centerRepository, + bool $multiple = false + ) { + $this->centerRepository = $centerRepository; + $this->multiple = $multiple; } public function reverseTransform($id) { if ($id === NULL) { - return NULL; + if ($this->multiple) { + return new ArrayCollection(); + } else { + return NULL; + } } - $center = $this - ->em - ->getRepository(Center::class) - ->find($id); + if ($this->multiple) { + $ids = \explode(',', $id); + } else { + $ids[] = (int) $id; + } - if ($center === NULL) { + $centers = $this + ->centerRepository + ->findBy(['id' => $ids ]); + + if ([] === $centers || count($ids) > count($centers)) { throw new TransformationFailedException(sprintf( - 'No center found with id %d', $id)); + 'No center found for one of those ids: %s', implode(',', $ids))); } - return $center; + if ($this->multiple) { + return new ArrayCollect($centers); + } else { + return $centers[0]; + } } public function transform($center) @@ -58,7 +79,21 @@ class CenterTransformer implements DataTransformerInterface return ''; } - return $center->getId(); - } + if ($this->multiple) { + if (!is_iterable($center)) { + throw new UnexpectedTypeException($center, \Traversable::class); + } + $ids = []; + foreach ($center as $c) { + $ids[] = $c->getId(); + } + return implode(',', $ids); + } else { + if (!$center instanceof Center) { + throw new UnexpectedTypeException($center, Center::class); + } + return (string) $center->getId(); + } + } } diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php index 2b223a0fe..afff931ed 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php @@ -41,9 +41,8 @@ use Chill\MainBundle\Entity\RoleScope; * * Provides methods for user and entities information. * - * @author Julien Fastré */ -class AuthorizationHelper +class AuthorizationHelper implements AuthorizationHelperInterface { protected RoleHierarchyInterface $roleHierarchy; @@ -203,9 +202,9 @@ class AuthorizationHelper * @param User $user * @param string|Role $role * @param null|Scope $scope - * @return Center[] + * @return Center[]|array */ - public function getReachableCenters(User $user, $role, Scope $scope = null) + public function getReachableCenters(User $user, string $role, ?Scope $scope = null): array { if ($role instanceof Role) { $role = $role->getRole(); @@ -267,9 +266,9 @@ class AuthorizationHelper * @param User $user * @param string role * @param Center|Center[] $center - * @return Scope[] + * @return Scope[]|array */ - public function getReachableScopes(User $user, $role, $center) + public function getReachableScopes(User $user, string $role, $center): array { if ($role instanceof Role) { $role = $role->getRole(); diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php new file mode 100644 index 000000000..31709bcd5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php @@ -0,0 +1,32 @@ +