Symfony 7 form CollectionType with additional parameters

82 views Asked by At

I got back to Symfony after many years and I'm loving this new version 7.

I'm struggling with the CollectionType as I need to display only certain fields based on the main object but I can't find a way to pass parameters/options to the embedded form (the CollectionType).

If I pass extra options I get the error: An error has occurred resolving the options of the form "Symfony\Component\Form\Extension\Core\Type\CollectionType": The option "category" does not exist.

I have a form based on the "Activity" Entity and the Activity can have many Animal. Based on the category of the activity, animals should have some fields and not some others.

IMPORTANT: the category of the activity is set in the controller and is not a field in the form, so I'm not trying to change it dynamically, all I need is to be able to write some logic into the embedded form class (AnimalType) based on the category of the Activity.

My idea was to pass the category of the activity to the subform and write the logic there to add the needed fields.

How can achieve that?

Here is my code:

<?php

namespace App\Form;

use App\Entity\Activity;
use App\Entity\Animal;
use App\Entity\User;
use App\Form\AnimalType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ActivityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('date')
            ->add('shift')
            ->add('people', EntityType::class, [
                'class' => User::class,
                'multiple' => true,
            ])
            ->add('animals', CollectionType::class, [
                'entry_type' => AnimalType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'category' => $options['category'],
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
            ])
            ->add('save', SubmitType::class);
        ;

        if ($options['category'] == 'asilo') {
            $builder->remove('people');
        }
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Activity::class,
            'category' => ActivityCategory::class,
        ]);
    }
}

And the AnimanType

<?php

namespace App\Form;

use App\Entity\Activity;
use App\Entity\Animal;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AnimalType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('quantity')
            ->add('type', ChoiceType::class, [
                'choices'  => [
                    'Full Day' => 1,
                    'Half Day' => 2,
                ],
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setRequired(['category']);
        $resolver->setDefaults([
            'data_class' => ActivityAnimal::class,
        ]);
    }
}

While passing the Activity->category to the main form from the controller works perfectly, I can't find a way to insert the same logic in the embedded CollectionType.

I tried passing the option to the CollectionType and I looked everywhere in the documentation. I also looked at event listeners but I didn't find a similar case.

Thank you!

1

There are 1 answers

3
Elinor Ifergan On

Two things :

-> As said, "category" does not exist as an option :) Take a look at the documentation here. So if you want to add category on your form, you can add an attribute ("attr" option).

-> If you need to do search on your datas to get the animals of the category on your option, you have to do it on your controller and passed datas in option.

For example, in your controller :

$animalCollection = $em->getRepository(Animal::class)->findBy(['category' => $category]);
$form = $this->createForm(ActivityType::class, $activity, ['category' => $category, 'animal_collection' => $animalCollection]);  

In you form :

<?php

namespace App\Form;

use App\Entity\Activity;
use App\Entity\ActivityCategory;
use App\Entity\Animal;
use App\Entity\User;
use App\Form\ActivityAnimalType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ActivityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('date')
            ->add('shift')
            ->add('people', EntityType::class, [
                'class' => User::class,
                'multiple' => true,
            ])
            ->add('animals', CollectionType::class, [
                'entry_type' => AnimalType::class,
                'entry_options' => [
                    'label' => false,
                ],
              // If you need to keep your category as an attribute in you form
                'attr' => ['category' => $options['category']],
              // Datas from your controller
                'data' =>  $option['animal_collection'],
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
            ])
            ->add('save', SubmitType::class);
        ;

        if ($options['category'] == 'asilo') {
            $builder->remove('people');
        }
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Activity::class,
            'category' => null,
            'animal_collection' => null,
        ]); 
    }
}

EDIT :

If you just need to have a tree view on your form, you can use StofDoctrineExtensionsBundle

I hope these will help you.