Symfony 5. EasyAdmin 3. VichUploader。同时上传多个文件
Posted
技术标签:
【中文标题】Symfony 5. EasyAdmin 3. VichUploader。同时上传多个文件【英文标题】:Symfony 5. EasyAdmin 3. VichUploader. Upload multiple files at the same time 【发布时间】:2021-10-19 08:37:23 【问题描述】:我有一个完美的多文件上传。使用一个“浏览”按钮一次上传一个文件。它基本上是一个可以包含许多图像的 Places 实体。
我正在尝试将其修改为仅通过一个“浏览”窗口一次上传所有文件。使用 Ctrl / shift 选择多个文件。
所以我得到的第一个内部信息是 VichUploader (VichFileType::class
) 不支持多次上传,所以到目前为止我发现只有一个选择是在我的 AttachmentType.php
中将 VichFileType::class
更改为 FileType::class
并添加在选项['multiple' => true]
所以现在我在我的管理面板字段中可以一次选择多个文件。这正是我需要的。但是在我选择了所有需要的文件并单击Create
创建一个新位置后,我得到了错误:Return value of Vich\UploaderBundle\Mapping\PropertyMapping::getFile() must be an instance of Symfony\Component\HttpFoundation\File\File or null, array returned
。似乎 VichUploader 只等待一个文件而不是数组,所以我修改了我的图像实体。
之前:
/**
* @param mixed $imageFile
*/
public function setImageFile($imageFile): void
$this->imageFile = $imageFile;
if ($imageFile)
$this->updatedAt = new \DateTime();
之后:
/**
* @param mixed $imageFile
*/
public function setImageFile($imageFile): void
foreach ($imageFile as $file)
$this->imageFile = $file;
if ($imageFile)
$this->updatedAt = new \DateTime();
之后,错误消失了,但问题是如果我添加多张图片,则只添加数组中的最后一张。
完整代码:
Places.php
/**
* @ORM\OneToMany(targetEntity=Images::class, mappedBy="place", cascade="persist", "remove")
*/
private $images;
public function __construct()
$this->images = new ArrayCollection();
/**
* @return Collection|Images[]
*/
public function getImages(): Collection
return $this->images;
public function addImage(Images $image): self
if (!$this->images->contains($image))
$this->images[] = $image;
$image->setPlace($this);
return $this;
public function removeImage(Images $image): self
if ($this->images->removeElement($image))
// set the owning side to null (unless already changed)
if ($image->getPlace() === $this)
$image->setPlace(null);
return $this;
Images.php
/**
* @ORM\Entity(repositoryClass=ImagesRepository::class)
* @Vich\Uploadable()
*/
class Images
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
/**
* @Vich\UploadableField(mapping="attachments", fileNameProperty="title")
*/
private $imageFile;
/**
* @ORM\Column(type="datetime")
*/
private $updatedAt;
/**
* @ORM\ManyToOne(targetEntity=Places::class, inversedBy="images")
*/
private $place;
/**
* @ORM\ManyToOne(targetEntity=Regions::class, inversedBy="image")
*/
private $region;
public function getTitle(): ?string
return $this->title;
public function setTitle(?string $title): self
$this->title = $title;
return $this;
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
$this->updatedAt = $updatedAt;
return $this;
/**
* @return mixed
*/
public function getUpdatedAt()
return $this->updatedAt;
/**
* @param mixed $imageFile
*/
public function setImageFile($imageFile): void
foreach ($imageFile as $file)
$this->imageFile = $file;
if ($imageFile)
$this->updatedAt = new \DateTime();
/**
* @return mixed
*/
public function getImageFile()
return $this->imageFile;
public function getPlace(): ?Places
return $this->place;
public function setPlace(?Places $place): self
$this->place = $place;
return $this;
public function getRegion(): ?Regions
return $this->region;
public function setRegion(?Regions $region): self
$this->region = $region;
return $this;
AttachmentType.php
class AttachmentType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
$builder
->add('imageFile', FileType::class, [
'multiple' => true
])
->add('updatedAt')
->add('place')
;
public function configureOptions(OptionsResolver $resolver)
$resolver->setDefaults([
'data_class' => Images::class,
]);
【问题讨论】:
嗨,@laneboyandrew!我建议你开始赏金 50 声望点。它可能会吸引知识渊博的用户。 【参考方案1】:通过创建我自己的 Field 类,我能够使用 EasyAdmin3 为画廊上传多张图片。
这是我如何实现这一目标的示例......
<?php
namespace App\Controller\Admin\Fields;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class MultipleImageField implements FieldInterface
use FieldTrait;
public static function new(string $propertyName, ?string $label = null): self
return (new self())
->setProperty($propertyName)
->setFormType(FileType::class)
->setFormTypeOptions([
'multiple' => true,
'data_class' => null,
]);
在我的 Gallery 实体中,我有 2 个字段来处理 imageFile 和 image(只存储文件名)和一个 upload()
函数来处理上传本身。
const UPLOAD_IMAGE_DIRECTORY = 'uploads/images';
/**
* @ORM\Column(type="string", length=255)
* @Assert\Regex(pattern="/[\/.](gif|jpg|jpeg|tiff|png)$/i", message="Please upload a valid image")
*/
private string $image;
/**
* Unmapped property to handle file uploads
*/
private $imageFile;
public function getImage(): ?string
return $this->image;
public function setImage(string $image): self
$this->image = $image;
return $this;
/**
* @return mixed
*/
public function getImageFile()
return $this->imageFile;
/**
* @param mixed $imageFile
*/
public function setImageFile($imageFile): void
$this->imageFile = $imageFile;
public function upload($file)
if(null === $file)
return;
$file->move(
self::UPLOAD_IMAGE_DIRECTORY,
$file->getClientOriginalName()
);
$this->setImage($file->getClientOriginalName());
$this->setImageFile(null);
在此处断言文件类型可能会做得更好..
然后我将这两个字段添加到我的 EasyAdmin CRUDController,一个用于在索引/详细信息上显示实际图像,一个用于表单,以及 updateEntity
和 persistEntity
函数。
<?php
namespace App\Controller\Admin;
use App\Controller\Admin\Fields\MultipleImageField;
use App\Entity\Gallery;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
class GalleryCrudController extends AbstractCrudController
public static function getEntityFqcn(): string
return Gallery::class;
public function configureFilters(Filters $filters): Filters
return $filters
->add('category')
;
public function configureActions(Actions $actions): Actions
return $actions
->add(Crud::PAGE_INDEX, Action::DETAIL)
->add(Crud::PAGE_EDIT, Action::SAVE_AND_ADD_ANOTHER)
;
public function configureFields(string $pageName): iterable
return [
AssociationField::new('category'),
ImageField::new('image')
->setUploadDir('public/uploads/images')
->setBasePath('uploads/images')
->setRequired(false)
->hideOnForm(),
MultipleImageField::new('imageFile')
->setRequired(false)
->onlyOnForms(),
];
/**
* @param EntityManagerInterface $entityManager
* @param Gallery $entityInstance
*/
public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
foreach($entityInstance->getImageFile() as $file)
$gallery = (new Gallery())
->setCategory($entityInstance->getCategory())
->setImage($file->getClientOriginalName())
;
$entityInstance->upload($file);
$entityManager->persist($gallery);
$entityManager->flush();
/**
* @param EntityManagerInterface $entityManager
* @param Gallery $entityInstance
*/
public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
foreach($entityInstance->getImageFile() as $file)
$gallery = (new Gallery())
->setCategory($entityInstance->getCategory())
->setImage($file->getClientOriginalName())
;
$entityInstance->upload($file);
$entityManager->persist($gallery);
$entityManager->flush();
我希望这对尝试使用 EasyAdmin3 做类似事情的人有所帮助
【讨论】:
感谢您的回答。一段时间后,我回到了这个问题。我的问题是我需要在PlacesCrudController
中保存多个图像,而我的Places
实体与Images
具有OneToMany 关联,所以我不能这样做`foreach($entityInstance->getImageFile() as $file)`因为我的文件在另一个实体中。
@laneboyandrew 在这种情况下,每个文件都会作为不同的实体上传。我一次为一个类别上传许多图像。假设我选择了我的类别,上传 10 个文件并单击保存。这将创建 10 个与同一类别相关的库实体。 getImageFile
未映射,因此它仅临时存储在实体中,因此您可以循环遍历它并实际为每个文件创建一个唯一实体。以上是关于Symfony 5. EasyAdmin 3. VichUploader。同时上传多个文件的主要内容,如果未能解决你的问题,请参考以下文章
Symfony 5 easyadmin 3 与 ManyToOne 关系的实体 - 不保存在“多”端
Symfony - EasyAdmin - 从 AssociationField 中忽略添加和删除功能
在 Symfony 5 中使用 composer 安装 EasyAdmin 的问题
EasyAdmin 3.1 CrudControllers Symfony