Передача даних до buildForm () у Symfony 2.8, 3.0 і вище


87

Наразі моя програма передає дані моєму типу форми за допомогою конструктора, як рекомендовано у цій відповіді . Однак посібник з оновлення Symfony 2.8 радить, що передача екземпляра типу до createFormфункції застаріла:

Передача екземплярів типу до форм :: add (), FormBuilder :: add () та методів FormFactory :: create * () застаріла і більше не підтримуватиметься в Symfony 3.0. Натомість передайте повну назву класу типу.

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

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

Зважаючи на те, що я не можу передавати дані з повноцінною назвою класу, чи існує альтернатива?


1
Який тип даних вам потрібно передати? Це щось, що можна вводити?
Церад

2
Сподіваємось, UPGRADE.md покращиться: github.com/symfony/symfony/issues/18662
althaus

Відповіді:


133

Це також порушило деякі наші форми. Я виправив це, передавши власні дані через засіб розгадування параметрів.

У вашій формі введіть:

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

Потім, коли ви створюєте форму у своєму контролері, передайте її як варіант замість конструктора:

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

8
Просто натрапив і на цю проблему і зробив подібне рішення. Я думаю, що якщо потрібні дані, і якщо ви хочете зробити такий натяк на тип, який ви зазвичай робите у визначенні конструктора, вам слід використовувати методи setRequired () та setAllowedTypes () для розв'язувача параметрів у вашому configureOptions () , замість setDefaults ().
sarahg

2
Це саме те, що ви повинні робити. :)
Бернхард Шуссек

3
@Roubi ви робите те саме, ви визначаєте параметр у методі configureOptions, а потім передаєте його при додаванні поля форми.
Барт Весселінк

2
Я також не задоволений цією зміною. Дякую за відповідь.
Адамбін

2
Типи форм діють як заводи, вони не повинні мати громадянства. Вприскування значень через їх конструктор (крім методу тегу служби) робить його статусом. Таким чином у вас є 1 єдиний спосіб створення вашого типу форми. Параметри завжди мали на меті використовуватись замість аргументів конструктора. Ця зміна чудово підходить для DX та зручності використання.
Будь-хто

6

Тут може бути використаний інший підхід - служба ін'єкції для отримання даних.

  1. Опишіть свою форму як послугу ( кулінарна книга )
  2. Додайте захищене поле та конструктор до класу форми
  3. Використовуйте ін’єктований об’єкт, щоб отримати будь-які потрібні дані

Приклад:

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

}

Це добре, але не буде працювати, коли аргумент недоступний менеджеру служби.
demonkoryu

5

Якщо хтось використовує функції 'createNamedBuilder' або 'createNamed' із служби form.factory, ось фрагмент, як встановити та зберегти дані, використовуючи його. Ви не можете використовувати поле "дані" (залиште це значення нулем), і вам потрібно встановити передані дані / сутності як $optionsзначення.

Я також включив інструкції @sarahg щодо використання параметрів setAllowedTypes () та setRequired (), і це, здається, працює нормально, але спочатку потрібно визначити поле за допомогою setDefined ()

Також всередині форми, якщо вам потрібно встановити дані, не забудьте додати їх у поле "дані".

У Controller я використовую getBlockPrefix, оскільки getName застаріла у версії 2.8 / 3.0

Контролер:

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

Форма:

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

Ось як передавати дані у вбудовану форму для всіх, хто використовує Symfony 3. Спочатку виконайте саме те, що було описано вище @sekl, а потім виконайте наступне:

У вашому первинному FormType

Передайте var у вбудовану форму, використовуючи ' entry_options '

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

У вашому вбудованому FormType

Додайте опцію до optionsResolver

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

Доступ до змінної у вашій функції buildForm. Не забудьте встановити цю змінну перед функцією конструктора. У моєму випадку мені потрібно було фільтрувати параметри на основі конкретного ідентифікатора.

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

Щоб менше плутанини - $ this-> var - це ваше значення, яке ви хочете передати, не обов'язково із змінної класу.
Дарій. V
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.