首页 > 解决方案 > Symfony 5. EasyAdmin 3. VichUploader。同时上传多个文件

问题描述

我有一个完美的多文件上传。使用一个“浏览”按钮一次上传一个文件。它基本上是一个可以包含许多图像的 Places 实体。

我正在尝试修改它以仅通过一个“浏览”窗口一次上传所有文件。使用 Ctrl / shift 选择多个文件。

所以我得到的第一个内部是 VichUploader ( VichFileType::class) 不支持多次上传,所以到目前为止我发现只有一个选项是在我的和添加选项中更改VichFileType::class,所以现在我在我的管理面板字段中可以选择一次有很多文件。这正是我所需要的。但是在我选择了所有需要的文件并单击创建一个新位置后,我得到了错误:. 似乎 VichUploader 只等待一个文件而不是数组,所以我修改了我的图像实体。FileType::classAttachmentType.php['multiple' => true]CreateReturn value of Vich\UploaderBundle\Mapping\PropertyMapping::getFile() must be an instance of Symfony\Component\HttpFoundation\File\File or null, array returned

前:

   /**
    * @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,
        ]);
    }
}

标签: phpsymfonyeasyadminvichuploaderbundle

解决方案


通过创建我自己的 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 做类似事情的人


推荐阅读