Passando dados para buildForm () no Symfony 2.8, 3.0 e superior

87

Meu aplicativo atualmente passa dados para meu tipo de formulário usando o construtor, conforme recomendado nesta resposta . No entanto, o guia de atualização do Symfony 2.8 informa que a passagem de uma instância de tipo para a createFormfunção está obsoleta:

Passar instâncias de tipo para Form :: add (), FormBuilder :: add () e os métodos FormFactory :: create * () está obsoleto e não será mais suportado no Symfony 3.0. Passe o nome de classe totalmente qualificado do tipo.

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

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

Visto que não consigo transmitir dados com o nome de classe totalmente qualificado, existe uma alternativa?

Jonathan
fonte
1
Que tipo de dados você precisa passar? É algo que pode ser injetado?
Cerad 01 de
2
Espero que o UPGRADE.md seja melhorado: github.com/symfony/symfony/issues/18662
althaus

Respostas:

133

Isso quebrou alguns de nossos formulários também. Eu consertei passando os dados personalizados pelo resolvedor de opções.

No seu formulário, digite:

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

Então, quando você criar o formulário em seu controlador, passe-o como uma opção em vez de no construtor:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
fonte
8
Também me deparei com esse problema e fiz uma solução semelhante. Acho que se os dados são necessários e se você deseja fazer o tipo de sugestão de tipo que normalmente faria na definição do construtor, você deve usar os métodos setRequired () e setAllowedTypes () para o resolvedor de opções em seu configureOptions () , em vez de setDefaults ().
sarahg
2
Isso é exatamente o que você deve fazer. :)
Bernhard Schussek
3
@Roubi você faz a mesma coisa, você define uma opção no método configureOptions e depois passa ao adicionar um campo do formulário.
Bart Wesselink
2
Eu também não estou feliz com essa mudança. Obrigado pela resposta embora.
Adambean de
2
Os FormTypes agem como fábricas e devem ser sem estado. Injetar valores por meio de seu construtor (diferente do método da tag de serviço) torna-o com estado. Desta forma, você tem uma maneira uniforme de criar seu tipo de formulário. As opções sempre foram feitas para serem usadas no lugar dos argumentos do construtor. Essa mudança é ótima para DX e usabilidade.
Qualquer pessoa de
6

Aqui pode ser usada outra abordagem - injetar serviço para recuperar dados.

  1. Descreva seu formulário como serviço ( livro de receitas )
  2. Adicionar campo protegido e construtor para formar classe
  3. Use o objeto injetado para obter todos os dados de que precisar

Exemplo:

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
fonte
Isso é bom, mas não funcionará quando o argumento não estiver disponível para o gerente de serviço.
demonkoryu 01 de
5

No caso de alguém estar usando as funções 'createNamedBuilder' ou 'createNamed' do serviço form.factory, aqui está o trecho sobre como definir e salvar os dados usando-o. Você não pode usar o campo 'dados' (deixe nulo) e você deve definir os dados / entidades passados ​​como $optionsvalor.

Eu também incorporei instruções @sarahg sobre como usar as opções setAllowedTypes () e setRequired () e parece funcionar bem, mas primeiro você precisa definir o campo com setDefined ()

Também dentro do formulário, se você precisar que os dados sejam configurados, lembre-se de adicioná-los ao campo 'dados'.

No controlador, estou usando getBlockPrefix, pois getName foi preterido em 2.8 / 3.0

Controlador:

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

Formato:

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

Veja como passar os dados para um formulário embutido para qualquer pessoa usando Symfony 3. Primeiro faça exatamente o que @sekl delineado acima e depois faça o seguinte:

Em seu FormType principal

Passe a var para o formulário incorporado usando ' entry_options '

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

Em seu FormType incorporado

Adicione a opção ao optionsResolver

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

Acesse a variável em sua função buildForm. Lembre-se de definir esta variável antes da função do construtor. No meu caso, precisei filtrar opções com base em um ID específico.

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
fonte
Para ter menos confusão - $ this-> var é o valor que você deseja passar, não necessariamente da variável de classe.
Darius.V