0

I have a very weird problem. I have an entity: Property It has a ManyToOne connection to pictures

When I add more pictures for the form, when I save it only the first added picture saved, the rest is not. However, I can add new pictures after every save, but only one at a time.

Code: Property Entity

namespace Ciber\PropertyBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use \Ciber\UtilBundle\Entity\Traits as CiberTraits;

/**
 * Property
 *
 * @ORM\Table(name="property")
 * @ORM\Entity(repositoryClass="Ciber\PropertyBundle\Repository\PropertyRepository")
 */
class Property
{
    use ORMBehaviors\Timestampable\Timestampable;
    use CiberTraits\AddressTrait;
    use CiberTraits\PriceTrait;
    use ORMBehaviors\Translatable\Translatable;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;


    /**
     *
     * @ORM\OneToMany(targetEntity="Ciber\PropertyBundle\Entity\PropertyImage", mappedBy="property", orphanRemoval=true, cascade={"persist"})
     */
    private $propertyImages;

    /**
     *
     * @ORM\OneToMany(targetEntity="Ciber\PropertyBundle\Entity\PropertyPlan", mappedBy="property", orphanRemoval=true, cascade={"persist"})
     */
    private $propertyPlans;


    /**
     * @ORM\ManyToOne(targetEntity="Ciber\UserBundle\Entity\User", inversedBy="properties", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;


    /**
     * Constructor
     */
    public function __construct()
    {
        $this->propertyImages = new ArrayCollection();
        $this->propertyPlans = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }


    /**
     * Add PropertyImage
     *
     * @param \Ciber\PropertyBundle\Entity\PropertyImage $propertyImage
     * @return Property
     */
    public function addPropertyImage(\Ciber\PropertyBundle\Entity\PropertyImage $propertyImage)
    {

        //$this->propertyImages[] = $propertyImage;
        $this->propertyImages->add($propertyImage);
        $propertyImage->setProperty($this);

        return $this;
    }

    /**
     * Remove propertyImage
     *
     * @param \Ciber\PropertyBundle\Entity\PropertyImage $propertyImage
     */
    public function removePropertyImage(\Ciber\PropertyBundle\Entity\PropertyImage $propertyImage)
    {
        $this->propertyImages->removeElement($propertyImage);
    }

    /**
     * Add propertyPlans
     *
     * @param \Ciber\PropertyBundle\Entity\PropertyPlan $propertyPlans
     * @return Property
     */
    public function addPropertyPlan(\Ciber\PropertyBundle\Entity\PropertyPlan $propertyPlans)
    {
        $this->propertyPlans[] = $propertyPlans;
        $propertyPlans->setProperty($this);

        return $this;
    }

    /**
     * Remove propertyPlans
     *
     * @param \Ciber\PropertyBundle\Entity\PropertyPlan $propertyPlan
     */
    public function removePropertyPlan(\Ciber\PropertyBundle\Entity\PropertyPlan $propertyPlan)
    {
        $this->propertyPlans->removeElement($propertyPlan);
    }

    /**
     * Get propertyImages
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getPropertyImages()
    {
        return $this->propertyImages;
    }


    /**
     * Get propertyPlans
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getPropertyPlans()
    {
        return $this->propertyPlans;
    }

    /**
     * Gets triggered only on insert

     * @ORM\PrePersist
     */
    public function onPrePersist()
    {
        $this->createdAt = new \DateTime("now");
    }

    public function __call($method, $arguments)
    {
        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }
}

The Picture entity

    namespace Ciber\PropertyBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File as File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;

/**
 * PropertyImage
 *
 * @ORM\Table(name="property_image")
 * @ORM\Entity
 * @Vich\Uploadable
 */
class PropertyImage
{
    use ORMBehaviors\Timestampable\Timestampable;
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Ciber\PropertyBundle\Entity\Property", inversedBy="propertyImages")
     * @ORM\JoinColumn(name="property_id", referencedColumnName="id")
     */
    private $property;

    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     *
     * @Vich\UploadableField(mapping="property_images", fileNameProperty="imageName")
     *
     * @var File
     */
    private $imageFile;

    /**
     * @ORM\Column(type="string", length=255)
     *
     * @var string
     */
    private $imageName;

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }


    /**
     *
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
     *
     * @return PropertyImage
     */
    public function setImageFile(File $image = null)
    {
        $this->imageFile = $image;

        if ($image) {
            $this->updatedAt = new \DateTime('now');
        }

        return $this;
    }

    /**
     * @return File
     */
    public function getImageFile()
    {
        return $this->imageFile;
    }

    /**
     * @param string $imageName
     *
     * @return PropertyImage
     */
    public function setImageName($imageName)
    {
        $this->imageName = $imageName;

        return $this;
    }

    /**
     * @return string
     */
    public function getImageName()
    {
        return $this->imageName;
    }

    /**
     * Set property
     *
     * @param \AppBundle\Entity\Property $property
     *
     * @return PropertyImage
     */
    public function setProperty( \Ciber\PropertyBundle\Entity\Property $property = null ) {
        $this->property = $property;

        return $this;
    }

    /**
     * Get property
     *
     * @return \Ciber\PropertyBundle\Entity\Property
     */
    public function getProperty() {
        return $this->property;
    }

    public function __toString()
    {
        return $this->imageName;
    }
}

The Form:

class FlatType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ...

            // Pictures
            ->add('propertyImages', CollectionType::class, array(
                'by_reference' => false,
                'entry_type' => PropertyImageType::class,
                'allow_add'    => true,
                'allow_delete' => true,
            ))
            ->add('propertyPlans', CollectionType::class, array(
                'by_reference' => false,
                'entry_type' => PropertyPlanType::class,
                'allow_add'    => true,
                'allow_delete' => true,
            ))

            ...
        ;
        $builder->add('save', ButtonType::class, array(
            'label' => 'property.form.save'
        ));
    }



    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Ciber\PropertyBundle\Entity\Property',
        ));
    }
}

Fun fact:

if ($request->isMethod('POST')) {
        $form->handleRequest($request);
        $property = $form->getData();
        dump($property); exit();
...

When I dump out $property - after submitting 2 pictures at the same time- , i got this:

elements: array:2 [▼
0 => PropertyImage {#3937 ▼
  -id: 10
  -isDefault: false
  -title: null
  -property: Property {#2799}
  -imageFile: null
  -imageName: "5939c65692d47.jpg"
  #createdAt: DateTime {#3933 ▶}
  #updatedAt: DateTime {#2605 ▶}
}
1 => PropertyImage {#4225 ▼
  -id: null
  -isDefault: false
  -title: null
  -property: Property {#2799}
  -imageFile: UploadedFile {#14 ▶}
  -imageName: null
  #createdAt: null
  #updatedAt: DateTime {#4224 ▶}
}
]

I really dont have idea what happened here (Of course I do have more fields, like title, which is - I think - not related for the issue. However as you see I also have a "PropertyPlans ArrayCollection, its do the same issue. Only the first one persist properly)

EDIT. Here is my Controller:

/**
 * @Route("/properties/{property_category}/new", name="propertycreateindex")
 */
public function propertyCreateIndexAction(Request $request)
{

    $entity = new Property();
    $form = $this->createForm(Ciber\PropertyBundle\Form\FlatType, $entity);

    if ($request->isMethod('POST')) {
        $form->handleRequest($request);
        $property = $form->getData();
        $property->setUser($this->getUser());
        $property->setIsActive(1);
        $em = $this->getDoctrine()->getManager();
        $em->persist($property);
        $em->flush();
        $this->addFlash(
            'success',
            $this->get('translator')->trans('success.message.create_successfull')
        );
        return $this->redirectToRoute('dashboard');
    }

    return $this->render(view.html.twig, array(
        'form' => $form->createView(),
        'property' => $entity
    ));

}

And this is the PropertyImageType.php

namespace Ciber\PropertyBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;

class PropertyImageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('isDefault')
            ->add('title', TextType::class, array(
                'required' => false
            ))
            ->add('imageFile', VichImageType::class, array(
                'required' => false,
                'allow_delete' => true, // not mandatory, default is true
                'download_link' => false //  default is true
            ));

    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Ciber\PropertyBundle\Entity\PropertyImage',
        ));
    }
}
  • Where is the controller, command or whatnot where it gets persisted? It's strange in the dump it shows the first as an entity already, but the second is an `UploadedFile`. Was this editing and the image already existed? – Jared Farrish Jun 08 '17 at 22:32
  • I have updated the post. No, both images are newly uploaded. The second one has the second images data on the UploadedFile node. – Tóth Arnold Jun 08 '17 at 22:49
  • And you're not pre-uploading them with some other tool in the browser? You don't think that's odd, the first one gets saved, the second one (which shows as only an UploadedFile) doesn't get saved? I would look at why that is. The first one, in that case, should also show as uploaded just then. – Jared Farrish Jun 08 '17 at 23:09
  • In any event, you should probably be iterating over those `PropertyImage`s in that collection when uploaded, handle the file itself, then persist and update your entity before you save. I don't see any code that specifically transforms an `UploadedFile` to something that can be persisted like the first row. – Jared Farrish Jun 08 '17 at 23:11
  • As the symfony document: [link](https://symfony.com/doc/current/form/form_collections.html) _With these two changes, when the form is submitted, each new Tag object is added to the Task class by calling the addTag() method. Before this change, they were added internally by the form by calling $task->getTags()->add($tag). That was just fine, but forcing the use of the "adder" method makes handling these new Tag objects easier ._ – Tóth Arnold Jun 09 '17 at 08:50

1 Answers1

0

Sorry, my bad. After I slept several hours, I re-tested. I found, I was wrong. Not the first, but the last image kept. From here, it was an easy way to figure out, i missed one line from the tutorial.

newinputs.each(function(){
    $(this).attr('id', $(this).attr('id').replace(/__name__/g, $randomId));
    $(this).attr('name', $(this).attr('name').replace(/__name__/g, $randomId));
});

The CollectionType default "property_name" is name which you have to modify with javascript after you created a new embeded from from the form.prototype You have to change name in the newly created fields.