Je suis développeur web freelance et propose des formations à Symfony2 ! Contactez-moi pour en discuter.

Permettre à l’utilisateur d’uploader des fichiers est une tâche qui revient régulièrement. Cela peut par exemple permettre à l’utilisateur de mettre en ligne sa photo de profil, ou encore si vous proposez une solution d’hébergement de documents, lui permettre de stocker des fichiers pdf importants.

Dans cet article, nous allons prendre l’exemple de la mise en ligne d’une photo de profil pour vos utilisateurs, et regarder comment implémenter cette fonctionnalité avec Symfony2. Le code utilisé est inspiré de la page du cookbook associée à ce sujet, mais je vais essayer de détailler un peu plus la procédure afin de faciliter sa compréhension.

Mise à jour des entités

Pour faire le lien entre le fichier uploadé et l’entité auquel il est associé, il est nécessaire de mettre à jour l’entité concernée. Notre but est de permettre aux utilisateurs d’uploader une photo de profil. Je vais donc supposer que nous avons déjà une entité User qui se charge de la gestion des utilisateurs (au passage, FOSUserBundle est un très bon bundle pour tout ce qui est création d’utilisateurs et authentification, mais c’est une autre histoire).

Dans notre entité User, nous allons rajouter un champ vers le chemin du fichier, pictureName, ainsi qu’un élément file, qui est présent dans l’entité mais n’est pas persisté : il est nécessaire pour créer l’élément de formulaire, et va nous permettre de manipuler le fichier physique sur le serveur. Une annotation permet de préciser la taille maximale du fichier qu’il est possible de mettre en ligne, ici 500Ko.
Nous allons également rajouter plusieurs méthodes qui serviront par la suite pour connaitre le chemin vers notre image (dont le code vient directement du cookbook déjà évoqué).

Notez bien la méthode getUploadDir : cette méthode va permettre de centraliser le nom du répertoire dans lequel uploader les fichiers, et nous éviter de trimballer une chaine de caractères. Elle veut également dire que tous nos fichiers seront uploadés dans le répertoire /web/uploads/pictures de votre application.

N’oubliez pas le use !


use Symfony\Component\Validator\Constraints as Assert;

class User
{
// ... Code de votre entité d'utilisateur

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

/**
* @Assert\File(maxSize="500k")
*/
public $file;

public function getWebPath()
{
return null === $this->pictureName ? null : $this->getUploadDir().'/'.$this->pictureName;
}

protected function getUploadRootDir()
{
// le chemin absolu du répertoire dans lequel sauvegarder les photos de profil
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}

protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/pictures';
}

public function uploadProfilePicture()
{
// Nous utilisons le nom de fichier original, donc il est dans la pratique
// nécessaire de le nettoyer pour éviter les problèmes de sécurité

// move copie le fichier présent chez le client dans le répertoire indiqué.
$this->file->move($this->getUploadRootDir(), $this->file->getClientOriginalName());

// On sauvegarde le nom de fichier
$this->pictureName = $this->file->getClientOriginalName();

// La propriété file ne servira plus
$this->file = null;
}

// Reste de votre entité
}

On met à jour les accesseurs et la base de données via un app/console generate:entities et app/console doctrine:schema:update –force, et on est prêt pour la suite.

Création de la page d’upload

Pour pouvoir uploader la photo de profil, il est nécessaire d’avoir un formulaire. Nous allons en créer un minimaliste, uniquement pour mettre à jour la photo de profil. Créez un fichier IdentityPictureType.php dans KeiruaProd/ApplicationBundle/Form (enfin, remplacez par le nom de votre bundle), et copiez-y le code suivant :


add('file')
;
}
public function getName()
{
return 'keiruaprod_applicationbundle_identitypicturetype';
}
}

Nous avons créé un formulaire ultra basique, qui ne contient qu’un champ de sélection de fichiers. Faisons un template très simple également. Je l’ai mis dans ressources/views/Profile/preferences.html.twig. N’oubliez pas {{ form_enctype(form) }} :


{% if user.pictureName %}

{% else %}
Pas d'image de profil chargée !
{% endif %}

Charger une image de profil

{{ form_widget(form) }}


Et maintenant, il est temps de créer notre méthode dans un contrôleur pour gérer l’affichage et l’upload.


getUser();

$form = $this->createForm(new IdentityPictureType(), $usr);

if ($this->getRequest()->getMethod() === 'POST') {
$form->bindRequest($this->getRequest());
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();

$usr->uploadProfilePicture();

$em->persist($usr);
$em->flush();

$this->redirect($this->generateUrl('KeiruaProdApplicationBundle_profile'));
}
}

return $this->render('KeiruaProdApplicationBundle:Profile:preferences.html.twig',
array (
'user' => $usr,
'form' => $form->createView()
)
);
}
}

Ce code suppose que vous avez créé une route « KeiruaProdApplicationBundle_profile », et que vous avez associés l’action preferencesAction à une route. Si vous l’exécutez, cela fonctionne : le formulaire, bien que très basique, est affiché, il permet de sélectionner un fichier, et de le mettre en ligne. Lorsque l’utilisateur a chargé une image, grâce à la fonction twig asset(), le chemin correct vers l’image est utilisé et l’image est affichée.

Bien évidemment, cette présentation de l’upload, ainsi que l’implémentation proposée, est très simpliste : si 2 utilisateurs uploadent un fichier du même nom, ils vont utiliser la même photo de profil, et le premier va perdre sa photo de profil. Il est donc nécessaire de rendre propres les noms de fichiers utilisés (par exemple en utilisant l’id de l’entité auxquels ils sont associés). Il peut également être une bonne idée d’intégrer l’upload dans le cycle de vie de l’entité pour ne pas avoir à appeler la méthode uploadProfilePicture lorsque l’on va persister l’entité, mais c’est un autre sujet que je vous invite à étudier dans le cookbook.

7 Réponses à “Permettre l’upload de fichiers avec Symfony2”

  1. Shad0ko Dit:

    Merci, je n’ai pas encore testé ce tuto, mais je l’ai trouvé vraiment très intéressant !
    Continue de nous faire rêver 🙂

  2. baguhan Dit:

    Bonjour et merci pour ce tuto.

    J’ai toutefois un petit problème. Lorsqu’un utilisateur upload une image mais que celle-ci ne répond pas à certaines contraintes (taille par exemple) cette image se retrouve dans le dossier web/uploads. Une idée pour éviter ce genre de souci ?

  3. tperroin Dit:

    Bonjour,

    Super tuto, très bien expliqué et toujours utile !

  4. Gamc Dit:

    Merci pour cette exemple très clair.
    Mais j’ai le problème suivant: je ne sais pas déclarer dans un fichier xxxx.yml ceci, quelqu’un saurait le faire
    /**
    * @Assert\File(maxSize= »500k »)
    */
    public $file;

  5. rim2004m Dit:

    bonjour,
    j’ai suivi ce lien: https://www.youtube.com/watch?v=KGLwZsHhrdQ. Les noms des images uploadées sont enregistrés dans la base de données mais le problème est que mon dossier uploads est vide.
    s’il vous plait c’est quoi la solution??

  6. MortisDux Dit:

    Hello, @Gamc, sur le site de symfony tu trouves la solution:
    http://symfony.com/fr/doc/current/reference/constraints/File.html

    Exemple pour un champ de type File, nommé « bioFile »:
    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author
    properties:
    bioFile:
    – File:
    maxSize: 1024k

  7. boriskw Dit:

    Super tutoriel, ça marche parfaitement, merci encore 🙂

Répondre

Unable to load the Are You a Human PlayThru™. Please contact the site owner to report the problem.