Easy image upload with Symfony 4 (without bundles)

60 min to read

For your ease of use, I included all code snippets in a gist. Gists and a fully working example can be found here: Fully working example

The setup

Setting up the Image Entity

Every image will have its own entry inside the database, this way you can always re-use the image or even change its source file without re-linking it to the custom entity.

The Image class contains the following attributes: file, alt, id where alt is optional. The file attribute is where the actual filename will be stored.

Create a File Uploader

The image uploader receives the upload directory as defined in the services.yml file. Defining it in the services.yml like this:

        $targetDirectory: '%image_directory%'

This will automate the uploading process, receiving the uploaded file, moving it to the directory and returning the filename.

You can steal the code from here: File upload gist

Create an Image Upload Listener

To avoid manually uploading the image every time you use, I created an image upload listener. It will trigger the uploadFile function as soon as the Image entity is created or updated.

A common use case of Listeners is to send emails during certain events, learn more about it in How to send an email with Symfony 4.

To ensure this, add this to the services.yml file.

     - { name: doctrine.event_listener, event: prePersist }  
     - { name: doctrine.event_listener, event: preUpdate }

Source code: view gist

How to use it the Image entity

You are almost there! To actually use the Image you’ll have to add it to your custom Entity or use it as a standalone entity


Create an Image form type, as recommended in this gist: Image form type gist

This form type will ensure the proper transformation from image to string and vice versa, plus it will serve as a nested form for all of your custom entities.

Now simply use it in your controller like so:

$image = new Image();  
$form = $this->createForm(ImageType::class, $image);  
if ($form->isSubmitted() && $form->isValid()) {  
  return $this->redirectToRoute('admin_images_index');  
return $this->render('admin/image/add.html.twig', array(  
  'form' => $form->createView()  

On proper validation, it will persist the Image entity and upload the image to the given directory set in your parameters.

Use in custom entity

As the Image Entity is just an entity and not an actual image it’s really easy to use it everywhere and for anything. Meaning adding an image to an entity now is a piece of cake!

* @var Image  
* @ORM\ManyToOne(targetEntity="Image", cascade={"persist"})  
* @ORM\JoinColumn(name="cover_id", referencedColumnName="id")  
private $cover;

Of course, we also need to link it to the form

$builder->add('cover', ImageType::class, array(  
  'label' => 'Cover'  

As soon as the custom entity is saved the life cycle events of the Image Entity will be triggered, resulting in a linked and uploaded an image!


It might look like quite a bit of work, but once this is done you’ve full control over image uploading to your website as they’re now stored in the database and referring to the filename. In case you need to swap an image everywhere you now only need to do it once, and that’s on the image entity itself.

In case you have any questions, feel free to ask me by commenting below!

Be sure to check a fully working example @Github