Symfony

Image for Easy image uploading with Symfony 4 (without bundles)

Image uploading with Symfony 4 using Doctrine's EventListeners. As soon as an Entity gets persisted the image will be uploaded.

For you ease of use, I included all code snippets in a gist. view 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:

App\Service\FileUploader:  
    arguments: 
        $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 everytime you use, I created an image upload listener. It will trigger the uploadFile function everytime the entity is created or updated.

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

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

Source code: view gist

How to use it

You are almost there! To actually use the Image now you’ll have to add it to your custom Entity or use it on itself.

As the image itself

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

This form type will ensure the proper transformation of the image to a 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);  
$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 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 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!

Conclusion

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

Interested in more articles about Symfony?