0

I am dynamically generating forms in Symfony 6. From the controller, I am calling a Form Type class in the traditional method. Therein, I am dynamically building some form fields based on the elements of the $options passed to the FormType class.

<?php
    public function index(Request $request): Response
    {

    // Entity is called Breed
    $data = new Breed();

    // I am passing these $options to the form
    $options = [
            'form_1'          => true,
            'form_2'          => false,
            'units'           => 'pounds'
            'form_3'          => false,
        ];

    $form = $this->createForm(BreedSurveyType::class, $data, $options);
    
?>

In the BreedSurveyType form class, I am able to get my variables where I declare them in the configureOptions method...

<?php
 public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Breed::class,
            'form_1'          => false,
            'form_2'          => false,
            'units'           => null
            'form_3'          => false,
        ]);
    }
?>

In the buildForm method, I can access the $options, but I cannot if I embed a sub-form.

<?php
 public function buildForm(FormBuilderInterface $builder,array $options): void 
{
        // form_1          =   true
        // form_2          =   false
        // units           =  'pounds'
        // form_3          =   true

        if ($options['form_1'] === true) {
            
            $builder
                ->add(
                    'name',
                    AnimalNameType::class
                );
        }

        if ($options['form_2'] === true) {
            
            $builder
                ->add(
                    'sex',
                    AnimalSexType::class
                );
        }

        if ($options['form_3'] === true) {
            
            $builder
                ->add(
                    'weight',
                    AnimalWeightType::class
                );
        }
?>

.... in the parent-form, when I dump the $options variable,

array:59 [▼
  // snippet from web debug toolbar
  "form_1" => true
  "form_2" => false
  "units"  => "pounds"
  "form_3" => true
]

.... in the sub-form, when I dump the $options variable, I get the results from the declarations I made in the configureOptions method.

array:59 [▼
  // snippet from web debug toolbar
  "form_1" => false
  "form_2" => false
  "units"  => null
  "form_3" => false
]

Effectively, is there a method to pass $options to a subform or do I need to isolate my logic of the dynamically created form in my BreedSurveyType class?

DarkBee
  • 16,592
  • 6
  • 46
  • 58
Tom Olson
  • 59
  • 7

2 Answers2

2

You can pass the required data to your child form by passing it in the options when adding the property.

Your parent form:

class BreedType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        if ($options['form_1'] === true) {

            $builder
                ->add('name',AnimalNameType::class, [
                        'form_1' => $options['form_1'],
                        'form_2' => $options['form_2'],
                        'units' => $options['units'],
                        'form_3' => $options['form_2'],
                    ]

                );
        }

    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Breed::class, 
            "form_1" => true,
            "form_2" => false,
            "units"  => "pounds",
            "form_3" => true,
        ]);
    }

}

And in your child form AnimalNameType specify the default values ​​in configureOptions

public function configureOptions(OptionsResolver $resolver): void
{
    $resolver->setDefaults([
        'data_class' => AnimalName::class,
        "form_1" => true,
        "form_2" => false,
        "units"  => "pounds",
        "form_3" => true,
    ]);
}
emrdev
  • 2,155
  • 3
  • 9
  • 15
  • 1
    Thank you for the help. By the way, I tried something similar, thinking I could just pass the options array as $options into the third argument and that did not work. Breaking them out into new key=>value pairs is what I was missing and where the documentation is lacking. When I complete this project, I will submit a pull request to Symfony to add this to the documentation. – Tom Olson Apr 12 '22 at 14:58
1

To follow up on the answer given by Mr. Harvi Dent above, I reached a global workaround with the following technique. Thank you Mr. Dent.

  1. I put all the default $options into a service class with a public (static) array. Therein that array, it contains only the default variables I would pass from parent-form to sub-form.
  2. Perform a call to the array_intersect_key() function

Constant (array) in my service:

<?php
    public const DEFAULT_ASSESSMENT_OPTIONS
        = [
            'form_1'          => false,
            'form_2'          => false,
            'form_3'          => false,
            'units'           => 'pounds',
        ];
?>

Revised Technique

<?php
class ParentFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $newOptions = array_intersect_key($options, Options::DEFAULT_ASSESSMENT_OPTIONS);

        // Herein, I am just passing the new $options array, after 
        // I have removed all of the other $options passed into the 
        // original $options array and all of the other Symfony $options 
        // are removed.
        if ($newOptions['form_1'] === true) {
            $builder->add(
                'transferee',
                SubFormOneType::class,
                $newOptions   // array from above
            );
        }
}
?>
Tom Olson
  • 59
  • 7