Przekazywanie danych do buildForm () w Symfony 2.8, 3.0 i nowszych

87

Moja aplikacja obecnie przekazuje dane do mojego typu formularza przy użyciu konstruktora, zgodnie z zaleceniami w tej odpowiedzi . Jednak przewodnik aktualizacji Symfony 2.8 radzi, że przekazywanie instancji typu do createFormfunkcji jest przestarzałe:

Przekazywanie instancji typu do metod Form :: add (), FormBuilder :: add () i FormFactory :: create * () jest przestarzałe i nie będzie już obsługiwane w Symfony 3.0. Zamiast tego przekaż w pełni kwalifikowaną nazwę klasy typu.

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

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

Widząc, że nie mogę przekazać danych przy użyciu w pełni kwalifikowanej nazwy klasy, czy istnieje alternatywa?

Jonathan
źródło
1
Jakie dane musisz przekazać? Czy to jest coś, co można wstrzyknąć?
Cerad
2
Miejmy nadzieję, że UPGRADE.md zostanie ulepszony: github.com/symfony/symfony/issues/18662
althaus

Odpowiedzi:

133

To również złamało niektóre z naszych form. Naprawiłem to, przekazując niestandardowe dane przez program rozpoznawania opcji.

W swoim formularzu wpisz:

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

Następnie, kiedy tworzysz formularz w swoim kontrolerze, przekaż go jako opcję zamiast w konstruktorze:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
źródło
8
Właśnie natknąłem się na ten problem i zrobiłem podobne rozwiązanie. Myślę, że jeśli dane są wymagane i jeśli chcesz zrobić rodzaj podpowiedzi typu, który zwykle robisz w definicji konstruktora, powinieneś użyć metod setRequired () i setAllowedTypes () dla opcji rozpoznawania opcji w konfiguracji configureOptions () , zamiast setDefaults ().
sarahg
2
Dokładnie to powinieneś zrobić. :)
Bernhard Schussek
3
@Roubi robisz to samo, definiujesz opcję w metodzie configureOptions, a następnie przekazujesz ją przy dodawaniu pola formularza.
Bart Wesselink,
2
Ja też nie jestem zadowolony z tej zmiany. Ale dzięki za odpowiedź.
Adambean
2
FormTypes działają jak fabryki, powinny być bezpaństwowcami. Wstrzyknięcie wartości przez ich konstruktor (inaczej niż za pomocą metody Service Tag) sprawia, że ​​jest on stanowy. W ten sposób masz jeden jednolity sposób tworzenia typu formularza. Opcje zawsze miały być używane zamiast argumentów konstruktora. Ta zmiana jest świetna dla DX i użyteczności.
Każdy z
6

Tutaj można zastosować inne podejście - wstrzyknąć usługę do pobierania danych.

  1. Opisz swój formularz jako usługę ( książka kucharska )
  2. Dodaj chronione pole i konstruktor do klasy formularza
  3. Użyj wstrzykniętego obiektu, aby uzyskać potrzebne dane

Przykład:

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
źródło
To jest dobre, ale nie zadziała, gdy argument nie jest dostępny dla menedżera usługi.
demonkoryu
5

W przypadku, gdy ktoś korzysta z funkcji „createNamedBuilder” lub „createNamed” z usługi form.factory, oto krótki opis sposobu ustawiania i zapisywania danych za ich pomocą. Nie możesz użyć pola „data” (pozostaw to puste) i musisz ustawić przekazane dane / encje jako $optionswartość.

Dodałem również instrukcje @sarahg dotyczące używania opcji setAllowedTypes () i setRequired () i wydaje się, że działa dobrze, ale najpierw musisz zdefiniować pole za pomocą setDefined ()

Również wewnątrz formularza, jeśli potrzebujesz ustawić dane, pamiętaj, aby dodać je do pola „dane”.

W kontrolerze używam getBlockPrefix, ponieważ getName zostało wycofane w wersji 2.8 / 3.0

Kontroler:

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

Formularz:

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
źródło
5

Oto jak przekazać dane do osadzonego formularza dla każdego, kto używa Symfony 3. Najpierw zrób dokładnie to, co @sekl opisał powyżej, a następnie wykonaj następujące czynności:

W swoim podstawowym FormType

Przekaż zmienną do osadzonego formularza za pomocą „ entry_options

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

W Twoim Embedded FormType

Dodaj opcję do optionsResolver

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

Uzyskaj dostęp do zmiennej w funkcji buildForm. Pamiętaj, aby ustawić tę zmienną przed funkcją budującą. W moim przypadku musiałem filtrować opcje na podstawie określonego identyfikatora.

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
źródło
Aby mieć mniej zamieszania - $ this-> zmienna to wartość, którą chcesz przekazać, niekoniecznie ze zmiennej klasy.
Darius.V