title: Symfony3 - Validation of an embedded form
body: <p>I have an embedded form like this:</p>
  
  <p>-> Parent (class)</p>
  
  <p>|---> Attribute 1 (collection) : children (class) : required</p>
  
  <p>.......|-----> Attribute 1 : child_name (string) : required</p>
  
  <p>.......|-----> Attribute 2 : child_description (string) : required</p>
  
  <p>I want, when I send the form:</p>
  
  <ol>
  <li>There is at least one parent object sent (I mean the children attribute contains at least one row of (child_name and child_description)</li>
  </ol>
  
  <p>If the data received will be like this :</p>
  
  <ul>
  <li><code>parent[children][__name__][child_name]=value1&amp;parent[children][__name__][child_description]=value2</code></li>
  </ul>
  
  <p>Where <strong>name</strong> is the iteration in the ArrayCollection of the attribute children</p>
  
  <p>So when we send two children in the form, we get this data:</p>
  
  <ul>
  <li><code>parent[children][0][child_name]=value1&amp;parent[children][0][child_description]=value2&amp;parent[children][1][child_name]=value3&amp;parent[children][1][child_description]=value4</code></li>
  </ul>
  
  <p>I want that:</p>
  
  <ol start="2">
  <li><p>If the user will send only nothing (empty form), he gets an error that the atribute children is required.</p></li>
  <li><p>If the user will send only the child_name (or the child_description) value, he gets an error that the child_description (or the child_name) is also required. I mean if he doesn't event send the varaible child_name (or child_description) in the form like this:</p>
  
  <ul>
  <li><code>parent[children][__name__][child_name]=value1 (without the name and the value of the variable parent[children][__name__][child_description])</code></li>
  </ul></li>
  </ol>
  
  <p>I tried to validate this form from the Entity But I got some problems.</p>
  
  <p>In the Parent Entity class:</p>
  
  <pre><code>/**
   * Parent
   *
   * @ORM\Table(name="parent")
   * @ORM\Entity
   */
  class Parent
  {
  
      /**
       * @ORM\Id
       * @ORM\Column(type="integer")
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      protected $id;
  
      /**
       * @var \Doctrine\Common\Collections\ArrayCollection
       *
       * @Assert\NotBlank(message="Error", groups={"Creation"})
       * @Assert\NotNull(message="Error", groups={"Creation"}))
       * @Assert\Count(
       *      min = 1,
       *      minMessage = "Error",
       *      groups={"Creation"}
       * )
       * @ORM\OneToMany(targetEntity="Children", mappedBy="parent", cascade={"ALL"}, orphanRemoval=true)
       */
      protected $children;
  
      public function __construct()
      {
          $this-&gt;children = new ArrayCollection();
      }
  
      /**
       * Add child
       *
       * @param Child $child
       * @return children
       */
      public function addChild(Child $child)
      {
          $this-&gt;children[] = $child;
          $child-&gt;setParent($this);
          return $this;
      }
  
      /**
       * Remove child
       *
       * @param Child $child
       */
      public function removeChild(Child $child)
      {
          $this-&gt;children-&gt;removeElement($child);
      }
  
      public function getChildren()
      {
          return $this-&gt;children;
      }
  
      /**
       * Get id.
       *
       * @return int
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  }
  </code></pre>
  
  <p>Then, the Child Entity class:</p>
  
  <pre><code>/**
   * Child
   *
   * @ORM\Table(name="child")
   * @UniqueEntity(
   *     fields={"child_name","child_description"},
   *     message="already registred"
   * )
   */
  class Child
  {
      /**
       * @ORM\Id
       * @ORM\Column(type="integer")
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      private $id;
  
      /**
       * @var Parent
       */
      private $parent;
  
          /**
           * @ORM\Column(name="child_name", type="string", length=255 )
           *
           * Assert\NotBlank(message="empty", groups={"Creation"})
           * Assert\NotNull(message="null", groups={"Creation"})
           * @Assert\Length(
           *     min=3,
           *     minMessage="too short",
           *     groups={"Creation"}
           * )
           * @Assert\Type(
           *     type="string",
           *     message="you need a string",
           *     groups={"Creation"}
           * )
           */
          private $child_name;
  
          /**
           * @ORM\Column(name="child_description", type="string", length=255 )
           *
           * Assert\NotBlank(message="empty", groups={"Creation"})
           * Assert\NotNull(message="null", groups={"Creation"})
           * @Assert\Length(
           *     min=3,
           *     minMessage="too short",
           *     groups={"Creation"}
           * )
           * @Assert\Type(
           *     type="string",
           *     message="you need a string",
           *     groups={"Creation"}
           * )
           */
          private $child_description;
  
      public function __construct()
      {}
  
      /**
       * Get id
       *
       * @return integer
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  
      /**
       * Set childName
       *
       * @param string $childName
       *
       * @return Child
       */
      public function setChildName($childName)
      {
          $this-&gt;child_name = $childName;
  
          return $this;
      }
  
      /**
       * Get childName
       *
       * @return string
       */
      public function getChildName()
      {
          return $this-&gt;child_name;
      }
  
      /**
       * Set childDescription
       *
       * @param string $childDescription
       *
       * @return Child
       */
      public function setChildDescription($childDescription)
      {
          $this-&gt;child_description = $childDescription;
  
          return $this;
      }
  
      /**
       * Get childDescription
       *
       * @return string
       */
      public function getDescription()
      {
          return $this-&gt;child_description ;
      }
  </code></pre>
  
  <p>In the Controller class:</p>
  
  <pre><code>public function postChildrenAction(Request $request)
  {
      $result = new Parent();
      $em = $this-&gt;get('doctrine.orm.entity_manager');
      $form = $this-&gt;createForm(ParentType::class, $result, array(
              'validation_groups' =&gt; array('Creation', 'Default'),
      ));
      $form-&gt;submit($request-&gt;request-&gt;get($form-&gt;getName()));
      if ($form-&gt;isSubmitted() &amp;&amp; $form-&gt;isValid()) {
          //persistance
      }
  }
  </code></pre>
  
  <p>In the ParentType class :</p>
  
  <pre><code>class ParentType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('children', CollectionType::class,array(
                  'entry_type' =&gt; ChildType::class,
                  'required'=&gt; true,
                  'allow_add' =&gt; true,
                  'allow_delete'  =&gt; true,
                  'by_reference' =&gt; false,
                  'constraints' =&gt; array(new Valid()), // __LINE_A__
                  'entry_options' =&gt; array(
                          'validation_groups' =&gt; $options['validation_groups'],
                          'required'=&gt; true,
                  ),
          ));
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults([
              'data_class' =&gt; 'Path\NameBundle\Entity\Parent',
              'csrf_protection' =&gt; false,
              'validation_groups' =&gt; array('Creation', 'Default'),
              'cascade_validation' =&gt; true,
              'error_bubbling' =&gt; false,
          ]);
      }
  }
  </code></pre>
  
  <p>And finally the ChildType:</p>
  
  <pre><code>class ChildType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('child_name',TextType::class,array('required'=&gt; true,'validation_groups'=&gt;$options['validation_groups']));
          $builder-&gt;add('child_description',TextType::class,array('required'=&gt; true,'validation_groups'=&gt;$options['validation_groups']));
      }
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults([
              'data_class' =&gt; 'Path\NameBundle\Entity\Child',
              'csrf_protection' =&gt; false,
              'validation_groups' =&gt; array('Creation', 'Default'),
              'cascade_validation' =&gt; true,
              'error_bubbling' =&gt; false,
          ]);
      }
  }
  </code></pre>
  
  <ol start="4">
  <li><p>The first problem is in the Parent Entity class: when I remove the Assert\Count() constraint from the children attribute, the validation will succeed. So why the constraints NotBlank and NotNull doesn't work ?</p></li>
  <li><p>If I comment the line <code>'constraints' =&gt; array(new Valid())</code> (<code>__LINE_A__</code>) in the ParentType, the constraint UniqueEntity of ChildType will not work. Why ?</p></li>
  <li><p>None of the <code>'required' =&gt; true</code> options in the ParentType and ChildType worked. When I send only the child name without a description (or the inverse), I got an SQL error saying that there is a null value of the description.</p></li>
  <li><p>When the form is empty the validation will succeed but I need to get an error when some data is missing (for the child object or the parent object).</p></li>
  <li><p>The groups option from the Parent/Child Entity classes doesn't work if the form is sent with a missing variable even when I enable <code>'cascade_validation' =&gt; true</code>.</p></li>
  </ol>
  
  <p>This is too hard.</p>
  
  <p>And to finish with this post, I want to know :</p>
  
  <ol start="9">
  <li>The constraints that we define in the Entity classes aren't the ones that validates the form ? Because when I create the same constraints in validation.yml (using NotBlank with child_name and child_description) the validation works fine and fails if there is a missing variable/data.</li>
  </ol>
  
  <h2>Thank you even by answering me to one of these questions.</h2>