Pasando datos a buildForm () en Symfony 2.8, 3.0 y superior

87

Mi aplicación actualmente pasa datos a mi tipo de formulario usando el constructor, como se recomienda en esta respuesta . Sin embargo, la guía de actualización de Symfony 2.8 advierte que pasar una instancia de tipo a la createFormfunción está en desuso:

Pasar instancias de tipo a Form :: add (), FormBuilder :: add () y los métodos FormFactory :: create * () está obsoleto y ya no será compatible con Symfony 3.0. En su lugar, pase el nombre de clase completamente calificado del tipo.

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

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

Dado que no puedo pasar datos con el nombre de clase completamente calificado, ¿hay alguna alternativa?

Jonathan
fuente
1
¿Qué tipo de datos necesitas pasar? ¿Es algo que se pueda inyectar?
Cerad
2
Con suerte, UPGRADE.md se mejora: github.com/symfony/symfony/issues/18662
althaus

Respuestas:

133

Esto también rompió algunas de nuestras formas. Lo arreglé pasando los datos personalizados a través del solucionador de opciones.

En su formulario escriba:

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

Luego, cuando cree el formulario en su controlador, páselo como una opción en lugar de en el constructor:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
fuente
8
También encontré este problema e hice una solución similar. Creo que si se requieren los datos y si desea hacer el tipo de sugerencia de tipo que normalmente haría en la definición del constructor, debe usar los métodos setRequired () y setAllowedTypes () para el solucionador de opciones en su configureOptions () , en lugar de setDefaults ().
sarahg
2
Eso es exactamente lo que debes hacer. :)
Bernhard Schussek
3
@Roubi haces lo mismo, defines una opción en el método configureOptions y luego la pasas al agregar un campo de formulario.
Bart Wesselink
2
Tampoco estoy contento con este cambio. Aunque gracias por la respuesta.
Adambean
2
Los FormTypes actúan como fábricas, deberían ser apátridas. Inyectar valores a través de su constructor (que no sea a través del método de etiqueta de servicio) lo hace con estado. De esta manera, tiene 1 forma uniforme de crear su tipo de formulario. Las opciones siempre estaban destinadas a usarse en lugar de los argumentos del constructor. Este cambio es excelente para DX y usabilidad.
Cualquiera
6

Aquí se puede utilizar otro enfoque: servicio de inyección para recuperar datos.

  1. Describe tu formulario como servicio ( libro de recetas )
  2. Agregar campo protegido y constructor a la clase de formulario
  3. Utilice el objeto inyectado para obtener los datos que necesite

Ejemplo:

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
fuente
Esto es bueno, pero no funcionará cuando el argumento no esté disponible para el administrador de servicios.
demonkoryu
5

En caso de que alguien esté usando las funciones 'createNamedBuilder' o 'createNamed' del servicio form.factory, aquí está el fragmento de cómo configurar y guardar los datos usándolo. No puede usar el campo 'datos' (déjelo nulo) y debe establecer los datos / entidades pasados ​​como $optionsvalor.

También incorporé instrucciones de @sarahg sobre el uso de las opciones setAllowedTypes () y setRequired () y parece funcionar bien, pero primero debes definir el campo con setDefined ()

También dentro del formulario, si necesita que se establezcan los datos, recuerde agregarlos al campo 'datos'.

En Controller estoy usando getBlockPrefix ya que getName quedó obsoleto en 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) );

Formar:

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

Aquí se explica cómo pasar los datos a un formulario incrustado para cualquier persona que use Symfony 3. Primero haga exactamente lo que @sekl describió anteriormente y luego haga lo siguiente:

En su FormType principal

Pase la var al formulario incrustado usando ' entry_options '

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

En su FormType incrustado

Agregue la opción a optionsResolver

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

Acceda a la variable en su función buildForm. Recuerde establecer esta variable antes de la función de constructor. En mi caso, necesitaba filtrar opciones en función de una identificación específica.

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
fuente
Para tener menos confusión, $ this-> var es el valor que desea pasar, no necesariamente de la variable de clase.
Darius.V