title: Uploading images using Form Collections and Doctrine in Symfony2
body: <p>I have been trying to make a Form Collection with file upload in Symfony2 and following this guide </p>
  
  <p><a href="http://symfony.com/doc/master/cookbook/form/form_collections.html" rel="noreferrer">http://symfony.com/doc/master/cookbook/form/form_collections.html</a> </p>
  
  <p>but can't seem to make this part work:</p>
  
  <pre><code>// src/Acme/TaskBundle/Entity/Task.php
  
  // ...
  
  public function setTags(ArrayCollection $tags)
  {
  foreach ($tags as $tag) {
      $tag-&gt;addTask($this);
  }
  
  $this-&gt;tags = $tags;
  }
  </code></pre>
  
  <p>. Basically, I have a Property Entity and an Image Entity with 1 to many relationship. I already made each of their FormTypes and the Property Entity persist just fine on the other hand Image entity's property_id column always gets NULL, even thought other properties of Image entities get persisted properly.</p>
  
  <p>Here is the Property Entity:</p>
  
  <pre><code>&lt;?php
  
  namespace Mata\MainBundle\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Doctrine\Common\Collections\ArrayCollection;
  
  use Mata\MainBundle\Entity\Image;
  
  
  class Property
  {
  /**
   * @var integer
   *
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;
  
  /**
   * @var string
   *
   * @ORM\Column(name="name", type="string", length=255)
   */
  private $name;
  
  /**
   * @var string
   *
   * @ORM\Column(name="description", type="text")
   */
  private $description;
  
  /**
   * @var float
   *
   * @ORM\Column(name="price", type="float")
   */
  private $price;
  
  /**
   * @var string
   *
   * @ORM\Column(name="type", type="string", length=255)
   */
  private $type;
  
  /**
   * @var integer
   *
   * @ORM\Column(name="owner", type="integer")
   */
  private $owner;
  
  /**
   * @var boolean
   *
   * @ORM\Column(name="available", type="boolean")
   */
  private $available;
  
  /**
   *
   *
   * @ORM\OneToMany(targetEntity="Image", mappedBy="property", cascade={"persist"})
   */
  private $images;
  
  public function __construct()
  {
      $this-&gt;images = new ArrayCollection();
  }
  
  /**
   * Get id
   *
   * @return integer 
   */
  public function getId()
  {
      return $this-&gt;id;
  }
  
  /**
   * Set name
   *
   * @param string $name
   * @return Property
   */
  public function setName($name)
  {
      $this-&gt;name = $name;
  
      return $this;
  }
  
  /**
   * Get name
   *
   * @return string 
   */
  public function getName()
  {
      return $this-&gt;name;
  }
  
  /**
   * Set description
   *
   * @param string $description
   * @return Property
   */
  public function setDescription($description)
  {
      $this-&gt;description = $description;
  
      return $this;
  }
  
  /**
   * Get description
   *
   * @return string 
   */
  public function getDescription()
  {
      return $this-&gt;description;
  }
  
  /**
   * Set price
   *
   * @param float $price
   * @return Property
   */
  public function setPrice($price)
  {
      $this-&gt;price = $price;
  
      return $this;
  }
  
  /**
   * Get price
   *
   * @return float 
   */
  public function getPrice()
  {
      return $this-&gt;price;
  }
  
  /**
   * Set type
   *
   * @param string $type
   * @return Property
   */
  public function setType($type)
  {
      $this-&gt;type = $type;
  
      return $this;
  }
  
  /**
   * Get type
   *
   * @return string 
   */
  public function getType()
  {
      return $this-&gt;type;
  }
  
  /**
   * Set owner
   *
   * @param integer $owner
   * @return Property
   */
  public function setOwner($owner)
  {
      $this-&gt;owner = $owner;
  
      return $this;
  }
  
  /**
   * Get owner
   *
   * @return integer 
   */
  public function getOwner()
  {
      return $this-&gt;owner;
  }
  
  /**
   * Set available
   *
   * @param boolean $available
   * @return Property
   */
  public function setAvailable($available)
  {
      $this-&gt;available = $available;
  
      return $this;
  }
  
  /**
   * Get available
   *
   * @return boolean 
   */
  public function getAvailable()
  {
      return $this-&gt;available;
  }
  
  /**
   * Add images
   *
   * @param \Mata\MainBundle\Entity\Image $images
   * @return Property
   */
  public function addImage(\Mata\MainBundle\Entity\Image $images)
  {
      $this-&gt;images[] = $images;
  
      return $this;
  }
  
  public function setImages(ArrayCollection $images)
  {
      foreach ($images as $image) {
          $image-&gt;setProperty($this);
      }
  
      $this-&gt;images = $images;
  }
  
  /**
   * Remove images
   *
   * @param \Mata\MainBundle\Entity\Image $images
   */
  public function removeImage(\Mata\MainBundle\Entity\Image $images)
  {
      $this-&gt;images-&gt;removeElement($images);
  }
  
  /**
   * Get images
   *
   * @return \Doctrine\Common\Collections\Collection 
   */
  public function getImages()
  {
      return $this-&gt;images;
  }
  }
  </code></pre>
  
  <p>Image Entity:
      
  
  <pre><code>namespace Mata\MainBundle\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Validator\Constraints as Assert;
  
  /**
   * Image
   *
   * @ORM\Table()
   * @ORM\Entity(repositoryClass="Mata\MainBundle\Entity\ImageRepository")
   * @ORM\HasLifecycleCallbacks
   */
  class Image
  {
  /**
   * @var integer
   *
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;
  
  /**
   * @Assert\File(maxSize="6000000")
   */
  public $file;
  
  /**
   * @var string
   *
   * @ORM\Column(name="name", type="string", length=255)
   */
  private $name;
  
  /**
   * @var string
   *
   * @ORM\Column(name="path", type="string", length=255)
   */
  private $path;
  
  
  /**
   * @ORM\ManyToOne(targetEntity="Property", inversedBy="images")
   * @ORM\JoinColumn(name="property_id", referencedColumnName="id")
   */
  protected $property;
  
  
  
  /**
   * Get id
   *
   * @return integer 
   */
  public function getId()
  {
      return $this-&gt;id;
  }
  
  /**
   * Set name
   *
   * @param string $name
   * @return Image
   */
  public function setName($name)
  {
      $this-&gt;name = $name;
  
      return $this;
  }
  
  /**
   * Get name
   *
   * @return string 
   */
  public function getName()
  {
      return $this-&gt;name;
  }
  
  /**
   * Set path
   *
   * @param string $path
   * @return Image
   */
  public function setPath($path)
  {
      $this-&gt;path = $path;
  
      return $this;
  }
  
  
  
  /**
   * Get path
   *
   * @return string 
   */
  public function getPath()
  {
      return $this-&gt;path;
  }
  
  public function getAbsolutePath()
  {
      return null === $this-&gt;path
          ? null
          : $this-&gt;getUploadRootDir().'/'.$this-&gt;path;
  }
  
  public function getWebPath()
  {
      return null === $this-&gt;path
          ? null
          : $this-&gt;getUploadDir().'/'.$this-&gt;path;
  }
  
  protected function getUploadRootDir()
  {
      // the absolute directory path where uploaded
      // documents should be saved
      return __DIR__.'/../../../../web/'.$this-&gt;getUploadDir();
  }
  
  protected function getUploadDir()
  {
      // get rid of the __DIR__ so it doesn't screw up
      // when displaying uploaded doc/image in the view.
      return 'uploads/documents';
  }
  
  /**
   * @ORM\PrePersist()
   * @ORM\PreUpdate()
   */
  public function preUpload()
  {
      if (null !== $this-&gt;file) {
          // do whatever you want to generate a unique name
          $filename = sha1(uniqid(mt_rand(), true));
          $this-&gt;path = $filename.'.'.$this-&gt;file-&gt;guessExtension();
      }
  }
  
  /**
   * @ORM\PostPersist()
   * @ORM\PostUpdate()
   */
  public function upload()
  {
      if (null === $this-&gt;file) {
          return;
      }
  
      // if there is an error when moving the file, an exception will
      // be automatically thrown by move(). This will properly prevent
      // the entity from being persisted to the database on error
      $this-&gt;file-&gt;move($this-&gt;getUploadRootDir(), $this-&gt;path);
  
      unset($this-&gt;file);
  }
  
  /**
   * @ORM\PostRemove()
   */
  public function removeUpload()
  {
      if ($file = $this-&gt;getAbsolutePath()) {
          unlink($file);
      }
  }
  
  /**
   * Set property
   *
   * @param \Mata\MainBundle\Entity\Property $property
   * @return Image
   */
  public function setProperty(\Mata\MainBundle\Entity\Property $property)
  {
      $this-&gt;property = $property;
  
      return $this;
  }
  
  /**
   * Get property
   *
   * @return \Mata\MainBundle\Entity\Property
   */
  public function getProperty()
  {
      return $this-&gt;property;
  }
  }
  </code></pre>
  
  <p>Property FormType:
  
  
  <pre><code>namespace Mata\AdminBundle\Form\Type;
  
  
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolverInterface;
  
  use Mata\AdminBundle\Form\Type\ImageType;
  
  class PropertyType extends AbstractType
  {
  
  
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
  
          $builder-&gt;add('name');
          $builder-&gt;add('description');
          $builder-&gt;add('price');
          $builder-&gt;add('type');
          $builder-&gt;add('owner');
          $builder-&gt;add('available');
          $builder-&gt;add('images', 'collection', array(
              'type' =&gt; new ImageType(),
              'allow_add' =&gt; true,
              'by_reference' =&gt; false,
              'allow_delete' =&gt; true,
              'prototype' =&gt; true
  
          ));
  
      }
  
      public function setDefaultOptions(OptionsResolverInterface $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'Mata\MainBundle\Entity\Property',
          ));
      }
  
      public function getName()
      {
          return 'property';
      }
  }
  </code></pre>
  
  <p>Image FormType:
  
  
  <pre><code>namespace Mata\AdminBundle\Form\Type;
  
  
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolverInterface;
  
  
  class ImageType extends AbstractType
  {
  
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('name','text');
          $builder-&gt;add('file', 'file');
  
      }
  
      public function setDefaultOptions(OptionsResolverInterface $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'Mata\MainBundle\Entity\Image',
          ));
      }
  
      public function getName()
      {
          return 'images';
      }
  }
  </code></pre>
  
  <p>Controller action:</p>
  
  <pre><code>public function addAction(Request $request)
  {
  
      $em = $this-&gt;getDoctrine()-&gt;getManager();
      $property = new Property();
  
      $form = $this-&gt;createForm(new PropertyType(), $property);
  
      if ($request-&gt;isMethod('POST')) {
          $form-&gt;bind($request);
  
          if ($form-&gt;isValid()) {
              $em-&gt;persist($property);
              $em-&gt;flush();
  
              $this-&gt;get('session')-&gt;getFlashBag()-&gt;add('notice', 'Successfully added new Property');
  
              return $this-&gt;redirect($this-&gt;generateUrl('mata_admin.property.create'));
          }
      }
  
      return $this-&gt;render('MataAdminBundle:Property:add.html.twig',array(
          'title' =&gt;  'Property',
          'form' =&gt; $form-&gt;createView()
          )
      );
  }
  </code></pre>