2 votes

étendre EntityType, fixer les choix à l'entité sélectionnée uniquement

J'ai étendu la fonction EntityType comme UserChooserType à utiliser avec mon User et Select2. La liste de choix pour l'entité UserChooserType provient d'une requête ldap (via un appel ajax), pas d'une requête Doctrine. Le champ est donc vide au départ.

L'entité User est liée à de nombreuses entités différentes dans mon application. Mais si je veux que le type UserChooserType se charge avec un courant l'utilisateur sélectionné, je dois ajouter un listener à chaque formulaire qui l'utilise, par exemple :

class SiteType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $siteAdminOpts = array('label' => 'entity.site.admin', 'required'=>false);
       //opts for the UserChooserType

        $builder
            ->add('siteName', FT\TextType::class, array('label' => 'entity.site.name'))
            ->add('siteAdmin', UserChooserType::class, $siteAdminOpts )

           //must be added to every form type that uses UserChooserType with mod for the datatype that $event->getData() returns
            ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
                $site = $event->getData();
                $form = $event->getForm(); //SiteType

                if($user = $site->getSiteAdmin()) $siteAdminOpts['choices'] = array($user);
                $form->add('siteAdmin', UserChooserType::class, $siteAdminOpts);
            });
    }
//etc.

tldr ;

J'aimerais que ce soit l'un ou l'autre :

  • fixer UserChooserType 's choices à l'utilisateur sélectionné dans UserChooserType::configureOptions() ou
  • déplacer ->addEventListener(...) en UserChooserType::buildForm() .

Une idée sur la manière de procéder ?


Voici le type de choix de l'utilisateur (UserChooserType) :

class UserChooserType extends AbstractType
{
    /**
     * @var UserManager
     */
    protected $um;

    /**
     * UserChooserType constructor.
     * @param UserManager $um
     */
    public function __construct(UserManager $um){
        $this->um = $um; //used to find and decorate User entities. It is not a Doctrine entity manager, but it uses one.
    }

    /**
     * @inheritDoc
     */
    public function buildForm(FormBuilderInterface $builder, array $options) {

        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
            $data = $event->getData();

            if (!$data) return;

            $user = $this->um->getUserByUserName($data);
            if(!$user->getId()) $this->um->saveUser($user); //create User in db, if it's not there yet.
        });

        $builder->resetViewTransformers(); //so new choices aren't discarded

        $builder->addModelTransformer(new CallbackTransformer(
          function ($user) { //internal storage format to display format
            return ($user instanceof User) ? $user->getUserName() : '';
          },
          function ($username) { //display format to storage format
              return ($username) ? $this->um->getUserByUserName($username) : null;
          }
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'class' => 'ACRDUserBundle:User',
            'label' => 'ldap.user.name',
            'choice_label' => function($user, $key, $index){
                $this->um->decorateUser($user);
                $label = $user->getDetail('displayName');
                return $label ? $label : $user->getUserName();
            },
            'choice_value' => 'userName',
            'choices' => [],
            'attr' => array(
                'class' => 'userchooser',
                'placeholder' => 'form.placeholder.userchooser'
            )

        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'my_userchooser';
    }

    /**
     * @inheritDoc
     */
    public function getParent() {
        return EntityType::class;
    }
}

0voto

Zephyr Points 1219

Quelques remarques d'abord :

  • Le nom du champ UserChooserType est un peu plus élaborée. UserType est plus court et tout aussi clair, et correspond mieux aux conventions de nommage de Symfony
  • Je n'élargirais pas UserChooserType de EntityType . Doc dit EntityType est spécifiquement dédié aux entités issues de la Doctrine. Il ajoute de la magie pour que le champ soit facilement configuré autour de Doctrine : transformateurs, récupération automatique des choix dans la base de données, etc. Si vos utilisateurs sont entièrement définis dans le LDAP, je recommanderais plutôt d'étendre le champ ChoiceType et de remplir vos choix directement à partir du LDAP.

Pour en revenir à votre problème, il s'agit ici de tirer parti de la toute-puissance de l'injection de dépendances, en déclarant vos le type de champ en tant que service . Après avoir fait cela, au lieu de :

$builder->add('siteAdmin', UserChooserType::class, $siteAdminOpts)

Vous ferez (en supposant que vous ayez choisi le nom du formulaire) user_chooser_type ) :

$builder->add('siteAdmin', 'user_chooser_type', $siteAdminOpts)

Vous devez injecter le security.token_storage dans votre type de champ. Il s'agit du service qui contient les informations de l'utilisateur actuel, auquel vous pouvez accéder de la manière suivante :

$user = $securityTokenStorage->getToken()->getUser();

Ainsi, la population de la valeur par défaut peut se faire au niveau du type de champ et non de son parent.

Vous pouvez ainsi ajouter les choix LDAP au niveau du type de champ, en injectant un service capable de communiquer avec le LDAP.

Il convient enfin de noter que tous les types de champs par défaut de Symfony sont également déclarés comme des services.

EDIT :

La réponse précédente est hors sujet.

Dans Symfony, je ne connais pas de moyen simple de modifier la liste de choix d'un champ choix/entité après sa création. Je ne pense pas qu'il y en ait, et en fait j'ai peut-être fait la même chose que vous pour résoudre le problème que vous décrivez.

Je pense que vous faites déjà ce qu'il faut dans ce cas.

Si vous êtes vraiment motivé, je pense qu'il est possible de déplacer l'écouteur d'événements dans le type de formulaire comme ceci :

->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($options) {
    if ($form->hasParent()) {
        $data = $event->getData();
        $form = $event->getForm();

        $method = 'get' . ucfirst($form['action_name']);
        if($user = $data->$method()) $siteAdminOpts['choices'] = array($user);
        $form->getParent()->add($options['form_name'], UserChooserType::class, $siteAdminOpts);
    }
});

Quelque chose comme ça. Je ne suis pas sûr de savoir comment cela fonctionnerait.

Je suis toujours intéressé de voir si quelqu'un a trouvé une solution propre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X