Множественная загрузка с помощью VichUploaderBundle на Symfony 3

Я пытаюсь разрешить множественную загрузку с помощью пакета VichUploader. В проекте у меня есть класс Театр, которому принадлежит основное изображение, а также ряд вторичных изображений (Коллекция изображений). На самом деле каждое вторичное изображение является ресурсом. Таким образом, Театр имеет один или несколько Ресурсов, и Ресурс связан с одним Театром.

Но когда я пытаюсь создать, я могу получить доступ к своей форме, но у меня возникает ошибка, когда я пытаюсь сохранить:

Expected argument of type "AppBundle\Entity\Resources", "AppBundle\Entity\Theatre" given 

Вот мой класс Theater с деталями только для многократной загрузки:

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
* Theatre
*
* @ORM\Table(name="theatre")
* @ORM\Entity
* @Vich\Uploadable
*/
class Theatre
{

/**
 *  @var ArrayCollection
 * @ORM\OneToMany(targetEntity="Resources", mappedBy="theatre", cascade={"persist", "remove"}, orphanRemoval=true)
 */
private $images;

// ..

/**
 * Constructor
 */
public function __construct()
{
    $this->images = new \Doctrine\Common\Collections\ArrayCollection();
}

// MultiUpload
/**
 * @return ArrayCollection
 */
public function getImages()
{
    return $this->images;
}

/**
 * @param ArrayCollection $pictures
 */
public function setImages($pictures)
{
    $this->images = $pictures;
}

public function getAttachImages()
{
    return null;
}

/**
 * @param array $files
 *
 * @return array
 */
public function setAttachImages(array $files=array())
{
    if (!$files) return [];
    foreach ($files as $file) {
        if (!$file) return [];
        $this->attachImages($file);
    }
    return [];
}

/**
 * @param UploadedFile|null $file
 */
public function attachImages(UploadedFile $file=null)
{
    if (!$file) {
        return;
    }
    $picture = new Resources();
    $picture->setImage($file);
    $this->addImage($picture);
}



/**
 * Add image.
 *
 * @param \AppBundle\Entity\Resources $image
 *
 *
 */
public function addImage(\AppBundle\Entity\Resources $image)
{
    $image->setTheatre($this);
    //$this->images->add($image);
    $this->images[] = $image;

    //return $this;
}

/**
 * Remove image.
 *
 * @param \AppBundle\Entity\Resources $image
 *
 *
 */
public function removeImage(\AppBundle\Entity\Resources $image)
{
    $image->setTheatre(null);
    $this->images->removeElement($image);
   // return $this->images->removeElement($image);
}

Тогда мои ресурсы класса:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
* Resources
*
* @ORM\Table(name="resources")
* @ORM\Entity
* @Vich\Uploadable
*/
class Resources
{

 /**
 * @var Theatre
 * @ORM\ManyToOne(targetEntity="Theatre", inversedBy="images")
 */
private $theatre;

/**
 * @Vich\UploadableField(mapping="uploads_image", fileNameProperty="url")
 * @Assert\File(
 *      mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
 *      mimeTypesMessage = "Please upload a valid valid IMAGE"
 * )
 *
 *
 * @var File $image
 */
protected $image;

/**
 * @ORM\Column(type="string", length=255, name="url")
 *
 * @var array $url
 */
protected $url;


/**
 *
 * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
 *
 * @return Theatre
 */
public function setImage(File $image = null)
{
    $this->image = $image;
}

public function getImage()
{
    return $this->image;
}

// ..

  /**
 * Set theatre.
 *
 * @param \AppBundle\Entity\Theatre $theatre
 *
 * @return Resources
 */
public function setTheatre(\AppBundle\Entity\Theatre $theatre)
{
    $this->theatre = $theatre;

    return $this;
}

/**
 * Get theatre.
 *
 * @return \AppBundle\Entity\Theatre
 */
public function getTheatre()
{
    return $this->theatre;
}

/**
 * Set url.
 *
 * @param string $url
 *
 * @return Resources
 */
public function setUrl($url)
{
    $this->url = $url;

    return $this;
}

/**
 * Get url.
 *
 * @return array
 */
public function getUrl()
{
    return $this->url;
}

Затем я добавляю в билдер:

namespace AppBundle\Form\Collection;

use AppBundle\Entity\Theatre;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Vich\UploaderBundle\Form\Type\VichFileType;


class TheatreImages extends AbstractType{
/**
 * @param FormBuilderInterface $builder
 * @param array                $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('attachImages', FileType::class, ['multiple'=>true, 'required'=>false])
    ;


}

/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => Theatre::class,
    ));
}

/**
 * @return string
 */
public function getName()
{
    return 'app_theatreImages';
}
}

Добавляю свой конфиг с связкой easyAdminBundle:

easy_admin:
   entities:
    Theatre:
        class: AppBundle\Entity\Theatre
        list:
            title: 'Liste des théâtres'
            fields:
                - 'Name'
                - 'adress'
                - 'Metro station'
                - { property: 'Main image', type: 'image',  template: 'theatreFile.html.twig',  base_path: '%app.path.theatre_images%' }

        new:
            title: 'Création théâtre'
            fields:
                - { type: 'section', label: 'Information du théâtre' }
                - {property: 'name', label: 'Nom'}
                - {property: 'number_of_seats', type: 'integer', label: 'Nombre de sièges'}
                - {property: 'about', label: 'description'}
                - { property: 'imageFile', type: 'vich_file', label: 'image', type_options: { required: false}}
                - {property: 'images', type: 'collection', type_options: {entry_type: 'AppBundle\Form\Collection\TheatreImages', by_reference: false}}
                - { type: 'section', label: 'Localisation du théâtre' }
                - {property: 'adress', label: 'adresse'}
                - {property: 'metro_station', label: 'Station de métro'}
                - {property: 'location_coordinates', label: 'Coordonnées'}
        edit:
            title: "Édition théâtre"
            fields:
                - { type: 'section', label: 'Information du théâtre' }
                - {property: 'name', label: 'Nom'}
                - {property: 'number_of_seats', type: 'integer', label: 'Nombre de sièges'}
                - {property: 'about', label: 'description'}
                - { property: 'imageFile', type: 'vich_file', label: 'image', type_options: { required: false}}
                - {property: 'images', type: 'collection', type_options: {entry_type: 'AppBundle\Form\Collection\TheatreImages', by_reference: false}}
                - { type: 'section', label: 'Localisation du théâtre' }
                - {property: 'adress', label: 'adresse'}
                - {property: 'metro_station', label: 'Station de métro'}
                - {property: 'location_coordinates', label: 'Coordonnées'}

Заранее спасибо.


person MrFireFerid    schedule 17.05.2018    source источник
comment
Не могли бы вы связать свой контроллер   -  person Shaithya Kannan    schedule 17.05.2018
comment
@ShaithyaKannan На самом деле я использую этот пакет с EasyAdmin. Я просто добавил конфиг об этом. Я не использовал пользовательский контроллер   -  person MrFireFerid    schedule 17.05.2018


Ответы (2)


Я создал решение для очень полезного пакета [VichUploader], в котором отсутствует функциональность нескольких загрузок, и он работает на каждой версии Symfony, которую я создал на [Symfony] 5.2. Это отношение OneToMany, и оно работает нормально. Поэтому я использовал CollectionType и [VichFileType] в своих пользовательских формах и небольшой трюк в моем контроллере. Вот код и чтобы увидеть весь проект, вы можете найти его в моем GitHub [ссылка] https://github.com/malek-laatiri

Допуск.php

class Admission
{
    /**
     * @ORM\OneToMany(targetEntity=Diplome::class, mappedBy="admission")
     */
    private $diplomes;
    /**
     * @return Collection|Diplome[]
     */
    public function getDiplomes(): Collection
    {
        return $this->diplomes;
    }

    public function addDiplome(Diplome $diplome): self
    {
        if (!$this->diplomes->contains($diplome)) {
            $this->diplomes[] = $diplome;
            $diplome->setAdmission($this);
        }

        return $this;
    }

    public function removeDiplome(Diplome $diplome): self
    {
        if ($this->diplomes->removeElement($diplome)) {
            // set the owning side to null (unless already changed)
            if ($diplome->getAdmission() === $this) {
                $diplome->setAdmission(null);
            }
        }

        return $this;
    }
}

Диплом.php

<?php

namespace App\Entity;

use App\Repository\DiplomeRepository;
use Doctrine\ORM\Mapping as ORM;
use Vich\UploaderBundle\Mapping\Annotation as Vich;


/**
 * @ORM\Entity(repositoryClass=DiplomeRepository::class)
 * @Vich\Uploadable
 */
class Diplome
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=Admission::class, inversedBy="diplomes",cascade={"persist","remove"})
     */
    private $admission;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @Vich\UploadableField(mapping="product_image", fileNameProperty="name")
     * @var File
     */
    private $file;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getAdmission(): ?Admission
    {
        return $this->admission;
    }

    public function setAdmission(?Admission $admission): self
    {
        $this->admission = $admission;

        return $this;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getFile()
    {
        return $this->file;
    }

    public function setFile( $file)
    {
        $this->file = $file;

        return $this;
    }
}

AdmissionType.php

<?php

namespace App\Form;

use App\Entity\Admission;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;

class Admission1Type extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('diplomes', CollectionType::class, [
                'entry_type' => DiplomeType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'required' => false,
                'label'=>false,
                'by_reference' => false,
                'disabled' => false,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Admission::class,
        ]);
    }
}

DiplomeType.php

<?php

namespace App\Form;

use App\Entity\Diplome;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;

class DiplomeType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('file',VichFileType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Diplome::class,
            "allow_extra_fields" => true,
        ]);
    }
}

_form.html.twig

<ul id="diplomes-fields-list"
    data-prototype="{{ form_widget(form.diplomes.vars.prototype)|e }}"
    data-widget-tags="{{ '<li></li>'|e }}"
    data-widget-counter="{{ form.diplomes|length }}">
    {% for emailField in form.diplomes %}
        <li>
            {{ form_errors(emailField) }}
            {{ form_widget(emailField) }}
        </li>
    {% endfor %}
</ul>
<button type="button"
        class="add-another-collection"
        data-list-selector="#diplomes-fields-list">Add another email
</button>

script.js

jQuery(document).ready(function () {
    jQuery('.add-another-collection').click(function (e) {
        var list = $("#diplomes-fields-list");
        var counter = list.data('widget-counter') | list.children().length;
        var newWidget = list.attr('data-prototype');
        newWidget = newWidget.replace(/__name__/g, counter);
        counter++;
        list.data('widget-counter', counter);

        var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
        newElem.appendTo(list);
        newElem.append('<a href="#" class="remove-tag" style="color: darkred">remove</a>');
        $('.remove-tag').click(function(e) {
            e.preventDefault();

            $(this).parent().remove();

        });
    });
});

Контроллер.php

$admission = new Admission();
        $form = $this->createForm(Admission1Type::class, $admission);
        $form->handleRequest($request);
        $entityManager = $this->getDoctrine()->getManager();
        if ($form->isSubmitted() && $form->isValid()) {
            foreach ($form->getData()->getNotes() as $dip){
                $entityManager->persist($dip);
                $admission->addNote($dip);
            }
            $entityManager->persist($admission);
            $entityManager->flush();
person L3xpert    schedule 27.03.2021
comment
Привет, Малек :) У тебя неплохой код, я бы хотел взглянуть на твой репозиторий на github! Ссылку можно отправить? - person user2711783; 28.03.2021

Vihcuploader не поддерживает загрузку нескольких файлов, вам нужно создать собственный тип формы, чтобы справиться с этим, возможно, это решение поможет:

пользовательское решение github

person Jasson Rojas    schedule 21.05.2018