refactor centerType

This commit is contained in:
Julien Fastré 2021-10-05 17:02:57 +02:00
parent 05b9476a71
commit a8edef13a3
7 changed files with 174 additions and 80 deletions

View File

@ -19,7 +19,11 @@
namespace Chill\MainBundle\Form\Type; 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\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 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 Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer;
use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; 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é <julien.fastre@champs-libres.coop>
*/ */
class CenterType extends AbstractType class CenterType extends AbstractType
{ {
/** protected AuthorizationHelperInterface $authorizationHelper;
* The user linked with this type.
*
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
/** protected Security $security;
* associative array where keys are center.id and
* value are center objects
*
* @var Center[]
*/
protected $reachableCenters = array();
/** protected CenterRepository $centerRepository;
*
* @var CenterTransformer
*/
protected $transformer;
public function __construct(TokenStorageInterface $tokenStorage, public function __construct(
CenterTransformer $transformer) AuthorizationHelperInterface $authorizationHelper,
{ Security $security,
$this->user = $tokenStorage->getToken()->getUser(); CenterRepository $centerRepository
$this->transformer = $transformer; ) {
$this->prepareReachableCenterByUser(); $this->authorizationHelper = $authorizationHelper;
$this->security = $security;
$this->centerRepository = $centerRepository;
} }
/** /**
@ -72,6 +67,7 @@ class CenterType extends AbstractType
* @return string * @return string
* @throws \RuntimeException if the user is not associated with any center * @throws \RuntimeException if the user is not associated with any center
*/ */
/*
public function getParent() public function getParent()
{ {
$nbReachableCenters = count($this->reachableCenters); $nbReachableCenters = count($this->reachableCenters);
@ -82,6 +78,7 @@ class CenterType extends AbstractType
return EntityType::class; return EntityType::class;
} }
} }
*/
/** /**
* configure default options, i.e. add choices if user can reach multiple * 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) public function configureOptions(OptionsResolver $resolver)
{ {
if (count($this->reachableCenters) > 1) { $resolver
$resolver->setDefault('class', Center::class) ->setDefault('class', Center::class)
->setDefault('choices', $this->reachableCenters) ->setRequired('role')
->setDefault('placeholder', 'Pick a center') ->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) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
if ($this->getParent() === HiddenType::class) { $centers = $this->getReachableCenters($options['role'], $options['scopes']);
$builder->addModelTransformer($this->transformer); 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'];
}
));
} }
/** private function getReachableCenters(string $role, iterable $scopes): array
* populate reachableCenters as an associative array where
* keys are center.id and value are center entities.
*
*/
private function prepareReachableCenterByUser()
{ {
$groupCenters = $this->user->getGroupCenters(); if (0 < count($scopes)) {
$centers = [];
foreach ($groupCenters as $groupCenter) { foreach($scopes as $scope) {
foreach ($this->authorizationHelper
$center = $groupCenter->getCenter(); ->getReachableCenters($this->security->getUser(), $role, $scope) as $center) {
$centers[spl_object_hash($center)] = $center;
if (!array_key_exists($center->getId(), }
$this->reachableCenters)) {
$this->reachableCenters[$center->getId()] = $center;
} }
return \array_values($centers);
} else {
return $this->authorizationHelper
->getReachableCenters($this->security->getUser(), $role);
} }
} }

View File

@ -20,36 +20,57 @@
namespace Chill\MainBundle\Form\Type\DataTransformer; namespace Chill\MainBundle\Form\Type\DataTransformer;
use Chill\MainBundle\Entity\Center; 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 Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
class CenterTransformer implements DataTransformerInterface class CenterTransformer implements DataTransformerInterface
{ {
private EntityManagerInterface $em; private CenterRepository $centerRepository;
private bool $multiple = false;
public function __construct(EntityManagerInterface $em) public function __construct(
{ CenterRepository $centerRepository,
$this->em = $em; bool $multiple = false
) {
$this->centerRepository = $centerRepository;
$this->multiple = $multiple;
} }
public function reverseTransform($id) public function reverseTransform($id)
{ {
if ($id === NULL) { if ($id === NULL) {
return NULL; if ($this->multiple) {
return new ArrayCollection();
} else {
return NULL;
}
} }
$center = $this if ($this->multiple) {
->em $ids = \explode(',', $id);
->getRepository(Center::class) } else {
->find($id); $ids[] = (int) $id;
}
if ($center === NULL) { $centers = $this
->centerRepository
->findBy(['id' => $ids ]);
if ([] === $centers || count($ids) > count($centers)) {
throw new TransformationFailedException(sprintf( 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) public function transform($center)
@ -58,7 +79,21 @@ class CenterTransformer implements DataTransformerInterface
return ''; 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();
}
}
} }

View File

@ -41,9 +41,8 @@ use Chill\MainBundle\Entity\RoleScope;
* *
* Provides methods for user and entities information. * Provides methods for user and entities information.
* *
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class AuthorizationHelper class AuthorizationHelper implements AuthorizationHelperInterface
{ {
protected RoleHierarchyInterface $roleHierarchy; protected RoleHierarchyInterface $roleHierarchy;
@ -203,9 +202,9 @@ class AuthorizationHelper
* @param User $user * @param User $user
* @param string|Role $role * @param string|Role $role
* @param null|Scope $scope * @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) { if ($role instanceof Role) {
$role = $role->getRole(); $role = $role->getRole();
@ -267,9 +266,9 @@ class AuthorizationHelper
* @param User $user * @param User $user
* @param string role * @param string role
* @param Center|Center[] $center * @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) { if ($role instanceof Role) {
$role = $role->getRole(); $role = $role->getRole();

View File

@ -0,0 +1,32 @@
<?php
namespace Chill\MainBundle\Security\Authorization;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Security\Core\Role\Role;
interface AuthorizationHelperInterface
{
/**
* Get reachable Centers for the given user, role,
* and optionnaly Scope
*
* @param User $user
* @param string|Role $role
* @param null|Scope $scope
* @return Center[]
*/
public function getReachableCenters(User $user, string $role, ?Scope $scope = null): array;
/**
* @param User $user
* @param string $role
* @param Center|Center[]|array $center
* @return array
*/
public function getReachableScopes(User $user, string $role, $center): array;
}

View File

@ -60,11 +60,6 @@ services:
tags: tags:
- { name: twig.extension } - { name: twig.extension }
chill.main.form.data_transformer.center_transformer:
class: Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer
arguments:
- "@doctrine.orm.entity_manager"
chill.main.validator.role_scope_scope_presence: chill.main.validator.role_scope_scope_presence:
class: Chill\MainBundle\Validation\Validator\RoleScopeScopePresence class: Chill\MainBundle\Validation\Validator\RoleScopeScopePresence
arguments: arguments:

View File

@ -36,13 +36,9 @@ services:
tags: tags:
- { name: form.type, alias: select2_chill_language } - { name: form.type, alias: select2_chill_language }
chill.main.form.type.center: Chill\MainBundle\Form\Type\CenterType:
class: Chill\MainBundle\Form\Type\CenterType autowire: true
arguments: autoconfigure: true
- "@security.token_storage"
- "@chill.main.form.data_transformer.center_transformer"
tags:
- { name: form.type, alias: center }
chill.main.form.type.composed_role_scope: chill.main.form.type.composed_role_scope:
class: Chill\MainBundle\Form\Type\ComposedRoleScopeType class: Chill\MainBundle\Form\Type\ComposedRoleScopeType
@ -97,6 +93,10 @@ services:
arguments: arguments:
- '@Chill\MainBundle\Export\ExportManager' - '@Chill\MainBundle\Export\ExportManager'
Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer:
autowire: true
autoconfigure: true
chill.main.form.advanced_search_type: chill.main.form.advanced_search_type:
class: Chill\MainBundle\Form\AdvancedSearchType class: Chill\MainBundle\Form\AdvancedSearchType
autowire: true autowire: true

View File

@ -38,6 +38,7 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper' Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper'
Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface: '@chill.main.security.authorization.helper'
chill.main.role_provider: chill.main.role_provider:
class: Chill\MainBundle\Security\RoleProvider class: Chill\MainBundle\Security\RoleProvider