Last active
October 10, 2020 21:30
-
-
Save acecconato/2867b10b6806f4bd5e815c997d73bfa9 to your computer and use it in GitHub Desktop.
Symfony + EasyAdmin: File upload system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In the constructor, $assetsHelper, FileUploader $fileUploader, $imageUploadPath are autowired by services.yamlwe use | |
*/ | |
# Default configuration for use with the Gist code | |
easy_admin: | |
entities: | |
Image: | |
class: App\Entity\Image | |
form: | |
fields: | |
- { property: 'file', type: 'file' } | |
list: | |
fields: | |
- 'id' | |
- { property: 'web_view', type: 'image', label: 'Thumbnail' } | |
- { property: 'tempFilename', label: 'Filename' } | |
# Filename is null due to the setFilename(null) in the postLoad events. We use tempFilename instead. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Due to best practices, we need to create a new service which is used by the ImageListener. | |
Its role is to upload files and generate an unique filename. | |
NOTA: | |
The default $uploadPath is autowired by argument binding inside services.yaml | |
but in this case, we override it with the $path argument of the upload method. | |
*/ | |
<?php | |
namespace App\Service; | |
use Symfony\Component\HttpFoundation\File\UploadedFile; | |
class FileUploader | |
{ | |
private $targetPath; | |
public function __construct($uploadPath) | |
{ | |
$this->targetPath = $uploadPath; | |
} | |
public function upload(UploadedFile $file, $path = null): string | |
{ | |
if (null === $path) { | |
$path = $this->targetPath; | |
} | |
$filename = $this->generateUniqueName($file); | |
# Fix the missing image/svg mimetype in Symfony | |
if ($file->getMimeType() === "image/svg") { | |
$filename .= "svg"; | |
} | |
$file->move($path, $filename); | |
return $filename; | |
} | |
public function generateUniqueName(UploadedFile $file): string | |
{ | |
return md5(uniqid()).".".$file->guessExtension(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In the constructor, $assetsHelper, FileUploader $fileUploader, $imageUploadPath are autowired by services.yamlwe use | |
*/ | |
/** | |
A simple image entity. | |
*/ | |
<?php | |
namespace App\Entity; | |
use Doctrine\ORM\Mapping as ORM; | |
use Symfony\Component\HttpFoundation\File\File; | |
use Symfony\Component\Validator\Constraints as Asserts; | |
/** | |
* @ORM\Entity(repositoryClass="App\Repository\ImageRepository") | |
* @ORM\EntityListeners({"App\EntityListener\ImageListener"}) | |
* @ORM\HasLifecycleCallbacks() | |
*/ | |
class Image | |
{ | |
/** | |
* @ORM\Id() | |
* @ORM\GeneratedValue() | |
* @ORM\Column(type="integer") | |
*/ | |
private $id; | |
/** | |
* Auto generated by entity listener | |
* @ORM\Column(type="string", length=255) | |
*/ | |
private $filename; | |
/** | |
* Auto generated by entity listener | |
* @var string | |
*/ | |
private $tempFilename; | |
/** | |
* @var File | |
* @Asserts\Image() | |
* @Asserts\NotBlank() | |
*/ | |
private $file; | |
/** | |
* @var string | |
*/ | |
private $webView; | |
public function getId(): ?int | |
{ | |
return $this->id; | |
} | |
public function getFilename(): ?string | |
{ | |
return $this->filename; | |
} | |
public function setFilename(?string $filename): self | |
{ | |
$this->filename = $filename; | |
return $this; | |
} | |
public function getFile(): ?File | |
{ | |
return $this->file; | |
} | |
public function setFile(File $file = null): self | |
{ | |
$this->file = $file; | |
return $this; | |
} | |
public function getTempFilename(): ?string | |
{ | |
return $this->tempFilename; | |
} | |
public function setTempFilename($filename): self | |
{ | |
$this->tempFilename = $filename; | |
return $this; | |
} | |
public function getWebView(): string | |
{ | |
return $this->webView; | |
} | |
public function setWebView(string $webView): self | |
{ | |
$this->webView = $webView; | |
return $this; | |
} | |
public function __toString() | |
{ | |
return $this->getFilename(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
The image listener intercepts all events we need: | |
- postLoad: Define the webview, tempFilename and reset the filename for update's events. | |
- prePersist: Upload the image and create an unique filename. | |
- preUpdate: Update the new image and the filename, remove the old one. | |
- preRemove: Remove the image. | |
In the constructor, $assetsHelper, FileUploader $fileUploader, $imageUploadPath are autowired by services.yaml | |
*/ | |
<?php | |
namespace App\EntityListener; | |
use App\Entity\Image; | |
use App\Service\FileUploader; | |
use Doctrine\ORM\Event\LifecycleEventArgs; | |
use Doctrine\ORM\Event\PreUpdateEventArgs; | |
use Symfony\Component\Asset\Package; | |
class ImageListener | |
{ | |
/** | |
* @var Package | |
*/ | |
private $assetsHelper; | |
/** | |
* @var FileUploader | |
*/ | |
private $fileUploader; | |
private $imageUploadPath; | |
private $image; | |
public function __construct($assetsHelper, FileUploader $fileUploader, $imageUploadPath) | |
{ | |
$this->assetsHelper = $assetsHelper; | |
$this->fileUploader = $fileUploader; | |
$this->imageUploadPath = $imageUploadPath; | |
} | |
public function postLoad(Image $image, LifecycleEventArgs $args): void | |
{ | |
$image->setWebView( | |
$this->assetsHelper->getUrl("/uploads/images/" . $image->getFilename()) | |
); | |
if (!$image->getTempFilename()) { | |
$image->setTempFilename($image->getFilename()); | |
// Enable update events to be triggered | |
$image->setFilename(null); | |
} | |
} | |
public function prePersist(Image $image, LifecycleEventArgs $args) | |
{ | |
$this->image = $args->getEntity(); | |
$filename = $this->fileUploader->upload($this->image->getFile(), $this->imageUploadPath); | |
$this->image->setFilename($filename); | |
} | |
public function preUpdate(Image $image, PreUpdateEventArgs $args) | |
{ | |
$this->image = $args->getEntity(); | |
$filename = $this->fileUploader->upload($this->image->getFile(), $this->imageUploadPath); | |
$this->image->setFilename($filename); | |
unlink($this->imageUploadPath . "/" . $this->image->getTempFilename()); | |
} | |
public function preRemove(Image $image, LifecycleEventArgs $args) | |
{ | |
$this->image = $args->getEntity(); | |
unlink($this->imageUploadPath . "/" . $this->image->getTempFilename()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Here, we define all we need for configuring properly the argument binding, services and listeners | |
parameters: | |
server_upload_default_path: '%kernel.project_dir%/public/uploads' | |
server_upload_image_path: '%kernel.project_dir%/public/uploads/images' | |
services: | |
_defaults: | |
# [...] | |
bind: | |
$uploadPath: '%server_upload_default_path%' | |
$imageUploadPath: '%server_upload_image_path%' | |
$assetsHelper: '@assets.packages' | |
# [...] | |
app.file_uploader: | |
class: App\Service\FileUploader | |
image_listener: | |
class: App\EntityListener\ImageListener | |
tags: | |
- { name: doctrine.orm.entity_listener } | |
# Autowiring | |
App\Service\FileUploader: '@app.file_uploader' | |
Wow i'm so sorry, I didn't upload a file on the new version yet but i guess you can refer to : https://symfony.com/doc/current/controller/upload_file.html
In the example, they've done it directly inside the controller without using Doctrine Event Listener. Let the link of your Gist if you manage to do it, good luck and sorry for the outdated practice!
Edit: That's can help you too with EasyAdmin and controller overriding if necessary : https://symfony.com/doc/2.x/bundles/EasyAdminBundle/book/complex-dynamic-backends.html
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In symfony5 doc about image upload is different. Doctrine event are not recommended in that doc. So how to upload files in easy admin? So it needs to be set on forms.