I have an entity "FicheProduit" in relation OneToMany with my entity "Photo" because a product sheet can have several photos. In my form, I would like to create a multiple upload via VichUploaderBundle but I get this error :
Too few arguments to function Vich\UploaderBundle\Form\Type\VichImageType::__construct(), 0 passed in C:\wamp64\www\blh_wines\vendor\symfony\form\FormRegistry.php on line 92 and at least 3 expected
FicheProduit.php :
<?php
declare(strict_types = 1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class FicheProduit
*
* #package App\Entity
*
* #ORM\Entity
*/
class FicheProduit
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer", options={"unsigned":true}, nullable=false)
*
* #var int
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Photo", mappedBy="ficheProduit", cascade={"persist"})
*
* #Assert\Valid()
*
* #var Photo[]|ArrayCollection
*/
protected $photos;
/**
* FicheProduit constructor.
*/
public function __construct()
{
$this->photos = new ArrayCollection();
}
/**
* #return int
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return Photo[]|ArrayCollection
*/
public function getPhotos()
{
return $this->photos;
}
/**
* #param Photo[]|ArrayCollection $photos
*/
public function setPhotos($photos): void
{
$this->photos = $photos;
}
/**
* Add photo
*
* #param Photo $photo
*
* #return FicheProduit
*/
public function addPhoto(Photo $photo)
{
$photo->setProduct($this);
$this->photos[] = $photo;
return $this;
}
/**
* Remove photo
*
* #param Photo $photo
*/
public function removePhoto(Photo $photo)
{
$this->photos->removeElement($photo);
}
}
Photo.php
<?php
declare(strict_types = 1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #Vich\Uploadable
*/
class Photo
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Vich\UploadableField(mapping="fiche_produit_photo", fileNameProperty="imageName", size="imageSize")
*
* #var File
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
protected $imageName;
/**
* #ORM\Column(type="integer")
*
* #var integer
*/
protected $imageSize;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
protected $updatedAt;
/**
* #ORM\ManyToOne(targetEntity="FicheProduit", inversedBy="photos")
*/
protected $ficheProduit;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*/
public function setImageFile(?File $image = null): void
{
$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();
}
}
/**
* #return null|File
*/
public function getImageFile(): ?File
{
return $this->imageFile;
}
/**
* #param null|string $imageName
*/
public function setImageName(?string $imageName): void
{
$this->imageName = $imageName;
}
/**
* #return null|string
*/
public function getImageName(): ?string
{
return $this->imageName;
}
/**
* #param int|null $imageSize
*/
public function setImageSize(?int $imageSize): void
{
$this->imageSize = $imageSize;
}
/**
* #return int|null
*/
public function getImageSize(): ?int
{
return $this->imageSize;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id): void
{
$this->id = $id;
}
/**
* #return \DateTime
*/
public function getUpdatedAt(): ?\DateTime
{
return $this->updatedAt;
}
/**
* #param \DateTime $updatedAt
*/
public function setUpdatedAt(\DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
/**
* #return mixed
*/
public function getFicheProduit()
{
return $this->ficheProduit;
}
/**
* #param mixed $ficheProduit
*/
public function setFicheProduit($ficheProduit): void
{
$this->ficheProduit = $ficheProduit;
}
}
vich_uploader.yaml :
vich_uploader:
db_driver: orm
mappings:
fiche_produit_photo:
uri_prefix: /images/products
upload_destination: '%kernel.project_dir%/public/images/products'
inject_on_load: false
delete_on_update: true
delete_on_remove: true
easy_admin.yaml :
easy_admin:
entities:
FicheProduit:
class: App\Entity\FicheProduit
list:
title: 'Fiche produit'
form:
title: 'Ajouter une fiche produit'
fields:
- { property: 'photos', type: 'collection', type_options: { entry_type: 'App\Form\PhotoType', by_reference: false }}
PhotoType.php :
<?php
declare(strict_types = 1);
namespace App\Form;
use App\Entity\Photo;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;
/**
* Class PhotoType
*
* #package App\Form
*/
class PhotoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageName')
->add('imageFile', VichImageType::class, [
'required' => false,
'allow_delete' => true,
'download_label' => '...',
'download_uri' => true,
'image_uri' => true,
'imagine_pattern' => '...',
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Photo::class,
]);
}
}
I tried to replace this:
->add('imageFile', VichImageType::class, [
'required' => false,
'allow_delete' => true,
'download_label' => '...',
'download_uri' => true,
'image_uri' => true,
'imagine_pattern' => '...',
])
By this :
->add('imageFile', CollectionType::class, ['entry_type' => VichImageType::class])
I have no more errors, but my file input isn't displayed and I have a "0" next to it.
I followed this documentation but it did not help me much :
https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/form/vich_image_type.md
https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md
Well i never used EasyAdmin myself but I think you need an admin definition for the Photo entity:
easy_admin:
entities:
Photo:
# ...
form:
fields:
- { property: 'imageFile', type: 'vich_image' }
I think you don't need the PhotoType formType for the EasyAdmin interface.
If you follow this tutorial: https://symfony.com/doc/master/bundles/EasyAdminBundle/integration/vichuploaderbundle.html#uploading-the-images-in-the-edit-and-new-views
You will see there is no FormType in the example.
Related
I'm using EasyAdminBundle in Symfony 4 and I would like to create a multiple image upload in my form with VichUploaderBundle (or without), but I can't find any updated documentation for Symfony 4, I do not know what to do to make it work.
I created a Photo entity and a Product entity with the relationship many-to-many unidirectional :
Product.php :
<?php
declare(strict_types = 1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Product
*
* #package App\Entity
*
* #ORM\Entity
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer", options={"unsigned":true}, nullable=false)
*
* #var int
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Photo", cascade={"persist"})
*
* #var Photo[]|ArrayCollection
*/
protected $photos;
/**
* Product constructor.
*/
public function __construct()
{
$this->photos = new ArrayCollection();
}
Photo.php :
<?php
declare(strict_types = 1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Photo
*
* #ORM\Entity()
* #Vich\Uploadable
*/
class Photo
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #var int
*/
protected $id;
/**
* #ORM\Column(name="name", type="string", length=255)
*
* #var string
*/
protected $name;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
protected $photo;
/**
* #Vich\UploadableField(mapping="photos", fileNameProperty="photo")
*
* #var File
*/
protected $photoFile;
/**
* #ORM\Column(type="datetime", length=255)
*
* #var \DateTime
*/
protected $updatedAt;
/**
* #return int
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getName(): ?string
{
return $this->name;
}
/**
* #param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* #param File|null $photo
* #return Photo
*/
public function setPhotoFile(File $photo = null)
{
$this->photoFile = $photo;
if ($photo) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
/**
* #return File
*/
public function getPhotoFile() : ?File
{
return $this->photoFile;
}
/**
* #param string $photo
* #return Photo
*/
public function setPhoto($photo)
{
$this->photo = $photo;
return $this;
}
/**
* #return string
*/
public function getPhoto() : ?string
{
return $this->photo;
}
}
The vich_uploader.yaml file :
vich_uploader:
db_driver: orm
mappings:
photos:
uri_prefix: "/products/"
upload_destination: '%kernel.root_dir%/../public/build/images/products/'
namer: vich_uploader.namer_uniqid
easy_admin.yaml :
easy_admin:
entities:
Product:
class: App\Entity\Product
form:
fields:
- { property: 'photos', type: 'collection', type_options: { entry_type: 'App\Form\PhotoType' } }
PhotoType.php :
<?php
declare(strict_types = 1);
namespace App\Form;
use App\Entity\Product;
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 PhotoType
*
* #package App\Form
*/
class PhotoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('photos', CollectionType::class, ['entry_type' => VichFileType::class])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Photo::class,
]);
}
}
I'm actually creating an api. I'm using vichuploaderBundler to import file and to put them in a specific directory. Since i use my form to send a POST request to create a resource, my ressource get created but the files aren't saved anymore. Here is my current code.
postController
/**
* #Rest\View(statusCode=Response::HTTP_CREATED)
* #Rest\Post("/posts")
*/
public function postPostsAction(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->submit($request->request->all()); // Validation des données
if ($form->isValid()) {
$post->setUpdatedAt(new \DateTime());
$em = $this->get('doctrine.orm.entity_manager');
$em->persist($post);
$em->flush();
return $post;
} else {
return $form;
}
}
PostType
class PostType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title');
$builder->add('description');
$builder->add('imageName');
$builder->add('imageFile', FileType::class);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Post',
//An API must be stateless, Cross-Site Request Forgery must be disable
'csrf_protection' => false,
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_post';
}
}
The post entity :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Post
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
* #ExclusionPolicy("all")
* #Vich\Uploadable
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Title", type="string", length=100)
*/
private $title;
/**
* #var string
* #Expose
* #ORM\Column(name="Description", type="string", length=400, nullable=true)
*/
private $description;
/**
* #var string
* #Expose
* #ORM\Column(name="imageName", type="string", length=255)
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="product_image", fileNameProperty="imageName")
* #var File
*/
private $imageFile;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
* #Expose
* #return Post
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($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();
}
return $this;
}
/**
* #return File|null
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* Set imageName
*
* #param string $imageName
*
* #return Post
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* Get imageName
*
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* #param \DateTime $updatedAt
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
}
/**
* Set description
*
* #param string $description
*
* #return Post
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
}
EDIT :
Here is my vichuploader configuration, in config.yml
#vich uploader Configuration
vich_uploader:
db_driver: orm
mappings:
product_image:
uri_prefix: /images/posts
upload_destination: '%kernel.root_dir%/../web/images/posts'
namer: vich_uploader.namer_uniqid
inject_on_load: false
delete_on_update: true
delete_on_remove: true
Before using my form i was uploading on that way :
public function postPostsAction(Request $request)
{
$post = new Post();
$post->setTitle($request->get('title'))
->setDescription($request->get('description'))
->setImageName($request->get('image_name'))
->setImageFile($request->files->get('imageFile'))
// 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
->setUpdatedAt(new \DateTime());
// On fait appel à doctrine pour seter un id lors de la création en base
$em = $this->get('doctrine.orm.entity_manager');
$em->persist($post);
$em->flush();
return $post;
}
Thanks for helping !
I'm working on Symfony 2.8, I've made a form to upload 1 picture, and it's working fine. But the problem I have now, is that I want to make this form allow us multiple pictures.
Here is what I tried, which was with no result yet !
Image entity :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Image
*
* #ORM\Table(name="image")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ImageRepository")
*/
class Image
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
/**
* #ORM\Column(type="string")
*
* #Assert\NotBlank(message="Please, upload the product name as a image file.")
* #Assert\File(mimeTypes={ "image/jpg", "image/jpeg" ,"image/png"})
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="create_time", type="datetime", nullable=false)
*/
private $createTime;
/**
* #var \DateTime
*
* #ORM\Column(name="change_time", type="datetime", nullable=true)
*/
private $changeTime;
/**
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\User", cascade={"persist"})
* #ORM\JoinColumn(referencedColumnName="id", nullable=false)
*/
private $createUser;
/**
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\User", cascade={"persist"})
* #ORM\JoinColumn(referencedColumnName="id", nullable=true)
*/
private $changeUser;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set path
*
* #param string $path
*
* #return Image
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return \DateTime
*/
public function getCreateTime()
{
return $this->createTime;
}
/**
* #param \DateTime $createTime
*/
public function setCreateTime($createTime)
{
$this->createTime = $createTime;
}
/**
* #return \DateTime
*/
public function getChangeTime()
{
return $this->changeTime;
}
/**
* #param \DateTime $changeTime
*/
public function setChangeTime($changeTime)
{
$this->changeTime = $changeTime;
}
/**
* #return mixed
*/
public function getChangeUser()
{
return $this->changeUser;
}
/**
* #param mixed $changeUser
*/
public function setChangeUser($changeUser)
{
$this->changeUser = $changeUser;
}
/**
* #return mixed
*/
public function getCreateUser()
{
return $this->createUser;
}
/**
* #param mixed $createUser
*/
public function setCreateUser($createUser)
{
$this->createUser = $createUser;
}
}
ImageType :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\SubmitButton;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ImageType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', FileType::class, array("label" => "Files",
"required" => FALSE,
"attr" => array(
"multiple" => "multiple")))
->add('path')
->add('Upload', SubmitType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Image'
));
}
}
ImageController :
public function UploadAction(Request $request)
{
$product = new Image();
$form = $this->createForm(ImageType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var UploadedFile $file */
$file = $product->getName();
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move(
'/var/www/html/master_c/web/images/',
$fileName
);
$product->setName($fileName);
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
}
return $this->render('AppBundle:Image:upload.html.twig', array(
'form' => $form->createView(),
));
}
upload.html.twig :
{{ form_start(form) }}
{# ... #}
{{ form_row(form.name, { 'attr': {'multiple': 'multiple' }}) }}
{{ form_end(form) }}
Creating multiples upload you need to create a collection type in your form type and make JavaScript to add multiple upload in front end. More detail you can check the doc.
http://symfony.com/doc/current/reference/forms/types/collection.html
you need to change this "multiple" => "multiple" to
'multiple' => true
you should have something like this
->add('name', FileType::class, array("label" => "Files",
'required' => false,
'multiple' => true))
I'm using VichUploader in my Symfony2.3 application. I created one to one relationship between my Post entity and Image entity and I'm using Data Transformer for adding images through Post Form. Everything is working well, I'm getting no exception but post_id is not saved in Image table in database. Probably because of that every image I want to post is broken.
I would appreciate any help!
Image entity:
namespace BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Class Image.
*
* #package Model
* #author Monika Malinowska
*
* #ORM\Table(name="images")
* #ORM\Entity(repositoryClass="BlogBundle\Repository\Image")
* #Vich\Uploadable
*/
class Image
{
/**
* #ORM\Id
* #ORM\Column(
* type="integer",
* nullable=false,
* options={
* "unsigned" = true
* }
* )
* #ORM\GeneratedValue(strategy="IDENTITY")
* #var integer $id
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Post", inversedBy="image")
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
protected $post;
/**
*
* #Vich\UploadableField(mapping="image", fileNameProperty="imageName")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* #return Image
*/
public function __construct()
{
$this->updatedAt= new \DateTime();
}
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($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 \DateTime('now');
}
return $this;
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*
* #return Image
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Set Id.
*
* #param integer $id Id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Get Id.
*
* #return integer Result
*/
public function getId()
{
return $this->id;
}
/**
* Set Post.
*
* #param integer $post Post
*/
public function setPost($post)
{
$this->post = $post;
}
/**
* Get Post.
*
* #return integer Result
*/
public function getPost()
{
return $this->post;
}}
Post Entity:
namespace BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class Post.
*
* #package Model
*
* #ORM\Table(name="posts")
* #ORM\Entity(repositoryClass="BlogBundle\Repository\Post")
*/
class Post
{
/**
* #ORM\Id
* #ORM\Column(
* type="integer",
* nullable=false,
* options={
* "unsigned" = true
* }
* )
* #ORM\GeneratedValue(strategy="IDENTITY")
* #var integer $id
*/
private $id;
...
/**
* #ORM\OneToOne(targetEntity="Image", mappedBy="post")
*/
protected $image;
/**
* Get Id.
*
* #return integer Result
*/
public function getId()
{
return $this->id;
}
/**
* Set Id.
*
* #param integer $id Id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Set image.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
* #return $image
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image.
*
* #return file Image
*/
public function getImage()
{
return $this->image;
}
/**
* Add images.
*
* #param \BlogBundle\Entity\Image $image
* #return Image
*/
public function addImage(\BlogBundle\Entity\Image $image)
{
$this->image = $image;
$image->setImage($this);
return $this;
}
/**
* Remove image
*
* #param \BlogBundle\Entity\Image $image
*/
public function removeImage(\BlogBundle\Entity\Image $image)
{
$this->$image->removeElement($image);
$image->setImage(null);
}
ImageType
namespace BlogBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormTypeExtensionInterface;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Form\Type\VichImageType;
/**
* Class ImageType.
*
* #package BlogBundle\Form
*/
class ImageType extends AbstractType
{
/**
* Form builder.
*
* #param FormBuilderInterface $builder Form builder
* #param array $options Form options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'id',
'hidden'
);
if (isset($options['validation_groups'])
&& count($options['validation_groups'])
&& !in_array('image-delete', $options['validation_groups'])
) {
// $builder->add('name', 'file', array('label' => 'Obrazek (format JPG)'));
//$builder->add('name', 'text');
$builder->add('imageFile', 'vich_image');
}
$builder->add(
'save',
'submit',
array(
'label' => 'Save'
)
);
}
/**
* Sets default options for form.
*
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'BlogBundle\Entity\Image',
'validation_groups' => 'image-default',
)
);
}
/**
* Getter for form name.
*
* #return string Form name
*/
public function getName()
{
return 'image_form';
}
}
PostType
namespace BlogBundle\Form;
use BlogBundle\Form\DataTransformer\TagDataTransformer;
use BlogBundle\Form\DataTransformer\ImageDataTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\HttpFoundation\File\File;
use BlogBundle\Form\ImageType;
/**
* Class PostType.
*
* #package BlogBundle\Form
*/
class PostType extends AbstractType
{
/**
* Form builder.
*
* #param FormBuilderInterface $builder Form builder
* #param array $options Form options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$imageDataTransformer = new ImageDataTransformer($options['image_model']);
$builder->add(
'id',
'hidden'
);
if (isset($options['validation_groups'])
&& count($options['validation_groups'])
&& !in_array('post-delete', $options['validation_groups'])
) {
...
$builder->add(
$builder
->create('image', 'file')
->addModelTransformer($imageDataTransformer)
);
}
$builder->add(
'save',
'submit',
array(
'label' => 'form.save'
)
);
}
/**
* Sets default options for form.
*
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'BlogBundle\Entity\Post',
'validation_groups' => 'post-default',
)
);
$resolver->setRequired(array('image_model'));
$resolver->setAllowedTypes(
array(
'image_model' => 'Doctrine\Common\Persistence\ObjectRepository'
)
);
}
/**
* Getter for form name.
*
* #return string Form name
*/
public function getName()
{
return 'post_form';
}
}
config.yml
vich_uploader:
db_driver: orm
mappings:
image:
uri_prefix: /assetic/images
upload_destination: '%kernel.root_dir%/../web/assetic/images'
namer: vich_uploader.namer_uniqid
inject_on_load: false
delete_on_update: true
delete_on_remove: true
You don't need any data transformer, but you should use 'vich_image' type instead of 'file' type.
Add cascade={"persist"} option to oneToOne annotation in your Post entity.
Well I'm trying to create a form which can handle multiple file uploads. So far so good. I have two tables image and product which has ManyToOne relationship. I'm using CollecitonType field for the images so I can dynamically add/remove fields on the fly. The problem is that I get this exception on form rendering
Neither the property "imageFile" nor one of the methods "getImageFile()", "imageFile()", "isImageFile()", "hasImageFile()", "__get()" exist and have public access in class "AppBundle\Entity\Product".
What I'm doing wrong?
AppBundle\Entity\Image
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Product
*
* #ORM\Table()
* #ORM\Entity
*/
class Image
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Product")
*/
protected $product;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="product_image", fileNameProperty="imageName")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($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 \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
*
* #return Image
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set product
*
* #param \AppBundle\Entity\Product $product
*
* #return Image
*/
public function setProduct(\AppBundle\Entity\Product $product = null)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* #return \AppBundle\Entity\Product
*/
public function getProduct()
{
return $this->product;
}
}
AppBundle\Entity\Product
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection ;
use Doctrine\ORM\Mapping as ORM;
/**
* Product
*
* #ORM\Table()
* #ORM\Entity
*/
class Product
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Product
*/
private $image;
/**
* Constructor
*/
public function __construct()
{
$this->image = new ArrayCollection;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function getImages()
{
return $this->images;
}
public function addImage(ImageInterface $image)
{
if (!$this->images->contains($image)) {
$this->images->add($image);
}
return $this;
}
public function removeImage(ImageInterface $image)
{
$this->images->remove($image);
return $this;
}
public function setImages(Collection $images)
{
$this->images = $images;
}
}
AppBundle\Form\ImageType
..............
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageFile', 'file')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Image',
'attr'=> array('novalidate'=>'novalidate')
));
}
.............
AppBundle\Form\ProductType
...........
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('imageFile', 'collection', array(
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'type' => new ImageType(),
'prototype' => true,
'attr' => array(
'class' => 'selection',
),
))
->add('upload', 'submit')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Product',
'attr'=> array('novalidate'=>'novalidate')
));
}
......
First :
In your Product class:
Your variable is named private $image, it should be private $images; with the 'S' for plurial.
Second :
In your product form builder, you ask for a field named ImageFile, but it doesn't exist in Product class (but exists on Image class), it should be images.