Passer des données à buildForm () dans Symfony 2.8, 3.0 et supérieur

87

Mon application transmet actuellement des données à mon type de formulaire à l'aide du constructeur, comme recommandé dans cette réponse . Cependant, le guide de mise à niveau de Symfony 2.8 indique que le passage d'une instance de type à la createFormfonction est obsolète:

Passer des instances de type à Form :: add (), FormBuilder :: add () et les méthodes FormFactory :: create * () est obsolète et ne sera plus pris en charge dans Symfony 3.0. Passez plutôt le nom de classe complet du type.

Before:    
$form = $this->createForm(new MyType());

After:
$form = $this->createForm(MyType::class);

Étant donné que je ne peux pas transmettre de données avec le nom de classe complet, existe-t-il une alternative?

Jonathan
la source
1
Quel type de données avez-vous besoin de transmettre? Est-ce quelque chose qui peut être injecté?
Cerad
2
Espérons que UPGRADE.md soit amélioré: github.com/symfony/symfony/issues/18662
althaus

Réponses:

133

Cela a également brisé certaines de nos formes. Je l'ai corrigé en passant les données personnalisées via le résolveur d'options.

Dans votre type de formulaire:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $this->traitChoices = $options['trait_choices'];

    $builder
        ...
        ->add('figure_type', ChoiceType::class, [
            'choices' => $this->traitChoices,
        ])
        ...
    ;
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'trait_choices' => null,
    ]);
}

Ensuite, lorsque vous créez le formulaire dans votre contrôleur, passez-le en tant qu'option au lieu de dans le constructeur:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
la source
8
Je viens de rencontrer ce problème et j'ai trouvé une solution similaire. Je pense que si les données sont requises et si vous voulez faire le type d'indication de type que vous feriez habituellement dans la définition du constructeur, vous devriez utiliser les méthodes setRequired () et setAllowedTypes () pour le résolveur d'options dans votre configureOptions () , au lieu de setDefaults ().
sarahg
2
C'est exactement ce que vous devez faire. :)
Bernhard Schussek
3
@Roubi vous faites la même chose, vous définissez une option dans la méthode configureOptions puis la passez lors de l'ajout d'un champ de formulaire.
Bart Wesselink
2
Je ne suis pas non plus satisfait de ce changement. Merci pour la réponse.
Adambean
2
Les FormTypes agissent comme des usines, ils doivent être apatrides. L'injection de valeurs via leur constructeur (autrement que via la méthode de la balise de service) le rend avec état. De cette façon, vous avez 1 manière uniforme de créer votre type de formulaire. Les options étaient toujours destinées à être utilisées à la place des arguments du constructeur. Ce changement est excellent pour le DX et la convivialité.
N'importe qui
6

Ici, une autre approche peut être utilisée - un service d'injection pour récupérer des données.

  1. Décrivez votre formulaire en tant que service ( livre de recettes )
  2. Ajouter un champ protégé et un constructeur à la classe de formulaire
  3. Utilisez un objet injecté pour obtenir toutes les données dont vous avez besoin

Exemple:

services:
    app.any.manager:
        class: AppBundle\Service\AnyManager

    form.my.type:
        class: AppBundle\Form\MyType
        arguments: ["@app.any.manager"]
        tags: [ name: form.type ]

<?php

namespace AppBundle\Form;

use AppBundle\Service\AnyManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MyType extends AbstractType {

    /**
     * @var AnyManager
     */
    protected $manager;

    /**
     * MyType constructor.
     * @param AnyManager $manager
     */
    public function __construct(AnyManager $manager) {
        $this->manager = $manager;
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $choices = $this->manager->getSomeData();

        $builder
            ->add('type', ChoiceType::class, [
                'choices' => $choices
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\MyData'
        ]);
    }

}
Denis
la source
C'est bien, mais ne fonctionnera pas lorsque l'argument n'est pas disponible pour le gestionnaire de service.
demonkoryu
5

Si quelqu'un utilise les fonctions 'createNamedBuilder' ou 'createNamed' du service form.factory, voici l'extrait de code expliquant comment définir et enregistrer les données en l'utilisant. Vous ne pouvez pas utiliser le champ 'data' (laissez ce champ nul) et vous devez définir les données / entités transmises comme $optionsvaleur.

J'ai également incorporé des instructions @sarahg sur l'utilisation des options setAllowedTypes () et setRequired () et cela semble fonctionner correctement, mais vous devez d'abord définir le champ avec setDefined ()

Aussi à l'intérieur du formulaire si vous avez besoin que les données soient définies, n'oubliez pas de les ajouter au champ «données».

Dans Controller, j'utilise getBlockPrefix car getName était obsolète dans 2.8 / 3.0

Manette:

/*
* @var $builder Symfony\Component\Form\FormBuilderInterface
*/
$formTicket = $this->get('form.factory')->createNamed($tasksPerformedForm->getBlockPrefix(), TaskAddToTicket::class, null, array('ticket'=>$ticket) );

Forme:

public function configureOptions(OptionsResolver $resolver)    {
    $resolver->setDefined('ticket');
    $resolver->setRequired('ticket');
    $resolver->addAllowedTypes('ticket', Ticket::class);

    $resolver->setDefaults(array(           
        'translation_domain'=>'AcmeForm',
        'validation_groups'=>array('validation_group_001'),
        'tasks' => null,
        'ticket' => null,
    ));
}

 public function buildForm(FormBuilderInterface $builder, array $options)   {

    $this->setTicket($options['ticket']);
    //This is required to set data inside the form!
    $options['data']['ticket']=$options['ticket'];

    $builder

        ->add('ticket',  HiddenType::class, array(
                'data_class'=>'acme\TicketBundle\Entity\Ticket',
            )
        )
...
}
Ethernal
la source
5

Voici comment transmettre les données à un formulaire intégré pour toute personne utilisant Symfony 3. Commencez par faire exactement ce que @sekl a décrit ci-dessus, puis procédez comme suit:

Dans votre FormType principal

Passez le var au formulaire incorporé en utilisant ' entry_options '

->add('your_embedded_field', CollectionType::class, array(
          'entry_type' => YourEntityType::class,
          'entry_options' => array(
            'var' => $this->var
          )))

Dans votre type de formulaire intégré

Ajouter l'option aux optionsResolver

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Yourbundle\Entity\YourEntity',
        'var' => null
    ));
}

Accédez à la variable dans votre fonction buildForm. N'oubliez pas de définir cette variable avant la fonction de générateur. Dans mon cas, j'avais besoin de filtrer les options en fonction d'un identifiant spécifique.

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $this->var = $options['var'];

    $builder
        ->add('your_field', EntityType::class, array(
          'class' => 'YourBundle:YourClass',
          'query_builder' => function ($er) {
              return $er->createQueryBuilder('u')
                ->join('u.entity', 'up')
                ->where('up.id = :var')
                ->setParameter("var", $this->var);
           }))
     ;
}
mcriecken
la source
Pour avoir moins de confusion - $ this-> var est votre valeur que vous voulez passer, pas nécessairement de la variable de classe.
Darius.V