Search code examples
phpsymfonyvichuploaderbundle

Upload file manually with VichUploaderBundle


I'm trying to upload a file with VichUploaderBundle manually

My entity BookingOrder is linked to an user, got some other fields and have a vichUpload field.

BookingOrder Entity:

<?php

namespace BookingBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use BookingBundle\Entity\Booking;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * BookingOrder
 *
 * @ORM\Table(name="booking_order")
 * @ORM\Entity(repositoryClass="BookingBundle\Repository\BookingOrderRepository")
 * @Vich\Uploadable
 */
class BookingOrder {

    public function __construct() {
        $this->bookings = new ArrayCollection();
        $this->drivingLicenceSize = 0;
        $this->confirmed = false;
        $this->creationDate = new \DateTime();
        $this->updatedAt = new \DateTime();
    }

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

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="creationDate", type="datetime")
     */
    private $creationDate;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="updatedAt", type="datetime")
     */
    private $updatedAt;

    /**
     * 
     * @Vich\UploadableField(mapping="booking_drivinglicence", fileNameProperty="drivingLicence", size="drivingLicenceSize")
     * 
     * @var UploadedFile
     */
    private $drivingLicenceFile;

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

    /**
     * @ORM\Column(type="integer")
     *
     * @var integer
     */
    private $drivingLicenceSize;

    /**
     * @ORM\Column(type="datetime",nullable = true)
     *
     * @var \DateTime
     */
    private $drivingLicenceUpdatedAt;

    /**
     * @ORM\OneToMany(targetEntity="BookingBundle\Entity\Booking", mappedBy="bookingOrder")
     */
    private $bookings;

    /**
     * @var bool
     *
     * @ORM\Column(name="confirmed", type="boolean")
     */
    private $confirmed;

    /**
     * @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="bookingOrders")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;

    /**
     * @var float
     *
     * @ORM\Column(name="totalCost", type="float")
     */
    private $totalCost;

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

    /**
     * Set creationDate
     *
     * @param \DateTime $creationDate
     *
     * @return BookingOrder
     */
    public function setCreationDate($creationDate) {
        $this->creationDate = $creationDate;

        return $this;
    }

    /**
     * Get creationDate
     *
     * @return \DateTime
     */
    public function getCreationDate() {
        return $this->creationDate;
    }

    /**
     * Set updatedAt
     *
     * @param \DateTime $updatedAt
     *
     * @return BookingOrder
     */
    public function setUpdatedAt($updatedAt) {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * Get updatedAt
     *
     * @return \DateTime
     */
    public function getUpdatedAt() {
        return $this->updatedAt;
    }

    function getUser() {
        return $this->user;
    }

    function setUser($user) {
        $this->user = $user;
    }

    function getDrivingLicenceUpdatedAt() {
        return $this->drivingLicenceUpdatedAt;
    }

    function setDrivingLicenceUpdatedAt(\DateTime $drivingLicenceUpdatedAt) {
        $this->drivingLicenceUpdatedAt = $drivingLicenceUpdatedAt;
    }

    /**
     * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
     * of 'UploadedFile' is injected into this setter to trigger the  update. If this
     * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
     * must be able to accept an instance of 'File' as the bundle will inject one here
     * during Doctrine hydration.
     *
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $drivingLicenceFile
     *
     * @return BookingOrder
     */
    public function setDrivingLicenceFile(UploadedFile $drivingLicenceFile = null) {
        $this->drivingLicenceFile = $drivingLicenceFile;
        if ($drivingLicenceFile) {
            $this->drivingLicenceUpdatedAt = new \DateTimeImmutable();
            $this->setDrivingLicenceSize($drivingLicenceFile->getSize());
        }
        return $this;
    }

    /**
     * @return File|null
     */
    public function getDrivingLicenceFile() {
        return $this->drivingLicenceFile;
    }

    /**
     * @param string $drivingLicence
     *
     * @return BookingOrder
     */
    public function setDrivingLicence($drivingLicence) {
        $this->drivingLicence = $drivingLicence;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getDrivingLicence() {
        return $this->drivingLicence;
    }

    /**
     * @param integer $imageSize
     *
     * @return BookingOrder
     */
    public function setDrivingLicenceSize($drivingLicenceSize) {
        $this->drivingLicenceSize = $drivingLicenceSize;
        return $this;
    }

    /**
     * @return integer|null
     */
    public function getDrivingLicenceSize() {
        return $this->drivingLicenceSize;
    }

    public function addBooking(Booking $booking) {
        $booking->setBookingOrder($this);
        // si la resa fait déjà partie de la collection on ne l'ajoute pas
        if (!$this->bookings->contains($booking)) {
            $this->bookings->add($booking);
        }
    }

    public function removeBooking(Booking $booking) {
        // Si la resa  fait déjà parti de la collection on la retire
        if ($this->bookings->contains($booking)) {
            $this->bookings->remove($booking);
            $booking->setBookingOrder(NULL);
        }
    }

    public function getBookings() {
        return $this->bookings;
    }

    public function getConfirmed() {
        return $this->confirmed;
    }

    public function setConfirmed($confirmed) {
        $this->confirmed = $confirmed;
    }

    public function getTotalCost() {
        return $this->totalCost;
    }

    public function setTotalCost($totalCost) {
        $this->totalCost = $totalCost;
    }

}

Controller:

public function basketAction(Request $request) {
        //recuperation des repository necessaire
        $bookingOrderRepository = $this->getDoctrine()->getRepository('BookingBundle:BookingOrder');

        //recuperation du user courant
        $user = $this->getUser();
        //on recupere la liste des reservation non confirmee (dans le panier) du user courant
        $bookingOrder = $bookingOrderRepository->getBookingOrderByUserAndConfirmed($user, false);
        $bookingList = $bookingOrder->getBookings();

        //creation du formulaire de reservation
        $docForm = $this->createFormBuilder()
        ->add('firstName', TextType::class, array(
        'required' => 'true',
        'label' => false,
        'data' => $user->getFirstName()
        ))
        ->add('lastName', TextType::class, array(
        'required' => 'true',
        'label' => false,
        'data' => $user->getLastName()
        ))
        ->add('tel', TextType::class, array(
        'data' => $user->getTel()
        ))
        ->add('persoEmail', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\EmailType'), array(
        'data' => $user->getPersoEmail()
        ))
        ->add('booking_drivinglicence', VichFileType::class, array(
        'label' => false,
        'required' => true
        ))
        ->add('booking_residenceproof', VichFileType::class, array(
        'label' => false,
        'required' => true
        ))
        ->add('booking_idcard', VichFileType::class, array(
        'label' => false,
        'required' => true
        ))
        ->getForm();

        $docForm->handleRequest($request);

        if ($docForm->isSubmitted() && $docForm->isValid()) {
            $tel = $docForm["tel"]->getData();
            $drivingLicence = $docForm["booking_drivinglicence"]->getData();
            $residenceProof = $docForm["booking_residenceproof"]->getData();
            $idCard = $docForm["booking_idcard"]->getData();


            $fileThumbnail = new UploadedFile($drivingLicence(), $drivingLicence->getClientOriginalName(), $drivingLicence->getClientMimeType(), $drivingLicence->getClientSize(), null, true);




            $bookingOrder->setDrivingLicenceFile($fileThumbnail);

            $em = $this->getDoctrine()->getManager();
            $em->persist($bookingOrder);
            $em->flush();

            return $this->redirectToRoute('platform_homepage');
        }


        return $this->render('PlatformBundle:Default:basket.html.twig', array(
                    'bookingList' => $bookingList,
                    'docForm' => $docForm->createView()
                        )
        );

At first i have try without this line :

$fileThumbnail = new UploadedFile(
    $drivingLicence(),
    $drivingLicence-getClientOriginalName(),
    $drivingLicence->getClientMimeType()
    $drivingLicence-getClientSize(), null, true
);

but only with this method:

$bookingOrder->setDrivingLicenceFile($drivingLicence);

In this case, I get no error, but the file is not uploaded even if in database, the updatedDate is well modified.

As I have read on other topics, I need to trigger the Doctrine upload, it's why I'm trying to create a new FileUpload Object with the test field equal at true.


Solution

  • My error was to think that i need to specify a vichUploader field.

    But all i had to do was to create a FormType like this :

    namespace BookingBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use UserBundle\Form\UserBookingOrderType;
    use Symfony\Component\Form\Extension\Core\Type\FileType;
    
    class BookingOrderType extends AbstractType {
    
        public function buildForm(FormBuilderInterface $builder, array $options) {
    
            $builder
                    ->add('user', UserBookingOrderType::class)
                    ->add('drivingLicenceFile', FileType::class, array(
                        "label" => false
                    ))
            ;
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'cascade_validation' => true,
            ));
        }
    
        public function getBlockPrefix() {
            return 'cp_create_booking_order';
        }
    
    }
    

    Call it in my controller :

     $docForm = $this->createForm(BookingOrderType::class, $bookingOrder);
    
            $docForm->handleRequest($request);
    
            if ($docForm->isSubmitted() && $docForm->isValid()) {
    
                $em = $this->getDoctrine()->getManager();
                $em->persist($bookingOrder);
                $em->flush();
    
                return $this->redirectToRoute('platform_homepage');
            }
    

    And, above all, do not forget to clear your cache !!

    Now, everything work well and the upload perform perfectly