Symfony 5.EasyAdmin 3.VichUploader.同时上传多个文件



我有一个完美的工作多文件上传。使用一个"浏览器"一次上传一个文件。按钮。它基本上是一个位置实体,可以有许多图像。

我正在尝试修改它,以便通过一个"浏览器"一次上传所有文件。窗口。按Ctrl/shift选择多个文件

所以我得到的第一个里面是VichUploader (VichFileType::class)不支持多次上传,所以到目前为止我发现只有一个选项是在我的AttachmentType.php中将VichFileType::class更改为FileType::class,并添加选项['multiple' => true],所以现在我在我的管理面板字段中有可能一次选择许多文件。这正是我所需要的。但在我选择了所有需要的文件并单击Create创建一个新的地方后,我得到了错误:Return value of VichUploaderBundleMappingPropertyMapping::getFile() must be an instance of SymfonyComponentHttpFoundationFileFile 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

/**
* @ORMOneToMany(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

/**
* @ORMEntity(repositoryClass=ImagesRepository::class)
* @VichUploadable()
*/
class Images
{
/**
* @ORMId
* @ORMGeneratedValue
* @ORMColumn(type="integer")
*/
private $id;
/**
* @ORMColumn(type="string", length=255)
*/
private $title;
/**
* @VichUploadableField(mapping="attachments", fileNameProperty="title")
*/
private $imageFile;
/**
* @ORMColumn(type="datetime")
*/
private $updatedAt;
/**
* @ORMManyToOne(targetEntity=Places::class, inversedBy="images")
*/
private $place;
/**
* @ORMManyToOne(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,
]);
}
}

通过创建自己的Field类,我能够为一个图库获得多个图像,以便与EasyAdmin3一起上传。

下面是我如何做到这一点的一个例子…

<?php
namespace AppControllerAdminFields;
use EasyCorpBundleEasyAdminBundleContractsFieldFieldInterface;
use EasyCorpBundleEasyAdminBundleFieldFieldTrait;
use SymfonyComponentFormExtensionCoreTypeFileType;
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,
]);
}
}

在我的画廊实体中,我有2个字段来处理imageFile和image(只是存储文件名)和一个upload()函数来处理上传本身。

const UPLOAD_IMAGE_DIRECTORY = 'uploads/images';
/**
* @ORMColumn(type="string", length=255)
* @AssertRegex(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中,一个用于显示索引/详细信息上的实际图像,另一个用于显示表单,以及updateEntitypersistEntity函数。

<?php
namespace AppControllerAdmin;
use AppControllerAdminFieldsMultipleImageField;
use AppEntityGallery;
use DoctrineORMEntityManagerInterface;
use EasyCorpBundleEasyAdminBundleConfigAction;
use EasyCorpBundleEasyAdminBundleConfigActions;
use EasyCorpBundleEasyAdminBundleConfigCrud;
use EasyCorpBundleEasyAdminBundleConfigFilters;
use EasyCorpBundleEasyAdminBundleControllerAbstractCrudController;
use EasyCorpBundleEasyAdminBundleFieldAssociationField;
use EasyCorpBundleEasyAdminBundleFieldImageField;
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

VichUploader只支持上传单个文件,因此唯一的解决方案是拥有一个与包含该文件的实体具有OneToMany关系的桥接实体,就像您在这个答案

中看到的那样一个可行的替代方案是创建许多不同的Image实体,而不是尝试在同一图像上放置多个附件。

那么Places的表单生成器应该是这样的

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('images', CollectionType::class, [
'entry_type' => Image::class,
'multiple' => true,
'by_reference' => false
])
;
}

确保在更新表单中的链接实体时传递by_reference => false,以确保调用setter方法。在这种情况下,你可以像以前一样恢复你的更改,它将与VichUploader一起工作。

相反,如果你想直接使用FileType::class, hobyb的答案有一个很好的观点,你将不得不在控制器中手动管理persistEntityupdateEntity事件,但根本不使用VichUploader类。请记住,在处理图像时,实体是唯一保存到数据库的数据,而附加的媒体文件存储在一个(可能是公共的)目录中。因此,如果您的目的是拥有多个AttachmentsImage实体,其中附件是路径列表,您将不得不将字符串数组字段放入图像实体并相应地管理图片的实际存储。

与问题无关,但在Symfony中,您可以使用Timestampable注释来自动管理像这样的updatedAt字段,使用内置功能是最佳实践。

/**
* @var DateTime $updated
*
* @GedmoTimestampable(on="update")
* @ORMColumn(type="datetime")
*/
private $updated;

最新更新