多文件上传 - symfony3

时间:2017-03-09 12:42:28

标签: symfony multiple-file-upload

Symfony3与PhpStorm.2016.3.2

我成功为一张图片制作文件上传器。但现在我需要让它“多重”

我将向您展示代码及其出现的错误,因为我无法上传多个文件。

这是控制器

public function newAction(Request $request)
{
    $restaurant = new Restaurant();
    $form = $this->createForm(RestaurantType::class, $restaurant);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $em = $this->getDoctrine()->getManager();

        // file upload
        if ($request->files->get('restaurant')['picture'] != null) {

            $file = $request->files->get('restaurant')['picture'];
            $targetDir = $this->getParameter('uploaded_restaurants');
            $filename = $this->get('app.uploader')->upload($file, $targetDir);
            $mediaRestaurant = null;
            if ($restaurant->getId() != null) {
                $mediaRestaurant = $this->getDoctrine()->getRepository('AppBundle:Media')->findPictureByRestaurant($restaurant);
            }
            if ($mediaRestaurant) {
                $media = $mediaRestaurant;
                $fs = new Filesystem();
                try {
                    $fs->remove($this->get('kernel')->getRootDir().'/../web/uploads/restaurants/'.$media->getName());
                } catch (IOException $e) {
                    echo "error";
                }
            } else {
                $media = new Media();
                $media->setCreatedAt(new \DateTime());
            }

            $originalName = $file->getClientOriginalName();
            $media->setName($filename)
                ->setOriginalName($originalName)
                ->setType('img')
                ->setContext('restaurant_picture')
                ->setUpdatedAt(new \DateTime())
            ;
            $media->setRestaurant($restaurant);
            $em->persist($media);
        }

        $restaurant->setIsActivated(false);
        $em->persist($restaurant);
        $em->flush();
    }

    return $this->render('admin/restaurant/new.html.twig', array(
        'restaurant' => $restaurant,
        'form' => $form->createView(),
    ));
}

我的FormType(称为RestaurantType),我在其中添加了文件上传字段(当我将multiple设置为false时,它实际上仅适用于一张图片)

$builder
        ->add('picture', FileType::class, array(
            'label'                 => 'Photos du restaurant',
            'multiple'              => true,
            'required'              => false,
            'mapped'                => false,
            'attr'                  => array(
                'accept'                => '.jpg,.jpeg,.png'),
        ))

上传文件的服务

    class FileUploader
{
    /**
     * @param UploadedFile $file
     * @param $targetDir
     * @return string
     */
    public function upload(UploadedFile $file, $targetDir)
    {
        $fileName = md5(uniqid()).'.'.$file->guessExtension();
        $file->move($targetDir, $fileName);
        return $fileName;
    }
}

MediaRepository中的queryBuilder

public function findPictureByRestaurant(Restaurant $restaurant)
{
    return $this->createQueryBuilder('m')
        ->select('m')
        ->where('m.restaurant = :restaurant')
        ->setParameter('restaurant', $restaurant)
        ->andWhere('m.context = :restaurant_picture')
        ->setParameter('restaurant_picture', 'restaurant_picture')
        ->getQuery()
        ->getOneOrNullResult();
}

在我的show.html.twig中,您可以看到图片

{% if restaurant.medias != null and restaurant.medias.count > 0 and restaurant.medias[0].name != null %}
        <img src="/uploads/restaurants/{{ restaurant.medias[0].name }}">
{% endif %}

我的config.yml上传文件

parameters:
    locale: fr
        uploaded_restaurants: "%kernel.root_dir%/../web/uploads/restaurants"

实体Restaurant很长(抱歉)

    /**
 * Restaurant
 *
 * @ORM\Table(name="restaurant")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\RestaurantRepository")
 */
class Restaurant
{
    /**
     * @var FoodType
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\FoodType", inversedBy="restaurants")
     * @ORM\JoinColumn(name="food_type_id", referencedColumnName="id")
     */
    private $foodType;

    /**
     * @var City
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\City", inversedBy="restaurants")
     * @ORM\JoinColumn(name="city_id", referencedColumnName="id")
     */
    private $city;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Media", mappedBy="restaurant")
     */
    private $medias;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Privatisation", mappedBy="restaurant")
     */
    private $privatisations;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Retrocession", mappedBy="restaurant")
     */
    private $retrocessions;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\OpenedSlot", mappedBy="restaurant")
     */
    private $openedSlots;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\ExceptionSlot", mappedBy="restaurant")
     */
    private $exceptionSlots;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Slot", mappedBy="restaurant")
     */
    private $slots;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Chef", mappedBy="restaurant")
     */
    private $chefs;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->medias = new ArrayCollection();
        $this->privatisations = new ArrayCollection();
        $this->retrocessions = new ArrayCollection();
        $this->openedSlots = new ArrayCollection();
        $this->exceptionSlots = new ArrayCollection();
        $this->slots = new ArrayCollection();
        $this->chefs = new ArrayCollection();
    }

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=120, unique=true)
     * @Assert\Length(
     *     max = 120,
     *     maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *     )
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="slug", type="string", length=255, unique=true)
     * @Gedmo\Slug(fields={"name"})
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $slug;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text")
     */
    private $description;

    /**
     * @var string
     *
     * @ORM\Column(name="webUrl", type="string", length=255, nullable=true)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $webUrl;

    /**
     * @var string
     *
     * @ORM\Column(name="tripAdvisorUrl", type="string", length=255, nullable=true)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $tripAdvisorUrl;

    /**
     * @var string
     *
     * @ORM\Column(name="facebookUrl", type="string", length=255, nullable=true)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $facebookUrl;

    /**
     * @var string
     *
     * @ORM\Column(name="twitterUrl", type="string", length=255, nullable=true)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $twitterUrl;

    /**
     * @var string
     *
     * @ORM\Column(name="instagramUrl", type="string", length=255, nullable=true)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $instagramUrl;

    /**
     * @var string
     *
     * @ORM\Column(name="phone", type="string", length=20)
     * @Assert\Regex(
     *     pattern="#^0[1-9]([-. ]?[0-9]{2}){4}$#",
     *     match=true,
     *     message="Numéro de téléphone invalide."
     *     )
     */
    private $phone;

    /**
     * @var string
     *
     * @ORM\Column(name="phone2", type="string", length=20, nullable=true)
     * @Assert\Regex(
     *     pattern="#^0[1-9]([-. ]?[0-9]{2}){4}$#",
     *     match=true,
     *     message="Numéro de téléphone invalide."
     *     )
     */
    private $phone2;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=255)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $email;

    /**
     * @var float
     *
     * @ORM\Column(name="latitude", type="float")
     * @Assert\Regex(
     *     pattern="/^-?(?:\d+|\d*\.\d+)$/",
     *     match=true,
     *     )
     */
    private $latitude;

    /**
     * @var float
     *
     * @ORM\Column(name="longitude", type="float")
     * @Assert\Regex(
     *     pattern="/^-?(?:\d+|\d*\.\d+)$/",
     *     match=true,
     *     )
     */
    private $longitude;

    /**
     * @var int
     *
     * @ORM\Column(name="stars", type="integer", nullable=true)
     * @Assert\Regex(
     *     pattern="/^[0-9]+$/",
     *     match=true,
     *     message="Ceci n'est pas un chiffre."
     *     )
     */
    private $stars;

    /**
     * @var int
     *
     * @ORM\Column(name="seatNumber", type="integer", nullable=true)
     * @Assert\Regex(
     *     pattern="/^[0-9]+$/",
     *     match=true,
     *     )
     */
    private $seatNumber;

    /**
     * @var float
     *
     * @ORM\Column(name="minPrice", type="float", nullable=true)
     * @Assert\Regex(
     *     pattern="/^-?(?:\d+|\d*\.\d+)$/",
     *     match=true,
     *     )
     */
    private $minPrice;

    /**
     * @var float
     *
     * @ORM\Column(name="maxPrice", type="float", nullable=true)
     * @Assert\Regex(
     *     pattern="/^-?(?:\d+|\d*\.\d+)$/",
     *     match=true,
     *     )
     */
    private $maxPrice;

    /**
     * @var string
     *
     * @ORM\Column(name="address", type="string", length=255)
     * @Assert\Length(
     *    max = 255,
     *    maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
     *    )
     */
    private $address;

    /**
     * @var bool
     *
     * @ORM\Column(name="is_activated", type="boolean")
     */
    private $isActivated = true;

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Restaurant
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set slug
     *
     * @param string $slug
     *
     * @return Restaurant
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /**
     * Set description
     *
     * @param string $description
     *
     * @return Restaurant
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set webUrl
     *
     * @param string $webUrl
     *
     * @return Restaurant
     */
    public function setWebUrl($webUrl)
    {
        $this->webUrl = $webUrl;

        return $this;
    }

    /**
     * Get webUrl
     *
     * @return string
     */
    public function getWebUrl()
    {
        return $this->webUrl;
    }

    /**
     * Set tripAdvisorUrl
     *
     * @param string $tripAdvisorUrl
     *
     * @return Restaurant
     */
    public function setTripAdvisorUrl($tripAdvisorUrl)
    {
        $this->tripAdvisorUrl = $tripAdvisorUrl;

        return $this;
    }

    /**
     * Get tripAdvisorUrl
     *
     * @return string
     */
    public function getTripAdvisorUrl()
    {
        return $this->tripAdvisorUrl;
    }

    /**
     * Set facebookUrl
     *
     * @param string $facebookUrl
     *
     * @return Restaurant
     */
    public function setFacebookUrl($facebookUrl)
    {
        $this->facebookUrl = $facebookUrl;

        return $this;
    }

    /**
     * Get facebookUrl
     *
     * @return string
     */
    public function getFacebookUrl()
    {
        return $this->facebookUrl;
    }

    /**
     * Set twitterUrl
     *
     * @param string $twitterUrl
     *
     * @return Restaurant
     */
    public function setTwitterUrl($twitterUrl)
    {
        $this->twitterUrl = $twitterUrl;

        return $this;
    }

    /**
     * Get twitterUrl
     *
     * @return string
     */
    public function getTwitterUrl()
    {
        return $this->twitterUrl;
    }

    /**
     * Set instagramUrl
     *
     * @param string $instagramUrl
     *
     * @return Restaurant
     */
    public function setInstagramUrl($instagramUrl)
    {
        $this->instagramUrl = $instagramUrl;

        return $this;
    }

    /**
     * Get instagramUrl
     *
     * @return string
     */
    public function getInstagramUrl()
    {
        return $this->instagramUrl;
    }

    /**
     * Set phone
     *
     * @param string $phone
     *
     * @return Restaurant
     */
    public function setPhone($phone)
    {
        $this->phone = $phone;

        return $this;
    }

    /**
     * Get phone
     *
     * @return string
     */
    public function getPhone()
    {
        return $this->phone;
    }

    /**
     * Set phone2
     *
     * @param string $phone2
     *
     * @return Restaurant
     */
    public function setPhone2($phone2)
    {
        $this->phone2 = $phone2;

        return $this;
    }

    /**
     * Get phone2
     *
     * @return string
     */
    public function getPhone2()
    {
        return $this->phone2;
    }

    /**
     * Set email
     *
     * @param string $email
     *
     * @return Restaurant
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set latitude
     *
     * @param float $latitude
     *
     * @return Restaurant
     */
    public function setLatitude($latitude)
    {
        $this->latitude = $latitude;

        return $this;
    }

    /**
     * Get latitude
     *
     * @return float
     */
    public function getLatitude()
    {
        return $this->latitude;
    }

    /**
     * Set longitude
     *
     * @param float $longitude
     *
     * @return Restaurant
     */
    public function setLongitude($longitude)
    {
        $this->longitude = $longitude;

        return $this;
    }

    /**
     * Get longitude
     *
     * @return float
     */
    public function getLongitude()
    {
        return $this->longitude;
    }

    /**
     * Set stars
     *
     * @param integer $stars
     *
     * @return Restaurant
     */
    public function setStars($stars)
    {
        $this->stars = $stars;

        return $this;
    }

    /**
     * Get stars
     *
     * @return int
     */
    public function getStars()
    {
        return $this->stars;
    }

    /**
     * Set seatNumber
     *
     * @param integer $seatNumber
     *
     * @return Restaurant
     */
    public function setSeatNumber($seatNumber)
    {
        $this->seatNumber = $seatNumber;

        return $this;
    }

    /**
     * Get seatNumber
     *
     * @return int
     */
    public function getSeatNumber()
    {
        return $this->seatNumber;
    }

    /**
     * Set minPrice
     *
     * @param float $minPrice
     *
     * @return Restaurant
     */
    public function setMinPrice($minPrice)
    {
        $this->minPrice = $minPrice;

        return $this;
    }

    /**
     * Get minPrice
     *
     * @return float
     */
    public function getMinPrice()
    {
        return $this->minPrice;
    }

    /**
     * Set maxPrice
     *
     * @param float $maxPrice
     *
     * @return Restaurant
     */
    public function setMaxPrice($maxPrice)
    {
        $this->maxPrice = $maxPrice;

        return $this;
    }

    /**
     * Get maxPrice
     *
     * @return float
     */
    public function getMaxPrice()
    {
        return $this->maxPrice;
    }

    /**
     * Set address
     *
     * @param string $address
     *
     * @return Restaurant
     */
    public function setAddress($address)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Get address
     *
     * @return string
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * Add media
     *
     * @param Media $media
     *
     * @return Restaurant
     */
    public function addMedia(Media $media)
    {
        $this->medias[] = $media;

        return $this;
    }

    /**
     * Remove media
     *
     * @param Media $media
     */
    public function removeMedia(Media $media)
    {
        $this->medias->removeElement($media);
    }

    /**
     * Get medias
     *
     * @return ArrayCollection
     */
    public function getMedias()
    {
        return $this->medias;
    }

    /**
     * Set foodType
     *
     * @param FoodType $foodType
     *
     * @return Restaurant
     */
    public function setFoodType(FoodType $foodType = null)
    {
        $this->foodType = $foodType;

        return $this;
    }

    /**
     * Get foodType
     *
     * @return FoodType
     */
    public function getFoodType()
    {
        return $this->foodType;
    }

    /**
     * Add privatisation
     *
     * @param Privatisation $privatisation
     *
     * @return Restaurant
     */
    public function addPrivatisation(Privatisation $privatisation)
    {
        $this->privatisations[] = $privatisation;

        return $this;
    }

    /**
     * Remove privatisation
     *
     * @param Privatisation $privatisation
     */
    public function removePrivatisation(Privatisation $privatisation)
    {
        $this->privatisations->removeElement($privatisation);
    }

    /**
     * Get privatisations
     *
     * @return ArrayCollection
     */
    public function getPrivatisations()
    {
        return $this->privatisations;
    }

    /**
     * Add retrocession
     *
     * @param Retrocession $retrocession
     *
     * @return Restaurant
     */
    public function addRetrocession(Retrocession $retrocession)
    {
        $this->retrocessions[] = $retrocession;

        return $this;
    }

    /**
     * Remove retrocession
     *
     * @param Retrocession $retrocession
     */
    public function removeRetrocession(Retrocession $retrocession)
    {
        $this->retrocessions->removeElement($retrocession);
    }

    /**
     * Get retrocessions
     *
     * @return ArrayCollection
     */
    public function getRetrocessions()
    {
        return $this->retrocessions;
    }

    /**
     * Add openedSlot
     *
     * @param OpenedSlot $openedSlot
     *
     * @return Restaurant
     */
    public function addOpenedSlot(OpenedSlot $openedSlot)
    {
        $this->openedSlots[] = $openedSlot;

        return $this;
    }

    /**
     * Remove openedSlot
     *
     * @param OpenedSlot $openedSlot
     */
    public function removeOpenedSlot(OpenedSlot $openedSlot)
    {
        $this->openedSlots->removeElement($openedSlot);
    }

    /**
     * Get openedSlots
     *
     * @return ArrayCollection
     */
    public function getOpenedSlots()
    {
        return $this->openedSlots;
    }

    /**
     * Add exceptionSlot
     *
     * @param ExceptionSlot $exceptionSlot
     *
     * @return Restaurant
     */
    public function addExceptionSlot(ExceptionSlot $exceptionSlot)
    {
        $this->exceptionSlots[] = $exceptionSlot;

        return $this;
    }

    /**
     * Remove exceptionSlot
     *
     * @param ExceptionSlot $exceptionSlot
     */
    public function removeExceptionSlot(ExceptionSlot $exceptionSlot)
    {
        $this->exceptionSlots->removeElement($exceptionSlot);
    }

    /**
     * Get exceptionSlots
     *
     * @return ArrayCollection
     */
    public function getExceptionSlots()
    {
        return $this->exceptionSlots;
    }

    /**
     * Add slot
     *
     * @param Slot $slot
     *
     * @return Restaurant
     */
    public function addSlot(Slot $slot)
    {
        $this->slots[] = $slot;

        return $this;
    }

    /**
     * Remove slot
     *
     * @param Slot $slot
     */
    public function removeSlot(Slot $slot)
    {
        $this->slots->removeElement($slot);
    }

    /**
     * Get slots
     *
     * @return ArrayCollection
     */
    public function getSlots()
    {
        return $this->slots;
    }

    /**
     * Add chef
     *
     * @param Chef $chef
     *
     * @return Restaurant
     */
    public function addChef(Chef $chef)
    {
        $this->chefs[] = $chef;

        return $this;
    }

    /**
     * Remove chef
     *
     * @param Chef $chef
     */
    public function removeChef(Chef $chef)
    {
        $this->chefs->removeElement($chef);
    }

    /**
     * Get chefs
     *
     * @return ArrayCollection
     */
    public function getChefs()
    {
        return $this->chefs;
    }

    /**
     * Set city
     *
     * @param City $city
     *
     * @return Restaurant
     */
    public function setCity(City $city = null)
    {
        $this->city = $city;

        return $this;
    }

    /**
     * Get city
     *
     * @return City
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * @return bool
     */
    public function getIsActivated()
    {
        return $this->isActivated;
    }

    /**
     * @param bool $isActivated
     */
    public function setIsActivated($isActivated)
    {
        $this->isActivated = $isActivated;
    }
}

以及我上传图片并点击submit按钮

时出现的错误

enter image description here

总结一下,当我在multiple中设置falseFormType时,此代码仅适用于一张图片。但后来我被困在多文件上传中,我无法找到解决方法。有人知道如何处理我给你的代码吗?

谢谢

2 个答案:

答案 0 :(得分:1)

由于您的文件定义接受多次上传,因此您需要修改控制器的上传部分:

// file upload
if ($request->files->get('restaurant')['picture'] != null) {
    $files = $request->files->get('restaurant')['picture'];
    foreach ($files as $file) {
        $targetDir = $this->getParameter('uploaded_restaurants');
        $filename = $this->get('app.uploader')->upload($file, $targetDir);
        $mediaRestaurant = null;
        if ($restaurant->getId() != null) {
            $mediaRestaurant = $this->getDoctrine()->getRepository('AppBundle:Media')->findPictureByRestaurant($restaurant);
        }
        if ($mediaRestaurant) {
            $media = $mediaRestaurant;
            $fs = new Filesystem();
            try {
                $fs->remove($this->get('kernel')->getRootDir().'/../web/uploads/restaurants/'.$media->getName());
            } catch (IOException $e) {
                echo "error";
            }
        } else {
            $media = new Media();
            $media->setCreatedAt(new \DateTime());
        }
        $originalName = $file->getClientOriginalName();
        $media->setName($filename)
            ->setOriginalName($originalName)
            ->setType('img')
            ->setContext('restaurant_picture')
            ->setUpdatedAt(new \DateTime())
            ;
        $media->setRestaurant($restaurant);
        $em->persist($media);
    }
}

通知我已首先从表单上传的数据中获取所有文件,并在foreach循环中运行您的代码。

答案 1 :(得分:1)

你需要在upload方法中处理一个数组,因为它会从表单中获取,而不是单个的UploadedFile对象。

/**
 * @param UploadedFile $file
 * @param $targetDir
 * @return array
 */
public function upload($files, $targetDir)
{
    if(!is_array($files)) {
         $files = (array) $files; // cast to array in case of a form that isn't multiple.
    }
    $filenames = [];
    foreach($files as $file) {
         $filenames[] = md5(uniqid()).'.'.$file->guessExtension();
         $file->move($targetDir, $fileName);
    }

    return $filenames;
}

你需要改变获得返回filename变量的任何内容,因为它现在是一个数组。

另一种方式,在我看来是一个更好的用户体验,就是使用CollectionType并为每个文件渲染一个文件输入框。这是一个简单的例子。

    $builder->add('file_uploads', CollectionType::class, [
        'entry_type'    => FileType::class,
        'entry_options' => [
            'label'        => 'Photos du restaurant',
            'multiple'     => true,
            'required'     => false,
            'mapped'       => false,
            'attr'         => [
                'accept' => '.jpg,.jpeg,.png',
            ],
            'allow_add'    => true,
            'allow_delete' => true,
        ],
        'prototype'     => true,
    ]);

然后,您需要在上传处理程序中处理生成的ArrayCollection。但这为用户提供了更好的界面。

  

我没有对此代码段进行测试,因此您可能需要调试或调整它以适应您正在进行的操作。