Передача данных в buildForm () в Symfony 2.8, 3.0 и выше

87

В настоящее время мое приложение передает данные в мой тип формы с помощью конструктора, как рекомендовано в этом ответе . Однако руководство по обновлению Symfony 2.8 сообщает, что передача экземпляра типа createFormфункции устарела:

Передача экземпляров типов в методы Form :: add (), FormBuilder :: add () и FormFactory :: create * () устарела и больше не будет поддерживаться в Symfony 3.0. Вместо этого передайте полное имя класса типа.

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

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

Поскольку я не могу передавать данные с полным именем класса, есть ли альтернатива?

Джонатан
источник
1
Какие данные вам нужно передать? Это что-то, что можно вводить?
Cerad 01
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 ().
Сараг
2
Это именно то, что вам следует делать. :)
Бернхард Шуссек
3
@Roubi, вы делаете то же самое, вы определяете параметр в методе configureOptions, а затем передаете его при добавлении поля формы.
Барт Весселинк,
2
Я тоже не доволен этим изменением. Спасибо за ответ.
Adambean
2
FormTypes действуют как фабрики, они не должны иметь состояния. Внедрение значений через их конструктор (кроме метода служебного тега) делает его с сохранением состояния. Таким образом, у вас есть 1 единый способ создания вашего типа формы. Параметры всегда предназначались для использования вместо аргументов конструктора. Это изменение отлично подходит для DX и удобства использования.
Anyone
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 01
5

Если кто-то использует функции createNamedBuilder или createNamed из службы form.factory, вот фрагмент о том, как установить и сохранить данные с его помощью. Вы не можете использовать поле 'data' (оставьте это значение null), и вы должны установить переданные данные / объекты как $optionsзначение.

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

Также внутри формы, если вам нужно установить данные, не забудьте добавить их в поле «данные».

В контроллере я использую 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

Пропустите вар к встраиваемой форме с использованием « 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);
           }))
     ;
}
Mcriecken
источник
Чтобы было меньше путаницы - $ this-> var - это ваше значение, которое вы хотите передать, не обязательно из переменной класса.
Darius.V 08