phpformssymfonyfile-uploadvichuploaderbundle

How to upload multiple files with vich uploader in Symfony 5


Expected argument of type "AppBundle\Entity\File", "AppBundle\Entity" given while trying to upload multiple files using vichuploader in formtype of collectionType


Solution

  • I have created a solution for a very useful bundle [VichUploader] which is missing the functionality of multiple uploads and it works on every Symfony version, I have created on [Symfony] 5.2. It's on OneToMany relation and it works fine. So I have used CollectionType and [VichFileType] in my custom forms and little trick in my controller, Here the code and to see all the project, you can find it in my GitHub [a link] https://github.com/malek-laatiri

    Admission.php

    class Admission
    {
        /**
         * @ORM\OneToMany(targetEntity=Diplome::class, mappedBy="admission")
         */
        private $diplomes;
        /**
         * @return Collection|Diplome[]
         */
        public function getDiplomes(): Collection
        {
            return $this->diplomes;
        }
    
        public function addDiplome(Diplome $diplome): self
        {
            if (!$this->diplomes->contains($diplome)) {
                $this->diplomes[] = $diplome;
                $diplome->setAdmission($this);
            }
    
            return $this;
        }
    
        public function removeDiplome(Diplome $diplome): self
        {
            if ($this->diplomes->removeElement($diplome)) {
                // set the owning side to null (unless already changed)
                if ($diplome->getAdmission() === $this) {
                    $diplome->setAdmission(null);
                }
            }
    
            return $this;
        }
    }
    

    Diplome.php

    <?php
    
    namespace App\Entity;
    
    use App\Repository\DiplomeRepository;
    use Doctrine\ORM\Mapping as ORM;
    use Vich\UploaderBundle\Mapping\Annotation as Vich;
    
    
    /**
     * @ORM\Entity(repositoryClass=DiplomeRepository::class)
     * @Vich\Uploadable
     */
    class Diplome
    {
        /**
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\ManyToOne(targetEntity=Admission::class, inversedBy="diplomes",cascade={"persist","remove"})
         */
        private $admission;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $name;
    
        /**
         * @Vich\UploadableField(mapping="product_image", fileNameProperty="name")
         * @var File
         */
        private $file;
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getAdmission(): ?Admission
        {
            return $this->admission;
        }
    
        public function setAdmission(?Admission $admission): self
        {
            $this->admission = $admission;
    
            return $this;
        }
    
        public function getName(): ?string
        {
            return $this->name;
        }
    
        public function setName(string $name): self
        {
            $this->name = $name;
    
            return $this;
        }
    
        public function getFile()
        {
            return $this->file;
        }
    
        public function setFile( $file)
        {
            $this->file = $file;
    
            return $this;
        }
    }
    

    AdmissionType.php

    <?php
    
    namespace App\Form;
    
    use App\Entity\Admission;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type\CollectionType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use Vich\UploaderBundle\Form\Type\VichFileType;
    
    class Admission1Type extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('diplomes', CollectionType::class, [
                    'entry_type' => DiplomeType::class,
                    'allow_add' => true,
                    'allow_delete' => true,
                    'required' => false,
                    'label'=>false,
                    'by_reference' => false,
                    'disabled' => false,
                ]);
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => Admission::class,
            ]);
        }
    }
    

    DiplomeType.php

    <?php
    
    namespace App\Form;
    
    use App\Entity\Diplome;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use Vich\UploaderBundle\Form\Type\VichFileType;
    
    class DiplomeType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('file',VichFileType::class)
            ;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => Diplome::class,
                "allow_extra_fields" => true,
            ]);
        }
    }
    

    _form.html.twig

    <ul id="diplomes-fields-list"
        data-prototype="{{ form_widget(form.diplomes.vars.prototype)|e }}"
        data-widget-tags="{{ '<li></li>'|e }}"
        data-widget-counter="{{ form.diplomes|length }}">
        {% for emailField in form.diplomes %}
            <li>
                {{ form_errors(emailField) }}
                {{ form_widget(emailField) }}
            </li>
        {% endfor %}
    </ul>
    <button type="button"
            class="add-another-collection"
            data-list-selector="#diplomes-fields-list">Add another email
    </button>
    

    script.js

    jQuery(document).ready(function () {
        jQuery('.add-another-collection').click(function (e) {
            var list = $("#diplomes-fields-list");
            var counter = list.data('widget-counter') | list.children().length;
            var newWidget = list.attr('data-prototype');
            newWidget = newWidget.replace(/__name__/g, counter);
            counter++;
            list.data('widget-counter', counter);
    
            var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
            newElem.appendTo(list);
            newElem.append('<a href="#" class="remove-tag" style="color: darkred">remove</a>');
            $('.remove-tag').click(function(e) {
                e.preventDefault();
    
                $(this).parent().remove();
    
            });
        });
    });
    

    Controller.php

    $admission = new Admission();
            $form = $this->createForm(Admission1Type::class, $admission);
            $form->handleRequest($request);
            $entityManager = $this->getDoctrine()->getManager();
            if ($form->isSubmitted() && $form->isValid()) {
                foreach ($form->getData()->getNotes() as $dip){
                    $entityManager->persist($dip);
                    $admission->addNote($dip);
                }
                $entityManager->persist($admission);
                $entityManager->flush();