Reputation: 65
im looking for multiple upload in symfony using vichuploadbundle. I also have working uploader but still for only one file. I cant find answers which solve my problem.I also tried many tutorials, but nothing worked for me. In their documentation i couldnt find answer. What should i do?
error:
Expected argument of type "Symfony\Component\HttpFoundation\File\File", "array" given
My entity:
/**
* @ORM\Entity
* @Vich\Uploadable
*/
class Product
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=50, nullable=true)
* @Assert\NotBlank()
* @Assert\Length(
* min=5,
* minMessage="Title is too short!",
* max=50,
* maxMessage="Title is too long!"
* )
*/
private $title;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* @ORM\JoinColumn(nullable=false)
*/
private $author;
/**
* @ORM\Column(type="string", length=150, nullable=true)
* @Assert\NotBlank()
* @Assert\Length(
* min=5,
* minMessage="Perex is too short!",
* max=100,
* maxMessage="Perex is too long!"
* )
*/
private $perex;
/**
* @ORM\Column(type="text", length=500, nullable=true)
* @Assert\NotBlank()
*/
private $text;
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="AppBundle\Entity\ProductPicture", mappedBy="product", cascade={"all"}, orphanRemoval=true)
*/
private $pictures;
public function __construct()
{
$this->pictures = new ArrayCollection();
}
/**
* @return ArrayCollection
*/
public function getPictures()
{
return $this->pictures;
}
/**
* @param ArrayCollection $pictures
*/
public function setPictures($pictures)
{
$this->pictures = $pictures;
}
public function getAttachPictures()
{
return null;
}
public function setAttachPictures(array $files=array())
{
if (!$files) return [];
foreach ($files as $file) {
if (!$file) return [];
$this->attachPicture($file);
}
return [];
}
public function attachPicture(UploadedFile $file=null)
{
if (!$file) {
return;
}
$picture = new ProductPicture();
$picture->setImageFile($file);
$this->addPicture($picture);
}
public function addPicture(ProductPicture $picture)
{
$picture->setProduct($this);
$this->pictures->add($picture);
}
public function removePicture(ProductPicture $picture)
{
$picture->setProduct(null);
$this->pictures->removeElement($picture);
}
}
ProductPicture:
/**
* @ORM\Entity
* @Vich\Uploadable
*/
class ProductPicture
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var Product
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Product", inversedBy="pictures")
*/
private $product;
/**
*
* @Vich\UploadableField(mapping="product_image", fileNameProperty="imageName", size="imageSize")
* @Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
* mimeTypesMessage = "Please upload a valid valid IMAGE"
* )
*
* @var File
*/
private $imageFile;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*
* @var array
*/
private $imageName;
/**
*
* @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* @return Product
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
}
public function getImageFile()
{
return $this->imageFile;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
public function getProduct()
{
return $this->product;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return array
*/
public function getImageName()
{
return $this->imageName;
}
/**
* @param array $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
}
Form:
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('perex')
->add('text')
->add('attachPictures', FileType::class, ['multiple'=>true, 'required'=>false])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
}
Upvotes: 3
Views: 2521
Reputation: 75
Symfony 4 working solution.
Quite more simple. Just tidy your entities, create form and form transformer
// src/Entity/Product.php
/**
* @ORM\Entity
* @Vich\Uploadable
*/
class Product
{
...
/**
* @var Collection|ProductPicture[]
* @ORM\OneToMany(targetEntity="AppBundle\Entity\ProductPicture", mappedBy="product", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $pictures;
public function __construct()
{
$this->pictures = new ArrayCollection();
}
/**
* @return Collection|ProductPicture[]
*/
public function getPictures(): Collection
{
return $this->pictures;
}
public function addPicture(ProductPicture $picture): self
{
if (!$this->pictures->contains($picture) {
$this->pictures->add($picture);
$picture->setProduct($this);
}
return $this;
}
public function removePicture(ProductPicture $picture): self
{
if ($this->pictures->contains($picture) {
$this->pictures->removeElement($picture);
if ($picture->getProduct() === $this) {
$picture->setProduct(null);
}
}
return $this;
}
...
}
// src/Entity/ProductPicture.php
/**
* @ORM\Entity
* @Vich\Uploadable
*/
class ProductPicture
{
...
/**
* @var Product
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Product", inversedBy="pictures")
* @ORM\JoinColumn(nullable=false)
*/
private $product;
/**
*
* @Vich\UploadableField(mapping="product_image", fileNameProperty="imageName", size="imageSize")
* @Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
* mimeTypesMessage = "Please upload a valid valid IMAGE"
* )
*
* @var File
*/
private $imageFile;
...
/**
* @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* @return Product
*/
public function setImageFile(?File $image = null)
{
$this->imageFile = $image;
if (null !== $image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new DateTimeImmutable();
}
}
public function getImageFile()
{
return $this->imageFile;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
public function getProduct()
{
return $this->product;
}
...
}
Then you need to create simple ProductType form:
// src/Form/ProductForm.php
class PictureType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add($builder->create(
'pictures',
FileType::class,
[
'required' => false,
'multiple' => true,
]
)->addModelTransformer(new FilesToPicturesTransformer()))
->add(...);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
and FilesToPicturesTransformer (data transformer):
// src/Form/FilesToPicturesTransformer.php
class FilesToPicturesTransformer implements DataTransformerInterface
{
/**
* {@inheritdoc}
*/
public function transform($value): void
{
// don't need this if you build API
}
/**
* {@inheritdoc}
*/
public function reverseTransform($value): ArrayCollection
{
$pictures = new ArrayCollection();
foreach ($value as $file) {
$picture = (new ProductPicture())
->setImageFile($file);
if (!$pictures->contains($picture)) {
$pictures->add($picture);
}
}
return $pictures;
}
}
Upvotes: 2
Reputation: 66
You should build the file part as an entity
/**
* @ORM\Entity
* @Vich\Uploadable
*/
class ProductPicture
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var Product
* @ORM\ManyToOne(targetEntity="path\to\your\entity\Product", inversedBy="pictures")
*/
private $product;
/**
*
* @Vich\UploadableField(mapping="product_image", fileNameProperty="imageName", size="imageSize")
* @Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
* mimeTypesMessage = "Please upload a valid valid IMAGE"
* )
*
* @var File
*/
private $imageFile;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*
* @var array
*/
private $imageName;
/**
*
* @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* @return Product
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
}
public function getImageFile()
{
return $this->imageFile;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
public function getProduct()
{
return $this->product;
}
}
And in your Product entity...
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="path\to\your\entity\ProductPicture", mappedBy="product", cascade={"all"}, orphanRemoval=true)
*/
private $pictures;
public function __construct()
{
$this->pictures = new ArrayCollection();
}
/**
* @return ArrayCollection
*/
public function getPictures()
{
return $this->pictures;
}
/**
* @param ArrayCollection $pictures
*/
public function setPictures($pictures)
{
$this->pictures = $pictures;
}
public function getAttachPictures()
{
return null;
}
public function setAttachPictures(array $files=array())
{
if (!$files) return [];
foreach ($files as $file) {
if (!$file) return [];
$this->attachPicture($file);
}
return [];
}
public function attachPicture(UploadedFile $file=null)
{
if (!$file) {
return;
}
$picture = new ProductPicture();
$picture->setImageFile($file);
$this->addPicture($picture);
}
public function addPicture(ProductPicture $picture)
{
$picture->setProduct($this);
$this->pictures->add($picture);
}
public function removePicture(ProductPicture $picture)
{
$picture->setProduct(null);
$this->pictures->removeElement($picture);
}
And finally in the form use...
->add('attachPictures', FileType::class, ['multiple'=>true, 'required'=>false])
Upvotes: 1
Reputation: 2302
I can't help you with this upload bundle, but i can show you how it's working in my case in more simple way (using simple requests, not some fancy bundle)
here https://gist.github.com/poznet/e3955a48fea01d8640dafbd966c53a83
you have
This trait it's a little mess (no attachemt size/type check etc ) , but it works.
If you have any questions fell free to ask.
PS in your case if you uploading multiple files you get array , not file class :D
Upvotes: 0