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;
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é <julien.fastre@champs-libres.coop>
*/
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);
}
}

View File

@ -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();
}
}
}

View File

@ -41,9 +41,8 @@ use Chill\MainBundle\Entity\RoleScope;
*
* Provides methods for user and entities information.
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
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();

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:
- { 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:
class: Chill\MainBundle\Validation\Validator\RoleScopeScopePresence
arguments:

View File

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

View File

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