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.
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 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.
I'm having an issue with something I think is probably fairly basic but the cause is escaping me.
I have a two entities, an Organisation and a Subscription. An organisation is linked with one subscription.
I want to be able to edit this via a form (creating via form already works). When I do the following:
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->findOneBy(array(
'id' => $id
));
$form = $this->createForm(new OrganisationType(), $organisation, array(
'method' => 'POST'
));
$form->submit($paramFetcher->all());
I get an exception because the subscription property of the organisation entity is not set (despite passing one which has a subscription into the form defaults). The exception is as follows:
Expected argument of type "AppBundle\Entity\Subscription", "NULL" given
This is obviously being thrown by the setSubscription method on the Organisation entity but I'm not sure why as the form should be converting the int value passed to a Subscription entity automatically.
Am I doing something stupid here? I have attached the relevant code below. Thank you!
The simplest controller action that replicates this issue
public function testAction(Request $request)
{
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->findOneBy(array('id' => 7));
$form = $this->createForm(new OrganisationType(), $organisation);
$form->submit($request->request->all());
return $this->render('test.html.twig', array(
'testform' => $form->createView()
));
}
Organisation.php
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Organisation
*
* #ORM\Table(name="organisation")
* #ORM\Entity(repositoryClass="AppBundle\Repository\OrganisationRepository")
*/
class Organisation
{
const ENABLED = 1;
const DISABLED = 2;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"default", "list-organisation"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #JMS\Groups({"default", "list-organisation"})
*/
private $name;
/**
* #var Subscription|null The subscription this organisation is currently linked to
* #ORM\ManyToOne(targetEntity="Subscription", inversedBy="organisations")
* #ORM\JoinColumn(name="subscription_id", referencedColumnName="id", onDelete="set null")
* #JMS\Groups({"default", "list-organisation"})
*/
private $subscription;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="User", mappedBy="organisation")
* #JMS\Groups({})
*/
private $users;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Subgroup", mappedBy="organisation")
* #JMS\Groups({"default"})
*/
private $subgroups;
/**
* #var string $enabled
*
* #ORM\Column(type="string")
*/
private $enabled = self::ENABLED;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default", "list-organisation"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default", "list-organisation"})
*/
private $updated;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Organisation
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Sets subscription
*
* #param Subscription $subscription
* #return $this
*/
public function setSubscription(Subscription $subscription)
{
$this->subscription = $subscription;
return $this;
}
/**
* Gets subscription
*
* #return Subscription|null
*/
public function getSubscription()
{
return $this->subscription;
}
/**
* Sets enabled
*
* #param $enabled
*/
public function setEnabled($enabled)
{
if (!in_array($enabled, array(self::ENABLED, self::DISABLED))) {
throw new \InvalidArgumentException('Invalid enabled status');
}
$this->enabled = $enabled;
}
/**
* Gets enabled
*
* #return string
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns Users belonging to this organisation
*
* #return Collection
*/
public function getUsers()
{
return $this->users;
}
public function __toString()
{
return $this->getName();
}
}
Subscription.php
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Organisation
*
* #ORM\Table(name="subscription")
* #ORM\Entity(repositoryClass="AppBundle\Repository\SubscriptionRepository")
*/
class Subscription
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"default", "list-organisation"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #JMS\Groups({"default", "list-organisation"})
*/
private $name;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Organisation", mappedBy="subscription")
* #JMS\Groups({"default"})
*/
private $organisations;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default"})
*/
private $updated;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Subscription
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns Organisations with this subscription type
*
* #return Collection
*/
public function getOrganisations()
{
return $this->organisations;
}
/**
* #return string
*/
public function __toString()
{
return $this->getName();
}
}
OrganisationType.php
<?php
namespace AppBundle\Form;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OrganisationType extends AbstractType
{
private $manager;
public function __construct(ObjectManager $objectManager)
{
$this->manager = $objectManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('subscription', EntityType::class, array(
'class' => 'AppBundle\Entity\Subscription'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Organisation',
'csrf_protection' => false
));
}
}
On a fresh Symfony 3.1, this works like a charm (and this is the recommended way to handle form submission, as stated in a previous comment) :
public function testAction(Request $request)
{
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->find(1);
$form = $this->createForm(OrganisationType::class, $organisation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->persist($organisation);
$this->getDoctrine()->getManager()->flush();
die('saved into database.');
}
return $this->render('test.html.twig', array(
'testform' => $form->createView()
));
}
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))
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.