How to Upload Files in Symfony 6
In this tutorial, we are going to see how we can upload files in Symfony 6.
I assume that you have already a fresh Symfony 6 application installed, a Product Model, and ProductController with create and store methods.
Create product entity
So let's add a Product Entity i assume that you already know how to create a Symfony Entity.
//
<?php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\Column(type: 'text')]
private $description;
#[ORM\Column(type: 'integer')]
private $price;
#[ORM\Column(type: 'integer')]
private $quantity;
#[ORM\Column(type: 'string', length: 255)]
private $image;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getPrice(): ?int
{
return $this->price;
}
public function setPrice(int $price): self
{
$this->price = $price;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): self
{
$this->quantity = $quantity;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
}
Create product controller
Let's add a product controller i assume that you already know how to create a Symfony Controller.
The store method checks for data validation and saves the product with the image file.
The file is saved inside the public directory, which is defined using the parameter image_directory.
We will add the image_directory later.
<?php
namespace App\Controller;
use App\Entity\Product;
use App\Form\ProductType;
use App\Repository\ProductRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class ProductController extends AbstractController
{
private $productRepository;
private $entityManager;
public function __construct(
ProductRepository $productRepository,
ManagerRegistry $doctrine)
{
$this->productRepository = $productRepository;
$this->entityManager = $doctrine->getManager();
}
#[Route('/store/product', name: 'product_store')]
public function store(Request $request): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class,$product);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$product = $form->getData();
if($request->files->get('product')['image']){
$image = $request->files->get('product')['image'];
$image_name = time().'_'.$image->getClientOriginalName();
$image->move($this->getParameter('image_directory'),$image_name);
$product->setImage($image_name);
}
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$this->entityManager->persist($product);
// actually executes the queries (i.e. the INSERT query)
$this->entityManager->flush();
$this->addFlash(
'success',
'Your product was saved'
);
return $this->redirectToRoute('home');
}
return $this->renderForm('create.html.twig', [
'form' => $form
]);
}
}
Create product type
Inside the product type, we have the form to create products.
<?php
namespace App\Form;
use App\Entity\Product;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name')
->add('description')
->add('price')
->add('quantity')
->add('image',FileType::class,[
'required' => false,
'mapped' => false,
])
->add('Submit',SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
}
Create product view
To display the create product form we add a new file we give it create.html.twig as a name.
{% extends 'base.html.twig' %}
{% block title %}Create Product{% endblock %}
{% block body %}
<div class="row my-4">
<div class="col-md-8 mx-auto">
<div class="card">
<div class="card-header">
Create new product
</div>
<div class="card-body">
{{form(form)}}
</div>
</div>
</div>
</div>
{% endblock %}
Update services.yaml file
Next step we update the file config/services.yaml, and we create the image_directory parameter that was used in the controller to specify the directory in which the images should be stored.
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
image_directory : '%kernel.project_dir%/public/uploads'
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones