
Comment mettre en place un formulaire avec upload d’images en Symfony ?
Tu veux permettre à tes utilisateurs d’envoyer des images via un formulaire Symfony ? Voici un guide complet pour gérer l’upload d’images avec Symfony, en prenant en compte les bonnes pratiques comme la gestion des doublons et la suppression des fichiers inutilisés.
Ce qu’il te faut
- Symfony 6+ installé sur ton projet.
- Doctrine ORM pour gérer tes entités.
- Un formulaire Symfony avec un champ de type file.
- Une classe de service pour gérer l’upload des fichiers.
Étape 1 : Créer l’entité associée
On va d’abord créer une entité Food
qui contiendra les informations sur l’aliment et l’image associée.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
class Food
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank(message: "Le nom est obligatoire")]
private ?string $name = null;
#[ORM\Column(type: 'float')]
#[Assert\Positive(message: "Le prix doit être positif")]
private ?float $price = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $image = null;
public function getId(): ?int { return $this->id; }
public function getName(): ?string { return $this->name; }
public function setName(string $name): static { $this->name = $name; return $this; }
public function getPrice(): ?float { return $this->price; }
public function setPrice(float $price): static { $this->price = $price; return $this; }
public function getImage(): ?string { return $this->image; }
public function setImage(?string $image): static { $this->image = $image; return $this; }
}
Étape 2 : Créer un service pour gérer l’upload
Pour éviter de répéter le code, on va créer une classe FileUploader.php
qui s’occupera du transfert des fichiers et de la gestion des doublons.
<?php
namespace App\Service;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\String\Slugger\AsciiSlugger;
class FileUploader
{
protected $parameterBag;
public function __construct(ParameterBagInterface $parameterBag)
{
$this->parameterBag = $parameterBag;
}
public function upload(UploadedFile $file, string $foodName): string
{
$slugger = new AsciiSlugger();
$safeFilename = $slugger->slug($foodName)->lower();
$extension = $file->guessExtension();
// Générer un nom unique
$uniqueId = time();
$newFileName = $safeFilename . '-' . $uniqueId . '.' . $extension;
$filesDir = $this->parameterBag->get('kernel.project_dir') . "/public/images/aliments";
$file->move($filesDir, $newFileName);
return $newFileName;
}
public function deleteFile(string $fileName): void
{
$filePath = $this->parameterBag->get('kernel.project_dir') . "/public/images/aliments/" . $fileName;
if (file_exists($filePath)) {
unlink($filePath);
}
}
}
🔹 Pourquoi un timestamp ? Pour éviter d’écraser des fichiers ayant le même nom.
🔹 Alternative ? Utiliser md5_file($file->getPathname())
pour générer un hash unique basé sur le contenu de l’image.
Étape 3 : Ajouter le champ image dans le formulaire
On va maintenant modifier le formulaire pour ajouter un champ d’upload d’image.
<?php
namespace App\Form;
use App\Entity\Food;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Image;
class FoodType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, [
'label' => "Nom de l'aliment"
])
->add('price', MoneyType::class)
->add('image', FileType::class, [
'label' => 'Image',
'mapped' => false,
'required' => false,
'constraints' => [
new Image([
'maxSize' => '5M',
'mimeTypes' => ['image/jpeg', 'image/png'],
'mimeTypesMessage' => 'Format d'image non valide (JPG, PNG uniquement)'
])
]
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Food::class,
]);
}
}
Étape 4 : Gérer l’upload dans le contrôleur
Dans le contrôleur, il faut récupérer le fichier envoyé et l’enregistrer.
if ($form->isSubmitted() && $form->isValid()) {
$imageFile = $form->get('image')->getData();
if ($imageFile) {
if ($food->getImage()) {
$fileUploader->deleteFile($food->getImage());
}
$newFilename = $fileUploader->upload($imageFile, $food->getName());
$food->setImage($newFilename);
}
$entityManager->persist($food);
$entityManager->flush();
return $this->redirectToRoute('food_index');
}
Conclusion
🎯 Ce qu’on a fait :
✅ Créé une entité avec un champ image
✅ Fait un service pour gérer l’upload et éviter les doublons
✅ Ajouté un champ image dans le formulaire
✅ Géré l’upload et la suppression des anciennes images
🚀 Prochaine étape ? Ajouter un système de thumbnails ou stocker les images sur un CDN comme Cloudinary !