Übergeben von Daten an buildForm () in Symfony 2.8, 3.0 und höher

86

Meine Anwendung übergibt derzeit Daten mithilfe des Konstruktors an meinen Formulartyp, wie in dieser Antwort empfohlen . Im Symfony 2.8-Upgrade-Handbuch wird jedoch empfohlen, eine Typinstanz an die createFormFunktion zu übergeben:

Das Übergeben von Typinstanzen an Form :: add (), FormBuilder :: add () und die FormFactory :: create * () -Methoden ist veraltet und wird in Symfony 3.0 nicht mehr unterstützt. Übergeben Sie stattdessen den vollständig qualifizierten Klassennamen des Typs.

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

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

Gibt es eine Alternative, da ich keine Daten mit dem vollqualifizierten Klassennamen weitergeben kann?

Jonathan
quelle
1
Welche Art von Daten müssen Sie übergeben? Kann es injiziert werden?
Cerad
2
Hoffentlich wird die UPGRADE.md verbessert: github.com/symfony/symfony/issues/18662
althaus

Antworten:

132

Dies hat auch einige unserer Formen gebrochen. Ich habe das Problem behoben, indem ich die benutzerdefinierten Daten über den Options-Resolver geleitet habe.

Geben Sie in Ihrem Formular Folgendes ein:

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

Wenn Sie das Formular dann in Ihrem Controller erstellen, übergeben Sie es als Option anstatt im Konstruktor:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
quelle
8
Ich bin gerade auch auf dieses Problem gestoßen und habe eine ähnliche Lösung gefunden. Ich denke, wenn die Daten erforderlich sind und Sie die Art von Typ-Hinweisen ausführen möchten, die Sie normalerweise in der Konstruktordefinition tun würden, sollten Sie die Methoden setRequired () und setAllowedTypes () für den Optionsauflöser in Ihrer configureOptions () verwenden. anstelle von setDefaults ().
Sarahg
2
Genau das sollten Sie tun. :)
Bernhard Schussek
3
@Roubi Wenn Sie dasselbe tun, definieren Sie eine Option in der configureOptions-Methode und übergeben sie beim Hinzufügen eines Formularfelds.
Bart Wesselink
2
Ich bin auch mit dieser Änderung nicht zufrieden. Danke für die Antwort.
Adambean
2
FormTypes verhalten sich wie Fabriken, sie sollten zustandslos sein. Durch das Injizieren von Werten über ihren Konstruktor (außer über die Service-Tag-Methode) wird der Status aktiviert. Auf diese Weise haben Sie 1 einheitliche Möglichkeit, Ihren Formulartyp zu erstellen. Optionen sollten immer anstelle von Konstruktorargumenten verwendet werden. Diese Änderung ist ideal für DX und Benutzerfreundlichkeit.
Jeder
6

Hier kann ein anderer Ansatz verwendet werden - Inject Service zum Abrufen von Daten.

  1. Beschreiben Sie Ihr Formular als Service ( Kochbuch )
  2. Fügen Sie ein geschütztes Feld und einen Konstruktor hinzu, um eine Klasse zu bilden
  3. Verwenden Sie injiziertes Objekt, um alle benötigten Daten abzurufen

Beispiel:

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
quelle
Dies ist gut, funktioniert aber nicht, wenn das Argument dem Service Manager nicht zur Verfügung steht.
Demonkoryu
5

Für den Fall, dass jemand die Funktionen 'createNamedBuilder' oder 'createNamed' aus dem Dienst form.factory verwendet, finden Sie hier einen Ausschnitt zum Festlegen und Speichern der Daten. Sie können das Feld 'Daten' nicht verwenden (lassen Sie diese Null) und müssen die übergebenen Daten / Entitäten als $optionsWert festlegen .

Ich habe auch @ sarahg-Anweisungen zur Verwendung der Optionen setAllowedTypes () und setRequired () eingefügt, und es scheint gut zu funktionieren, aber Sie müssen zuerst das Feld mit setDefined () definieren.

Wenn Sie die Daten festlegen möchten, müssen Sie sie auch innerhalb des Formulars zum Feld "Daten" hinzufügen.

In Controller verwende ich getBlockPrefix, da getName in 2.8 / 3.0 veraltet war

Regler:

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

Bilden:

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
quelle
5

So übergeben Sie die Daten an ein eingebettetes Formular für alle Benutzer von Symfony 3. Führen Sie zuerst genau das aus, was @sekl oben beschrieben hat, und führen Sie dann die folgenden Schritte aus:

In Ihrem primären FormType

Übergeben Sie die Variable mit ' entry_options ' an das eingebettete Formular.

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

In Ihrem eingebetteten FormType

Fügen Sie die Option dem optionsResolver hinzu

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

Greifen Sie auf die Variable in Ihrer buildForm-Funktion zu. Denken Sie daran, diese Variable vor der Builder-Funktion festzulegen. In meinem Fall musste ich Optionen basierend auf einer bestimmten ID filtern.

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
quelle
Um weniger Verwirrung zu stiften - $ this-> var ist Ihr Wert, den Sie übergeben möchten, nicht unbedingt von der Klassenvariablen.
Darius.V