简体   繁体   中英

Associative array fields in form builder - Symfony3

Let's start with a background.

I need user profile that will have basic info like name, email, phone etc. And for that I have an entity User.

I also need to store answers from a questionnaire. I was thinking to store them in database as a json in a text field. Those questions can change in the future, there are currently ~30 questions so I don't want to store it as an entity. So currently in my User entity I have this:

/**
 * @var array
 *
 * @ORM\Column(name="questionnaire", type="json_array", nullable=true)
 */
private $questionnaire;

I understand that Symfony will take care of json_encode/json_decode thing. So this is great.

But now, I have a bit of problem with creating a form with the symfony builder.

First I thought I could try something like this:

$builder->add('questionnaire[source]');

Which doesn't work. For symfony masters it's obvious I know ;o)

So currently my choices I see are: CollectionType or Data Transformers.

From what I see, CollectionType will not work, as it's only work with numeric arrays where we have field with some JS "Add another row" or something. http://symfony.com/doc/current/reference/forms/types/collection.html#adding-and-removing-items But if I'm wrong about this and I should go with CollectionType and there is some magic way please tell me. I can't find much about this.

So I was thinking Data Transformers or simply creating an array on submit without this Transformer thing. Create all fields that are in Questionnaire with the "mapped=>false" and then set those submitted values as an associative array to $questionnaire. This "feels" ok, but I'm not sure how to later handle this in "Edit" form (from docs I think with this http://symfony.com/doc/current/reference/forms/types/form.html#data ).

Questionnaire itself will have many ChoiceType fields, a CollectionType with "Add more row" and so on and it will have lots of questions. So it will be a bit complex. I would like to avoid creating entity for that with each question as a property (not sure if it's a right choice, but considering everything I believe it's the best one).

This is my first date with symfony so any help/tips appreciate.

After few days I found the answer for my own question. Bounty didn't helped to get it faster, but hey, there is a solution! ;o)

So turns out it's really simple. I've actually didn't found a lot on this particular problem, so in case you need something similar, here it is.

In the entity class with this json_array, define all needed keys like so:

/**
 * @var array
 *
 * @ORM\Column(name="questionnaire", type="json_array", nullable=true)
 */
private $questionnaire = [
    'favPet'=>'',
    'favFood'=>'',
    'favColor'=>''
];

Next, in the form builder use the "property_path"! It's that simple... https://symfony.com/doc/current/reference/forms/types/form.html#property-path like this:

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name')
        ->add('email', EmailType::class)
        ->add('phone')
        ->add('address')
         ->add('favPet',TextType::class,[
            'label'=>'Fav pet',
            'property_path'=>'questionnaire[favPet]'
        ])
        ->add('favFood',TextType::class,[
            'label'=>'Fav food',
            'property_path'=>'questionnaire[favFood]'
        ])
        ->add('favColor',TextType::class,[
            'label'=>'Fav color',
            'property_path'=>'questionnaire[favColor]'
        ])
    ;
}

And symfony will handle the rest. Because we used json_array as a type, symfony will handle json_encode/json_decode thing. In order for symfony to map/populate values in edit mode the property must have it's key defined. Otherwise you will get error:

PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path

And then in the twig template you can do this:

{{ user.questionnaire.favPet }}

And that's it! :o))

As for the JSON or Entity. I know I wrote no entity, but I didn't made up my mind. After some IRC conversations, reading and this https://stackoverflow.com/a/4013207/531099 I will probably go with EAV. So if you are deciding between JSON vs Entity, add EAV to the race.

I had a similar problem with Sylius: the entity had an array field called "configuration" and that field could was expected by the app to have the format like this:

['configuration' =>   
        ['filters' =>   
            ['taxons' =>   
                ['frg', 'books']   
            ]   
        ]   
     ];

I have managed to persist an associative array formatted like it was requested by using nested forms:

The form class hierarchy is this:

  • PercentageDiscountConfigurationType - for configuration
  • ActionFiltersType - for filters
  • TaxonFilterType - for taxons

You can see a PR with a similar functionality here: https://github.com/Sylius/Sylius/pull/6054/files

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM