Couverture de l'article Tuto : Symfony3 et DropzoneJS
Retour aux articles

L'agence

WanadevStudio

Tuto : Symfony3 et DropzoneJS

DropzoneJS est une bibliothèque open-source permettant le drag'n'drop de fichiers dans une zone tout en affichant une miniature à l'envoi ainsi qu'une barre de progression. Voyons ensemble comment le câbléer à une architecture Symfony3.

^ Ce tuto a été réalisé avec une version 3.0.* de Symfony. Peut être que dans le futur, la récupération des lignes de code ci-dessous ne marchera plus ;) !

DropzoneJS

DropzoneJS est une biblithèque javascript, rapide à mettre en place, qui combine toutes les fonctionnalités d'un uploader. Très pratique et relativement clé en main, elle fait gagner beaucoup de temps ! Nous avons très rapidement un rendu idéal.

Parmi toute la panoplie des uploaders ou des file managers que nous pouvons trouver en ligne, DropzoneJS est une des bibliothèques que je trouve la plus pratique à utiliser... Voici à quoi s'attendre rapidement :

Aperçu de dropzoneJS

A savoir que toute la doc de DropzoneJS se trouve à cette adresse.

Téléchargements des sources de DropzoneJS

Nous allons faire ça à l'ancienne. Débutez par télécharger les sources :

Je vous invite à les placer dans votre dossier contenant vos ressources publiques, puis à charger vos scripts avec votre méthode préférée. Cependant, en gros, ça devrait jamais vraiment s'éloigner de...

 <script src="./path/to/dropzone.js"></script>

@ À noter que DropzoneJS peut aussi être installée via Bower, "dropzone": "4.3.*"

Câblage de DropzoneJS avec Symfony3

Tout d'abord, vous devez définir dans votre template (par exemple uploader.html.twig) votre zone qui accueillera vos glisser/déposer ou votre invitation à l'upload. Il s'agit dans le cas de DropzoneJS, par défaut, d'une div de classe dropzone.

<div class="dropzone my-dropzone" id="form_snippet_image" action="{{path('ajax_snippet_image_send')}}">
</div>

Ensuite, dans votre Javascript, définissez cette zone comment étant celle de votre dropzone.

//je récupère l'action où sera traité l'upload en PHP
var _actionToDropZone = $("#form_snippet_image").attr('action');

//je définis ma zone de drop grâce à l'ID de ma div citée plus haut.
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#form_snippet_image", { url: _actionToDropZone });

A ce stade, votre div devrait s'être transformée en zone d'accueil de drop de fichier ! Vos fichiers glissés ne sont pas encore uploadés sur votre serveur mais nous pouvons déjà voir que DropzoneJS fait son travail. Par exemple, dans votre Javascript, insérez le code suivant :

myDropzone.on("addedfile", function(file) {
            alert('nouveau fichier reçu');
        });

La petite fonction précédente devrait vous faire afficher un message à chaque fois qu'un fichier est ajouté à votre zone de drop. J'ai ici utilisé l'événement addedfile, mais toute une liste bien pratique est disponible à cette adresse

Uploader les fichiers

Pour que vos images ou documents soient correctement déplacés où vous le souhaitez lorsqu'ils sont déposés dans notre Dropzone, je me suis inspiré de la documentation officielle de Symfony3 sur la gestion des uploads.

Rappelez-vous, dans notre code ci-dessus, j'ai indiqué :

action="{{path('ajax_snippet_image_send')}}"

C'est dans l'action associée à la route ajax_snippet_image_send que nous traiterons nos fichiers/images. A chaque ajout d'un document dans la zone de drop, DropzoneJS enverra ce fichier sur la route définie juste au dessus.

Création de l'entité des documents

Si vous n'avez pas d'entité liée à vos médias/documents, vous êtes invités à le faire. C'est très pratique pour manager tout ça par la suite (ré-utilisation de fichiers anciennements uploadé, par exemple !).

Nous créons donc une entité Document.php ( toujours fortement inspiré de la doc officielle )

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * @ORM\Entity
 * @ORM\Table(name="document")
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity(repositoryClass="AppBundle\Repository\DocumentRepository")
 */
class Document
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    public $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank
     */
    public $name;

    /**
     * @Assert\File(maxSize="6000000")
     */
    private $file;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    public $path;

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
         $this->file = $file;
        // check if we have an old image path
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    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()
    {
        // la propriété « file » peut être vide si le champ n'est pas requis
        if (null === $this->file) {
            return;
        }

        if ($this->path != $this->file->getClientOriginalName()) {
            $this->path = $this->file->getClientOriginalName();
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        // la propriété « file » peut être vide si le champ n'est pas requis
        if (null === $this->file) {
            return;
        }

        $file_name = $this->file->getClientOriginalName();

        // la méthode « move » prend comme arguments le répertoire cible et
        // le nom de fichier cible où le fichier doit être déplacé
        if (!file_exists($this->getUploadRootDir())) {
            mkdir($this->getUploadRootDir(), 0775, true);
        }
        $this->file->move(
            $this->getUploadRootDir(), $file_name
        );
        $this->file = null;
    }

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

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Document
     */
    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 Document
     */
    public function setPath($path)
    {
        $this->path = $path;

        return $this;
    }

    /**
     * Get path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }
}

Votre entité est créée, un petit php bin/console doctrine:schema:update --force pour synchroniser votre base de données, et on est bon pour la suite !

@ Le dossier dans lequel seront transférées mes images est défini dans la méthode getUploadDir de l'entité Document.php. C'est à dire : 'uploads/documents'. Vous n'êtes pas obligés de créer ce dossier au préalable, un test d'existence est présent dans la méthode upload() de l'entité Document.php.

Création d'un controller de gestion du document envoyé par DropzoneJS

Voici l'action qui, appelée par DropzoneJS, traitera l'envoi de document.

    /**
     *
     * @Method({"GET", "POST"})
     * @Route("/ajax/snippet/image/send", name="ajax_snippet_image_send")
     */
    public function ajaxSnippetImageSendAction(Request $request)
    {
        $em = $this->container->get("doctrine.orm.default_entity_manager");

        $document = new Document();
        $media = $request->files->get('file');

        $document->setFile($media);
        $document->setPath($media->getPathName());
        $document->setName($media->getClientOriginalName());
        $document->upload();
        $em->persist($document);
        $em->flush();

        //infos sur le document envoyé
        //var_dump($request->files->get('file'));die;
        return new JsonResponse(array('success' => true));
    }

et voilà !

Et voilà, votre uploader est terminé ! Il est sommaire, mais il fonctionne ! Vous êtes invités à sécuriser ou à améliorer toute cette interface grâce à toute la panoplie d'événements et de méthodes que possède DropzoneJS !

N'hésitez pas à me faire vos retours d'utilisations, d'expériences (ou même à me remonter des erreurs s'il y en a ;) !)

Enfin, vous êtes invités à nous interpeller sur notre Twitter @agence_wanadev ou en commentaire ci-dessous !

  • Couverture de l'article #2 - Module Federation et Vue à l'ère de l'IA : retour de la MadVue 2026
    #2 - Module Federation et Vue à l'ère de l'IA : retour de la MadVue 2026

    Il y a 3 mois

    Voici la seconde partie de mon retour sur la MadVue 2026, la conférence Vue.js qui s'est tenue à Madrid le 22 mai 2026. Si vous avez manqué le début, la première partie est juste ici :

    Après les fondations de l'application Vue moderne, on change d'échelle et d'époque. Dans cet article, on va voir comment partager des composants entre plusieurs applications Vue avec Module Federation, puis trois talks consacrés à Vue et l'intelligence artificielle : faire générer une interface en direct par un LLM, préparer son projet Vue pour les agents, et enfin apprendre le frontend à l'ère de l'IA.

    C'est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite
  • Couverture de l'article #1 - Temps réel, URLs typées, formulaires et toolchain : retour de la MadVue 2026
    #1 - Temps réel, URLs typées, formulaires et toolchain : retour de la MadVue 2026

    Il y a 3 mois

    Le 22 mai 2026, je me suis rendu à Madrid pour la MadVue 2026, la conférence espagnole dédiée à l'écosystème Vue.js. Une journée dense, neuf talks, et une tendance de fond très claire : entre une grosse moitié orientée IA et expérience développeur et une autre consacrée aux fondations qui rendent nos applications soutenables.

    Dans cette série de deux articles, je reviens sur l'ensemble des talks. En les lisant, vous aurez l'impression d'y avoir assisté.

    Cet article est le premier des deux, vous pourrez retrouver chaque partie de cette série ici :

    Dans ce premier article, on pose les fondations d'une application Vue moderne : on verra comment faire du temps réel proprement, comment en finir avec le parsing manuel des paramètres d'URL, un tout nouveau modèle pour construire ses composants de formulaire, l'avènement d'une toolchain JavaScript unifiée (Oxc, Rolldown), et ce qui nous attend avec Nuxt 5.

    Beaucoup de sujets à traiter, alors c'est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite
  • Couverture de l'article #4 - Construire un DOM Virtuel, les différentes méthodes de rendu et bien écrire ses tests : retour de la Vue.js Amsterdam 2023
    #4 - Construire un DOM Virtuel, les différentes méthodes de rendu et bien écrire ses tests : retour de la Vue.js Amsterdam 2023

    Il y a 3 ans

    Pour la première fois, Wanadev s’est rendu à la plus grosse conférence annuelle autour de Vue.js : la Vue.js Amsterdam 2023. J’ai eu l’honneur d’assister à la 5ème édition, couronnée par le retour d’Evan You, le créateur de Vue.js et Vite, en personne.

    Dans cette série de quatre articles, je vais vous faire revivre ces deux jours de conférences. En les lisant, vous aurez l’impression d’y avoir assisté !

    Cet article est le troisième des quatre, vous pourrez retrouver chaque partie de cette série ici :

    Dans cet article nous allons voir comment construire un Virtual DOM, on parlera des différentes façons de faire du rendu dans le web, de comment écrire des bons tests et plein d’autres choses.

    Beaucoup de sujets à traiter, alors, c’est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite
  • Couverture de l'article #3 - Vue.js en 2023, l'avenir de Vue Router, les patterns avec Pinia  : retour de la Vue.js Amsterdam 2023
    #3 - Vue.js en 2023, l'avenir de Vue Router, les patterns avec Pinia : retour de la Vue.js Amsterdam 2023

    Il y a 3 ans

    Pour la première fois, Wanadev s’est rendu à la plus grosse conférence annuelle autour de Vue.js : la Vue.js Amsterdam 2023. J’ai eu l’honneur d’assister à la 5ème édition, couronnée par le retour d’Evan You, le créateur de Vue.js et Vite, en personne.

    Dans cette série de quatre articles, je vais vous faire revivre ces deux jours de conférences. En les lisant, vous aurez l’impression d’y avoir assisté !

    Cet article est le troisième des quatre, vous pourrez retrouver chaque partie de cette série ici :

    Dans cet article on va parler de Vue en 2023, de patterns Pinia, du futur de vue-router, de l’écosystème Vue et de TypeScript avec Vue.

    Beaucoup de sujets à traiter, alors, c’est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite
  • Couverture de l'article #2 - Nuxt.js, l'art de la composition, accessibilité : retour de la Vue.js Amsterdam 2023
    #2 - Nuxt.js, l'art de la composition, accessibilité : retour de la Vue.js Amsterdam 2023

    Il y a 4 ans

    Pour la première fois, Wanadev s’est rendu à la plus grosse conférence annuelle autour de Vue.js : la Vue.js Amsterdam 2023. J’ai eu l’honneur d’assister à la 5ème édition, couronnée par le retour d’Evan You, le créateur de Vue.js et Vite, en personne.

    Dans cette série de quatre articles, je vais vous faire revivre ces deux jours de conférences. En les lisant, vous aurez l’impression d’y avoir assisté !

    Cet article est le second des quatre, vous pourrez retrouver chaque partie de cette série ici :

    Dans cet article on va parler de Nuxt en 2023, d’un client Mastodon fait avec Nuxt, d’expérience développeur sur Nuxt, de Nitropack, de CSS-in-TS, de comment concevoir ses composants Vue.js, d’accessibilité et de maintenance d’écosystème open-source en entreprise.

    Beaucoup de sujets à traiter, alors c’est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite
  • Couverture de l'article #1 - NPM Package, PlayWright, Google LightHouse : retour de la Vue.js Amsterdam 2023
    #1 - NPM Package, PlayWright, Google LightHouse : retour de la Vue.js Amsterdam 2023

    Il y a 4 ans

    Pour la première fois, Wanadev s’est rendu à la plus grosse conférence annuelle autour de Vue.js : la Vue.js Amsterdam 2023. J’ai eu l’honneur d’assister à la 5ème édition, couronnée par le retour d’Evan You, le créateur de Vue.js et Vite, en personne.

    Dans cette série de quatre articles, je vais vous faire revivre ces deux jours de conférences. En les lisant, vous aurez l’impression d’y avoir assisté !

    Cet article est le premier des quatre, vous pourrez retrouver chaque partie de cette série ici :

    Dans cet article, nous allons parler de Rock’n Roll, voir comment créer son package NPM, découvrir PlayWright pour tester ses applications de bout en bout, parler de commerce international, déconstruire les idées reçues sur Google Lighthouse et aborder le sujet de la performance sous Vue.js.

    Beaucoup de sujets à traiter, alors c’est parti !

    Photo de Louis GOURAIN auteur de l'article

    Louis GOURAIN

    Lire la suite