Search code examples
formssymfonycollectionsfosrestbundle

Symfony 4: JSON to Form Collection


Im working on a FormType that will be populated through JSON (FOSRestBundle). This form has a CollectionType and I have the entry_type set to another FormType.

But whatever I try, I end up with the error: This form should not contain extra fields.

ProductCollection:

class ProductCollection
{
    const GROUP_PRODUCT_COLLECTION_LIST = "product_collection_list";

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=128)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="GamePoint\Shop\Domain\ShopPackage\ShopPackage", mappedBy="productCollection")
     */
    private $shopPackages;

    /**
     * @ORM\OneToMany(targetEntity="GamePoint\Shop\Domain\ProductProductCollection\ProductProductCollection", mappedBy="productCollection")
     */
    private $productProductCollections;

ProductCollectionType:

class ProductCollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class)
            ->add('productProductCollections', CollectionType::class, [
                'entry_type' => ProductProductCollectionType::class
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ProductCollection::class,
            'csrf_protection' => false,
        ]);
    }
}

ProductProductCollection:

class ProductProductCollection
{
    /**
     * @ORM\Column(type="json")
     */
    private $settings = [];

    /**
     * @ORM\Id()
     * @ORM\ManyToOne(targetEntity="GamePoint\Shop\Domain\Product\Product", inversedBy="productProductCollections")
     */
    private $product;

    /**
     * @ORM\Id()
     * @ORM\ManyToOne(targetEntity="GamePoint\Shop\Domain\ProductCollection\ProductCollection", inversedBy="productProductCollections")
     */
    private $productCollection;

ProductProductCollectionType:

class ProductProductCollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('settings')
            ->add('product', EntityType::class, ['required' => false, 'class' => Product::class])
            ->add('productCollection', EntityType::class, ['required' => false, 'class' => ProductCollection::class])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ProductProductCollection::class,
            'csrf_protection' => false,
        ]);
    }
}

JSON send to Route:

{
    "product_collection": {
        "name": "cheap stuff",
        "productProductCollections": [
            {
                "product": 1,
                "settings" : "['test' => '123']"
            },
            {
                "product": 2,
                "settings" : "['test' => '123']"
            }
        ]
    }
}

Parameter bag:

+request: ParameterBag {#537
    #parameters: array:1 [
      "product_collection" => array:2 [
        "name" => "cheap stuff"
        "productProductCollections" => array:2 [
          0 => array:2 [
            "product" => 1
            "settings" => "['test' => '123']"
          ]
          1 => array:2 [
            "product" => 2
            "settings" => "['test' => '123']"
          ]
        ]
      ]
    ]
  }

Whenever I send this JSON to the ProductCollectionType, I get the following FormErrorIterator:

array (size=1)
  0 => 
    object(Symfony\Component\Form\FormError)[613]
      protected 'messageTemplate' => string 'This form should not contain extra fields.' (length=42)
      protected 'messageParameters' => 
        array (size=1)
          '{{ extra_fields }}' => string '"0", "1"' (length=8)
      protected 'messagePluralization' => null
      private 'message' => string 'This form should not contain extra fields.' (length=42)

It says that the array with ProductProductCollections there keys are "extra fields"

Does anybody know how the JSON should look like to fix this error?

If have tried to replicate the setup and check how Twig send the data to the form, which looks like the following:

Symfony\Component\HttpFoundation\Request {#7 ▼
  +attributes: Symfony\Component\HttpFoundation\ParameterBag {#10 ▶}
  +request: Symfony\Component\HttpFoundation\ParameterBag {#8 ▼
    #parameters: array:1 [▼
      "product_collection" => array:4 [▼
        "name" => "ewerew"
        "productSelections" => array:2 [▼
          0 => array:2 [▼
            "settings" => "qrew"
            "product" => "2"
          ]
          1 => array:2 [▼
            "settings" => "qwre"
            "product" => "1"
          ]
        ]
        "save" => ""
        "_token" => "z3um5Wu_iTZvr2OYsaGZJvtLBHVfJN5HYkO4UFQbpiw"
      ]
    ]
  }

Its saves fine in the Twig setup, so I really can't see what I'm doing wrong....

So I would like to know the following things:

  • How should the JSON look to not get this error?
  • Do I need to change something else?

Solution

  • Try adding the allow_add attribute to the CollectionType in the form, as it is false by default. Documentation

    ->add('productProductCollections', CollectionType::class, [
        'entry_type' => ProductProductCollectionType::class,
        'allow_add' => true,
    ])
    

    You could also set the allow_extra_fields to true in your form defaults. But you shouldn't be using this as it allows all extra fields.

    $resolver->setDefaults([
        'data_class' => ProductCollection::class,
        'csrf_protection' => false,
        'allow_extra_fields' => true,
    ]);