Meneruskan data ke buildForm () di Symfony 2.8, 3.0 dan yang lebih baru

87

Aplikasi saya saat ini mengirimkan data ke jenis formulir saya menggunakan konstruktor, seperti yang direkomendasikan dalam jawaban ini . Namun, panduan peningkatan versi Symfony 2.8 menyarankan bahwa meneruskan instans tipe ke createFormfungsi sudah tidak digunakan lagi:

Meneruskan tipe instance ke Form :: add (), FormBuilder :: add () dan metode FormFactory :: create * () tidak digunakan lagi dan tidak akan didukung lagi di Symfony 3.0. Teruskan nama kelas yang sepenuhnya memenuhi syarat dari jenis tersebut.

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

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

Mengingat saya tidak dapat meneruskan data dengan nama kelas yang sepenuhnya memenuhi syarat, apakah ada alternatifnya?

Jonathan
sumber
1
Jenis data apa yang perlu Anda sampaikan? Apakah itu sesuatu yang bisa disuntikkan?
Cerad
2
Semoga UPGRADE.md akan ditingkatkan: github.com/symfony/symfony/issues/18662
Althaus

Jawaban:

133

Ini juga merusak beberapa formulir kami. Saya memperbaikinya dengan melewatkan data khusus melalui opsi resolver.

Dalam tipe formulir Anda:

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

Kemudian saat Anda membuat formulir di pengontrol Anda, berikan sebagai opsi alih-alih di konstruktor:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);
sekl
sumber
8
Baru saja menemukan masalah ini juga dan melakukan solusi serupa. Saya pikir jika data diperlukan dan jika Anda ingin melakukan jenis yang mengisyaratkan yang biasanya Anda lakukan dalam definisi konstruktor, Anda harus menggunakan metode setRequired () dan setAllowedTypes () untuk opsi resolver di configureOptions () , sebagai ganti setDefaults ().
sarahg
2
Itulah yang harus Anda lakukan. :)
Bernhard Schussek
3
@Roubi Anda melakukan hal yang sama, Anda menentukan opsi dalam metode configureOptions dan kemudian meneruskannya saat menambahkan bidang formulir.
Bart Wesselink
2
Saya juga tidak senang dengan perubahan ini. Terima kasih atas jawabannya.
Adambean
2
BentukTipe bertindak seperti pabrik, mereka harus tanpa kewarganegaraan. Menyuntikkan nilai melalui konstruktornya (selain melalui metode tag layanan) membuatnya menjadi stateful. Dengan cara ini Anda memiliki 1 cara seragam untuk membuat jenis formulir Anda. Opsi selalu dimaksudkan untuk digunakan sebagai pengganti argumen konstruktor. Perubahan ini bagus untuk DX dan kegunaan.
Siapapun
6

Di sini dapat digunakan pendekatan lain - layanan injeksi untuk mengambil data.

  1. Jelaskan formulir Anda sebagai layanan ( buku masak )
  2. Tambahkan bidang dan konstruktor yang dilindungi ke kelas formulir
  3. Gunakan objek yang diinjeksi untuk mendapatkan data yang Anda butuhkan

Contoh:

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
sumber
Ini bagus, tetapi tidak akan berfungsi ketika argumen tidak tersedia untuk manajer layanan.
demonkoryu
5

Jika ada yang menggunakan fungsi 'createNamedBuilder' atau 'createNamed' dari layanan form.factory, berikut cuplikan tentang cara menyetel dan menyimpan data menggunakannya. Anda tidak dapat menggunakan bidang 'data' (biarkan nol) dan Anda harus menyetel data / entitas yang diteruskan sebagai $optionsnilai.

Saya juga memasukkan instruksi @sarahg tentang menggunakan opsi setAllowedTypes () dan setRequired () dan tampaknya berfungsi dengan baik tetapi Anda harus terlebih dahulu menentukan bidang dengan setDefined ()

Juga di dalam formulir jika Anda membutuhkan data untuk disetel, ingatlah untuk menambahkannya ke bidang 'data'.

Di Controller saya menggunakan getBlockPrefix karena getName sudah tidak digunakan lagi di 2.8 / 3.0

Pengontrol:

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

Bentuk:

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

Berikut ini cara mengirimkan data ke formulir yang disematkan untuk siapa saja yang menggunakan Symfony 3. Pertama lakukan persis seperti yang diuraikan @sekl di atas dan kemudian lakukan hal berikut:

Di FormType utama Anda

Meneruskan var ke formulir yang disematkan menggunakan ' entry_options '

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

Di Jenis Formulir Tersemat Anda

Tambahkan opsi ke optionsResolver

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

Akses variabel dalam fungsi buildForm Anda. Ingatlah untuk menyetel variabel ini sebelum fungsi pembuat. Dalam kasus saya, saya perlu memfilter opsi berdasarkan ID tertentu.

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.dll
sumber
Untuk mengurangi kebingungan - $ this-> var adalah nilai yang ingin Anda teruskan, tidak harus dari variabel kelas.
Darius.V