Symfony 3 上使用 VichUploaderBundle 进行“多重上传”

MrF*_*rid 2 file-upload symfony vichuploaderbundle symfony-3.4

我尝试允许使用捆绑包 VichUploader 进行多次上传。在该项目中,我有一个类剧院,它拥有一个主图像,但也有一些辅助图像(图像集合)。实际上,每个辅助图像都是一个资源。\n因此,一个剧院有一个多个资源,一个资源连接到一个剧院。

\n\n

但是当我尝试创建时,我可以访问我的表单,但当我尝试保存时出现错误:

\n\n
Expected argument of type "AppBundle\\Entity\\Resources", "AppBundle\\Entity\\Theatre" given \n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我的班级剧院,仅包含多次上传的详细信息:

\n\n
namespace AppBundle\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Symfony\\Component\\HttpFoundation\\File\\File;\nuse Symfony\\Component\\HttpFoundation\\File\\UploadedFile;\nuse Symfony\\Component\\Validator\\Constraints as Assert;\nuse Symfony\\Component\\Validator\\Context\\ExecutionContextInterface;\nuse Vich\\UploaderBundle\\Mapping\\Annotation as Vich;\n\n/**\n* Theatre\n*\n* @ORM\\Table(name="theatre")\n* @ORM\\Entity\n* @Vich\\Uploadable\n*/\nclass Theatre\n{\n\n/**\n *  @var ArrayCollection\n * @ORM\\OneToMany(targetEntity="Resources", mappedBy="theatre", cascade={"persist", "remove"}, orphanRemoval=true)\n */\nprivate $images;\n\n// ..\n\n/**\n * Constructor\n */\npublic function __construct()\n{\n    $this->images = new \\Doctrine\\Common\\Collections\\ArrayCollection();\n}\n\n// MultiUpload\n/**\n * @return ArrayCollection\n */\npublic function getImages()\n{\n    return $this->images;\n}\n\n/**\n * @param ArrayCollection $pictures\n */\npublic function setImages($pictures)\n{\n    $this->images = $pictures;\n}\n\npublic function getAttachImages()\n{\n    return null;\n}\n\n/**\n * @param array $files\n *\n * @return array\n */\npublic function setAttachImages(array $files=array())\n{\n    if (!$files) return [];\n    foreach ($files as $file) {\n        if (!$file) return [];\n        $this->attachImages($file);\n    }\n    return [];\n}\n\n/**\n * @param UploadedFile|null $file\n */\npublic function attachImages(UploadedFile $file=null)\n{\n    if (!$file) {\n        return;\n    }\n    $picture = new Resources();\n    $picture->setImage($file);\n    $this->addImage($picture);\n}\n\n\n\n/**\n * Add image.\n *\n * @param \\AppBundle\\Entity\\Resources $image\n *\n *\n */\npublic function addImage(\\AppBundle\\Entity\\Resources $image)\n{\n    $image->setTheatre($this);\n    //$this->images->add($image);\n    $this->images[] = $image;\n\n    //return $this;\n}\n\n/**\n * Remove image.\n *\n * @param \\AppBundle\\Entity\\Resources $image\n *\n *\n */\npublic function removeImage(\\AppBundle\\Entity\\Resources $image)\n{\n    $image->setTheatre(null);\n    $this->images->removeElement($image);\n   // return $this->images->removeElement($image);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我的班级资源:

\n\n
namespace AppBundle\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Symfony\\Component\\HttpFoundation\\File\\File;\nuse Symfony\\Component\\HttpFoundation\\File\\UploadedFile;\nuse Symfony\\Component\\Validator\\Constraints as Assert;\nuse Symfony\\Component\\Validator\\Context\\ExecutionContextInterface;\nuse Vich\\UploaderBundle\\Mapping\\Annotation as Vich;\n\n/**\n* Resources\n*\n* @ORM\\Table(name="resources")\n* @ORM\\Entity\n* @Vich\\Uploadable\n*/\nclass Resources\n{\n\n /**\n * @var Theatre\n * @ORM\\ManyToOne(targetEntity="Theatre", inversedBy="images")\n */\nprivate $theatre;\n\n/**\n * @Vich\\UploadableField(mapping="uploads_image", fileNameProperty="url")\n * @Assert\\File(\n *      mimeTypes = {"image/png", "image/jpeg", "image/jpg"},\n *      mimeTypesMessage = "Please upload a valid valid IMAGE"\n * )\n *\n *\n * @var File $image\n */\nprotected $image;\n\n/**\n * @ORM\\Column(type="string", length=255, name="url")\n *\n * @var array $url\n */\nprotected $url;\n\n\n/**\n *\n * @param File|\\Symfony\\Component\\HttpFoundation\\File\\UploadedFile $image\n *\n * @return Theatre\n */\npublic function setImage(File $image = null)\n{\n    $this->image = $image;\n}\n\npublic function getImage()\n{\n    return $this->image;\n}\n\n// ..\n\n  /**\n * Set theatre.\n *\n * @param \\AppBundle\\Entity\\Theatre $theatre\n *\n * @return Resources\n */\npublic function setTheatre(\\AppBundle\\Entity\\Theatre $theatre)\n{\n    $this->theatre = $theatre;\n\n    return $this;\n}\n\n/**\n * Get theatre.\n *\n * @return \\AppBundle\\Entity\\Theatre\n */\npublic function getTheatre()\n{\n    return $this->theatre;\n}\n\n/**\n * Set url.\n *\n * @param string $url\n *\n * @return Resources\n */\npublic function setUrl($url)\n{\n    $this->url = $url;\n\n    return $this;\n}\n\n/**\n * Get url.\n *\n * @return array\n */\npublic function getUrl()\n{\n    return $this->url;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我添加到构建器:

\n\n
namespace AppBundle\\Form\\Collection;\n\nuse AppBundle\\Entity\\Theatre;\nuse Symfony\\Component\\Form\\AbstractType;\nuse Symfony\\Component\\Form\\Extension\\Core\\Type\\FileType;\nuse Symfony\\Component\\Form\\FormBuilderInterface;\nuse Symfony\\Component\\OptionsResolver\\OptionsResolver;\nuse Symfony\\Bridge\\Doctrine\\Form\\Type\\EntityType;\nuse Vich\\UploaderBundle\\Form\\Type\\VichFileType;\n\n\nclass TheatreImages extends AbstractType{\n/**\n * @param FormBuilderInterface $builder\n * @param array                $options\n */\npublic function buildForm(FormBuilderInterface $builder, array $options)\n{\n    $builder\n        ->add(\'attachImages\', FileType::class, [\'multiple\'=>true, \'required\'=>false])\n    ;\n\n\n}\n\n/**\n * @param OptionsResolver $resolver\n */\npublic function configureOptions(OptionsResolver $resolver)\n{\n    $resolver->setDefaults(array(\n        \'data_class\' => Theatre::class,\n    ));\n}\n\n/**\n * @return string\n */\npublic function getName()\n{\n    return \'app_theatreImages\';\n}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我使用捆绑包 easyAdminBundle 添加我的配置:

\n\n
easy_admin:\n   entities:\n    Theatre:\n        class: AppBundle\\Entity\\Theatre\n        list:\n            title: \'Liste des th\xc3\xa9\xc3\xa2tres\'\n            fields:\n                - \'Name\'\n                - \'adress\'\n                - \'Metro station\'\n                - { property: \'Main image\', type: \'image\',  template: \'theatreFile.html.twig\',  base_path: \'%app.path.theatre_images%\' }\n\n        new:\n            title: \'Cr\xc3\xa9ation th\xc3\xa9\xc3\xa2tre\'\n            fields:\n                - { type: \'section\', label: \'Information du th\xc3\xa9\xc3\xa2tre\' }\n                - {property: \'name\', label: \'Nom\'}\n                - {property: \'number_of_seats\', type: \'integer\', label: \'Nombre de si\xc3\xa8ges\'}\n                - {property: \'about\', label: \'description\'}\n                - { property: \'imageFile\', type: \'vich_file\', label: \'image\', type_options: { required: false}}\n                - {property: \'images\', type: \'collection\', type_options: {entry_type: \'AppBundle\\Form\\Collection\\TheatreImages\', by_reference: false}}\n                - { type: \'section\', label: \'Localisation du th\xc3\xa9\xc3\xa2tre\' }\n                - {property: \'adress\', label: \'adresse\'}\n                - {property: \'metro_station\', label: \'Station de m\xc3\xa9tro\'}\n                - {property: \'location_coordinates\', label: \'Coordonn\xc3\xa9es\'}\n        edit:\n            title: "\xc3\x89dition th\xc3\xa9\xc3\xa2tre"\n            fields:\n                - { type: \'section\', label: \'Information du th\xc3\xa9\xc3\xa2tre\' }\n                - {property: \'name\', label: \'Nom\'}\n                - {property: \'number_of_seats\', type: \'integer\', label: \'Nombre de si\xc3\xa8ges\'}\n                - {property: \'about\', label: \'description\'}\n                - { property: \'imageFile\', type: \'vich_file\', label: \'image\', type_options: { required: false}}\n                - {property: \'images\', type: \'collection\', type_options: {entry_type: \'AppBundle\\Form\\Collection\\TheatreImages\', by_reference: false}}\n                - { type: \'section\', label: \'Localisation du th\xc3\xa9\xc3\xa2tre\' }\n                - {property: \'adress\', label: \'adresse\'}\n                - {property: \'metro_station\', label: \'Station de m\xc3\xa9tro\'}\n                - {property: \'location_coordinates\', label: \'Coordonn\xc3\xa9es\'}\n
Run Code Online (Sandbox Code Playgroud)\n\n

提前致谢。

\n

Vin*_*nci 5

我为一个非常有用的包 [VichUploader] 创建了一个解决方案,它缺少多个上传的功能,并且它适用于我在 [Symfony] 5.2 上创建的每个 Symfony 版本。它是 OneToMany 关系并且工作正常。所以我在我的自定义表单中使用了 CollectionType 和 [VichFileType],并在我的控制器中使用了一些小技巧,这里是代码并查看所有项目,您可以在我的GitHub [链接] https://github.com/malek-中找到它拉蒂里

入学.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

文凭.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

入学类型.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,
        ]);
    }
}
Run Code Online (Sandbox Code Playgroud)

文凭类型.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,
        ]);
    }
}
Run Code Online (Sandbox Code Playgroud)

_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>
Run Code Online (Sandbox Code Playgroud)

脚本.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();

        });
    });
});
Run Code Online (Sandbox Code Playgroud)

控制器.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();
Run Code Online (Sandbox Code Playgroud)