Как правило, во многих руководствах указывается, что вам необходимо извлечь загруженный файл в контроллер и выполнить там некоторую логику для сохранения, обновления или удаления, однако это с Symfony 3 не требуется, если ваша форма основана на сущности Doctrine. Но подождите, В официальных руководствах Symfony уже есть статья о том, как автоматически загружать файл, не модифицируя контроллер сущностью доктрины., почему я должен читать эту статью вместо этого? Ну, официальный учебник по Symfony будет работать так, как описано, проблема в том, что загрузчик будет загружать новый файл каждый раз, когда вы редактируете (обновляете) вашу сущность, что приводит к обширной коллекции файлов, которая вам больше не нужна, потому что ваша сущность только хранит имя одного файла Если вы хотите, чтобы при каждом обновлении формы старый файл удалялся (только если был загружен новый файл) или старый файл оставался (если ничего нового не было загружено), то следующая реализация проведет вас через это.
Мы сделаем учебник с тем же примером брошюра поле и Товар юридическое лицо в официальной статье Symfony.
1. Настройте FormType с помощью FileType
Первое, что вам нужно сделать, это определить поле формы, которое следует использовать для загрузки файла, используя FileType:
Заметка
Важно установить data_class свойство поля с нулевым значением, в противном случае вы столкнетесь с исключением, а именно:
Предполагается, что данные представления формы являются экземпляром класса Symfony \ Component \ HttpFoundation \ File \ File, но являются строкой (n).
когда вы пытаетесь обновить свою сущность с помощью формы.
add('brochure', FileType::class, array(
'label' => 'Brochure (PDF file)',
'data_class' => null,
'required' => false
))
// ...
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Product::class,
));
}
}
2. Создайте и настройте сервис FileUploader
В качестве первого шага для настройки FileUploader класс, чтобы определить глобальный параметр, который определяет, куда файлы брошюры будут загружены. В этом случае мы создадим 2 папки в сети, а именно, для загрузки и внутри загрузки, папку брошюр, где будут храниться загруженные файлы. Вы можете определить этот параметр в config.yml файл вашей заявки:
# app/config/config.yml
parameters:
brochure_files_directory: '%kernel.project_dir%/web/uploads/brochures'
Нам понадобится brochure_files_directory параметр, чтобы ввести его в FileUploader Сервис, который мы создадим, как в следующем. Создайте класс FileUploader со следующим кодом внутри:
targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}
Стандартно мы создали папку Service внутри нашего пакета, поэтому пространство имен проще для понимания, кроме того, оно доступно для нашего приложения. Код не нуждается в модификации, поскольку он реализует очень простой загрузчик файлов, который перемещает предоставленный файл по нужному пути со случайным именем, сгенерированным в одной строке кода. Как только этот файл существует, вам нужно зарегистрировать его в services.yml файл вашего проекта Symfony:
# app/config/services.yml
services:
AppBundle\Service\FileUploader:
arguments:
$targetDir: '%brochure_files_directory%'
Обратите внимание, что мы внедряем созданный параметр brochure_files_directory в качестве значения для $targetDir аргумент. Этот класс FileUploader будет использоваться слушателем доктрины, чтобы легко манипулировать файлами.
3. Создайте и настройте Doctrine Listener.
Как упомянуто в официальном руководстве, для предотвращения добавления дополнительного кода в контроллерах для обработки загрузки файлов вы можете создать слушатель доктрины для автоматической загрузки файла при сохранении объекта. В этом примере мы создали папку EventListener внутри AppBundle, где будет размещен наш класс прослушивателя загрузки. Код слушателя доктрины следует следующей логике:
Заметка
Этот код должен быть изменен в соответствии с полем вашей организации. Это значит, замените получателей и установщиков Брошюры своими.
uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
// Retrieve Form as Entity
$entity = $args->getEntity();
// This logic only works for Product entities
if (!$entity instanceof Product) {
return;
}
// Check which fields were changes
$changes = $args->getEntityChangeSet();
// Declare a variable that will contain the name of the previous file, if exists.
$previousFilename = null;
// Verify if the brochure field was changed
if(array_key_exists("brochure", $changes)){
// Update previous file name
$previousFilename = $changes["brochure"][0];
}
// If no new brochure file was uploaded
if(is_null($entity->getBrochure())){
// Let original filename in the entity
$entity->setBrochure($previousFilename);
// If a new brochure was uploaded in the form
}else{
// If some previous file exist
if(!is_null($previousFilename)){
$pathPreviousFile = $this->uploader->getTargetDir(). "/". $previousFilename;
// Remove it
if(file_exists($pathPreviousFile)){
unlink($pathPreviousFile);
}
}
// Upload new file
$this->uploadFile($entity);
}
}
private function uploadFile($entity)
{
// upload only works for Product entities
if (!$entity instanceof Product) {
return;
}
$file = $entity->getBrochure();
// only upload new files
if ($file instanceof UploadedFile) {
$fileName = $this->uploader->upload($file);
$entity->setBrochure($fileName);
}
}
}
Когда пользователь создает новый регистр для продукта (доступ к форме для создания нового продукта), форма позволяет ему загрузить файл в поле брошюры. Это, если предоставлено, будет автоматически сохранено в /uploads/brochures каталог со случайным именем файла (имя, которое хранится в базе данных в поле брошюры), если не загружен какой-либо файл, поле будет пустым. Когда пользователь редактирует продукт, если кто-то уже загрузил файл брошюры и пользователь обновляет продукт, не загружая новый файл, тогда старый файл будет сохранен и ничего не произойдет, однако если пользователь загрузит новый файл, то старый удаляется, а новый сохраняется (также обновляется поле брошюры).
Наконец, вам нужно зарегистрировать слушателя доктрины в services.yml файл вашего проекта:
# app/config/services.yml
services:
AppBundle\EventListener\BrochureUploadListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
Сохраните изменения, очистите кеш и протестируйте свою форму.