Web hints Navbar toggle

Image upload with Symfony

Uploading images to your website is a common task, that's why it shouldn't be too hard to implement.

The way we want to upload images is to connect the image to an Entity, the reason for this is so the image can be linked to any Entity using Doctrine Relations.

Entity and file uploader

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 name of the file will be stored.

Create a file uploader

The upload directory is passed as an argument within services.yml.

App\Service\FileUploader:  
    arguments: 
        $targetDirectory: '%image_directory%'

The file uploader will eventually automate the uploading process, receives the uploaded file and moves it to the assigned directory.

You can steal the code from here: File upload gist

To avoid manually triggering the file uploader, we will use Event Listeners to trigger this class.

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.

Register your event listener inside services.yml

 App\EventListener\ImageUploadListener:  
 tags:  
     - { name: doctrine.event_listener, event: prePersist }  
     - { name: doctrine.event_listener, event: preUpdate }

Source code: view gist

How to use it the Image entity

Almost there! Create a relation between your Image class and custom Entity, or you can just use it on its own.

Standalone

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

This form type will properly transform the image to string and vice versa, plus you can use this form in your own Form Types.

Now use it in your controller:

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

On validation, the image entity will be persisted and the image file will be uploaded to the directory set in your parameters.

Use as a relation to your custom entity

Create a relation using Doctrine ManyToOne, the type of relation does not matter, you can use any relation Doctrine supports.

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

Add the image type to your own Form Type of the related entity.

$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!

Conclusion

Now you have full control over your image, you can add multiple properties such as an alt text or custom formats and establish a relation with any entity available in your application.

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

Be sure to check a fully working example @Github

Have any questions about this article?