Passaggio dei dati a buildForm () in Symfony 2.8, 3.0 e versioni successive


87

La mia applicazione attualmente passa i dati al mio tipo di modulo utilizzando il costruttore, come consigliato in questa risposta . Tuttavia la guida all'aggiornamento di Symfony 2.8 avvisa che il passaggio di un'istanza di tipo alla createFormfunzione è deprecato:

Il passaggio di istanze di tipo a Form :: add (), FormBuilder :: add () e ai metodi FormFactory :: create * () è deprecato e non sarà più supportato in Symfony 3.0. Passa invece il nome completo della classe del tipo.

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

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

Visto che non riesco a trasmettere i dati con il nome completo della classe, esiste un'alternativa?


1
Che tipo di dati devi trasmettere? È qualcosa che può essere iniettato?
Cerad

2
Si spera che UPGRADE.md venga migliorato: github.com/symfony/symfony/issues/18662
althaus

Risposte:


133

Questo ha anche rotto alcuni dei nostri moduli. L'ho risolto passando i dati personalizzati attraverso il resolver delle opzioni.

Nel tuo tipo di modulo:

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,
    ]);
}

Quindi, quando crei il modulo nel tuo controller, passalo come opzione invece che nel costruttore:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);

8
Ho appena incontrato anche questo problema e ho fatto una soluzione simile. Penso che se i dati sono richiesti e se vuoi fare il tipo di suggerimento sul tipo che faresti normalmente nella definizione del costruttore, dovresti usare i metodi setRequired () e setAllowedTypes () per il risolutore di opzioni nel tuo configureOptions () , invece di setDefaults ().
sarahg

2
È esattamente quello che dovresti fare. :)
Bernhard Schussek

3
@Roubi fai la stessa cosa, definisci un'opzione nel metodo configureOptions e poi la passi quando aggiungi un campo modulo.
Bart Wesselink

2
Nemmeno io sono contento di questo cambiamento. Grazie per la risposta però.
Adambean

2
I FormTypes si comportano come fabbriche, dovrebbero essere apolidi. L'iniezione di valori tramite il loro costruttore (diverso dal metodo del tag di servizio) lo rende stateful. In questo modo hai 1 modo uniforme per creare il tuo tipo di modulo. Le opzioni sono sempre state pensate per essere utilizzate al posto degli argomenti del costruttore. Questa modifica è ottima per DX e usabilità.
Chiunque

6

Qui può essere utilizzato un altro approccio: inserire il servizio per recuperare i dati.

  1. Descrivi il tuo modulo come servizio ( ricettario )
  2. Aggiungi campo protetto e costruttore per formare la classe
  3. Usa l'oggetto iniettato per ottenere tutti i dati di cui hai bisogno

Esempio:

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'
        ]);
    }

}

Questo va bene, ma non funzionerà quando l'argomento non è disponibile per il gestore del servizio.
demonkoryu

5

Nel caso in cui qualcuno stia utilizzando una funzione 'createNamedBuilder' o 'createNamed' dal servizio form.factory, ecco lo snippet su come impostare e salvare i dati utilizzandolo. Non è possibile utilizzare il campo "dati" (lasciare il valore nullo) e si devono impostare i dati / entità passati come $optionsvalore.

Ho anche incorporato le istruzioni @sarahg sull'uso delle opzioni setAllowedTypes () e setRequired () e sembra funzionare bene ma devi prima definire il campo con setDefined ()

Anche all'interno del form se hai bisogno dei dati da impostare ricordati di aggiungerlo al campo 'dati'.

Nel controller sto usando getBlockPrefix poiché getName è stato deprecato in 2.8 / 3.0

Controller:

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

Modulo:

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',
            )
        )
...
}

5

Ecco come passare i dati a un form incorporato per chiunque usi Symfony 3. Prima fai esattamente ciò che @sekl ha descritto sopra e poi fai quanto segue:

Nel tuo FormType principale

Passa la var al modulo incorporato utilizzando " entry_options "

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

Nel tuo FormType incorporato

Aggiungi l'opzione a optionsResolver

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

Accedi alla variabile nella tua funzione buildForm. Ricordarsi di impostare questa variabile prima della funzione builder. Nel mio caso avevo bisogno di filtrare le opzioni in base a un ID specifico.

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

Per avere meno confusione - $ this-> var è il tuo valore che vuoi passare, non necessariamente dalla variabile di classe.
Darius.V
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.