Uploading images using Form Collections and Doctrine in Symfony2

10,719

When you add by_reference => false you are suposed to set the counter entity in the add function.

So your property_id should be persisting correctly in the image changing the addImage function in your propery to:

public function addImage(\Mata\MainBundle\Entity\Image $image)
{
  $this->images[] = $image;

  $image->setProperty($this);

  return $this;
}
Share:
10,719
Christopher Mata
Author by

Christopher Mata

Updated on July 20, 2022

Comments

  • Christopher Mata
    Christopher Mata almost 2 years

    I have been trying to make a Form Collection with file upload in Symfony2 and following this guide

    http://symfony.com/doc/master/cookbook/form/form_collections.html

    but can't seem to make this part work:

    // src/Acme/TaskBundle/Entity/Task.php
    
    // ...
    
    public function setTags(ArrayCollection $tags)
    {
    foreach ($tags as $tag) {
        $tag->addTask($this);
    }
    
    $this->tags = $tags;
    }
    

    . 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.

    Here is the Property Entity:

    <?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->images = new ArrayCollection();
    }
    
    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
    
    /**
     * Set name
     *
     * @param string $name
     * @return Property
     */
    public function setName($name)
    {
        $this->name = $name;
    
        return $this;
    }
    
    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }
    
    /**
     * Set description
     *
     * @param string $description
     * @return Property
     */
    public function setDescription($description)
    {
        $this->description = $description;
    
        return $this;
    }
    
    /**
     * Get description
     *
     * @return string 
     */
    public function getDescription()
    {
        return $this->description;
    }
    
    /**
     * Set price
     *
     * @param float $price
     * @return Property
     */
    public function setPrice($price)
    {
        $this->price = $price;
    
        return $this;
    }
    
    /**
     * Get price
     *
     * @return float 
     */
    public function getPrice()
    {
        return $this->price;
    }
    
    /**
     * Set type
     *
     * @param string $type
     * @return Property
     */
    public function setType($type)
    {
        $this->type = $type;
    
        return $this;
    }
    
    /**
     * Get type
     *
     * @return string 
     */
    public function getType()
    {
        return $this->type;
    }
    
    /**
     * Set owner
     *
     * @param integer $owner
     * @return Property
     */
    public function setOwner($owner)
    {
        $this->owner = $owner;
    
        return $this;
    }
    
    /**
     * Get owner
     *
     * @return integer 
     */
    public function getOwner()
    {
        return $this->owner;
    }
    
    /**
     * Set available
     *
     * @param boolean $available
     * @return Property
     */
    public function setAvailable($available)
    {
        $this->available = $available;
    
        return $this;
    }
    
    /**
     * Get available
     *
     * @return boolean 
     */
    public function getAvailable()
    {
        return $this->available;
    }
    
    /**
     * Add images
     *
     * @param \Mata\MainBundle\Entity\Image $images
     * @return Property
     */
    public function addImage(\Mata\MainBundle\Entity\Image $images)
    {
        $this->images[] = $images;
    
        return $this;
    }
    
    public function setImages(ArrayCollection $images)
    {
        foreach ($images as $image) {
            $image->setProperty($this);
        }
    
        $this->images = $images;
    }
    
    /**
     * Remove images
     *
     * @param \Mata\MainBundle\Entity\Image $images
     */
    public function removeImage(\Mata\MainBundle\Entity\Image $images)
    {
        $this->images->removeElement($images);
    }
    
    /**
     * Get images
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getImages()
    {
        return $this->images;
    }
    }
    

    Image Entity:

    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->id;
    }
    
    /**
     * Set name
     *
     * @param string $name
     * @return Image
     */
    public function setName($name)
    {
        $this->name = $name;
    
        return $this;
    }
    
    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }
    
    /**
     * Set path
     *
     * @param string $path
     * @return Image
     */
    public function setPath($path)
    {
        $this->path = $path;
    
        return $this;
    }
    
    
    
    /**
     * Get path
     *
     * @return string 
     */
    public function getPath()
    {
        return $this->path;
    }
    
    public function getAbsolutePath()
    {
        return null === $this->path
            ? null
            : $this->getUploadRootDir().'/'.$this->path;
    }
    
    public function getWebPath()
    {
        return null === $this->path
            ? null
            : $this->getUploadDir().'/'.$this->path;
    }
    
    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__.'/../../../../web/'.$this->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->file) {
            // do whatever you want to generate a unique name
            $filename = sha1(uniqid(mt_rand(), true));
            $this->path = $filename.'.'.$this->file->guessExtension();
        }
    }
    
    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->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->file->move($this->getUploadRootDir(), $this->path);
    
        unset($this->file);
    }
    
    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }
    }
    
    /**
     * Set property
     *
     * @param \Mata\MainBundle\Entity\Property $property
     * @return Image
     */
    public function setProperty(\Mata\MainBundle\Entity\Property $property)
    {
        $this->property = $property;
    
        return $this;
    }
    
    /**
     * Get property
     *
     * @return \Mata\MainBundle\Entity\Property
     */
    public function getProperty()
    {
        return $this->property;
    }
    }
    

    Property FormType:

    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->add('name');
            $builder->add('description');
            $builder->add('price');
            $builder->add('type');
            $builder->add('owner');
            $builder->add('available');
            $builder->add('images', 'collection', array(
                'type' => new ImageType(),
                'allow_add' => true,
                'by_reference' => false,
                'allow_delete' => true,
                'prototype' => true
    
            ));
    
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'Mata\MainBundle\Entity\Property',
            ));
        }
    
        public function getName()
        {
            return 'property';
        }
    }
    

    Image FormType:

    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->add('name','text');
            $builder->add('file', 'file');
    
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'Mata\MainBundle\Entity\Image',
            ));
        }
    
        public function getName()
        {
            return 'images';
        }
    }
    

    Controller action:

    public function addAction(Request $request)
    {
    
        $em = $this->getDoctrine()->getManager();
        $property = new Property();
    
        $form = $this->createForm(new PropertyType(), $property);
    
        if ($request->isMethod('POST')) {
            $form->bind($request);
    
            if ($form->isValid()) {
                $em->persist($property);
                $em->flush();
    
                $this->get('session')->getFlashBag()->add('notice', 'Successfully added new Property');
    
                return $this->redirect($this->generateUrl('mata_admin.property.create'));
            }
        }
    
        return $this->render('MataAdminBundle:Property:add.html.twig',array(
            'title' =>  'Property',
            'form' => $form->createView()
            )
        );
    }
    
  • Christopher Mata
    Christopher Mata over 10 years
    Thanks for the response. I will try this. I do think this will solve it. Thank you very much.