Closed mkalisz77 closed 8 years ago
As there are some helpers for VichUploaderBundle
in EasyAdmin, you might want to take a look at this issue: dustin10/VichUploaderBundle#276 , because it contains exactly what you need.
If you want multiple file uploads, then you should have a *ToMany
relationship with an entity that contains medias/images/files, then use the collection
form type for your field, and finally add VichFile or VichImage as prototype for this collection
form field.
I have not tested it, but it should be working actually, and the issue on the bundle might give some clues about how to do it.
If you test it and find it to be working, it would be great for you to propose a cookbook to add to EasyAdmin ;)
@mkalisz77 I am trying to do the same so if you get it soon and can share your solution sounds good perhaps this could be added to the guides related to Files uploads
I'll try to setup multiple upload, but this OneupUploaderBundle seems to be more user friendly than VichUploader.
I'm closing this issue as "won't fix" ... but if someone sets up this multiple upload thing with VichUploader bundle and explains the process to me, I will gladly improve the How to Upload Files and Images tutorial. Thanks!
I got this to work with VichUploaderBundle.
I wanted to have an entity to hold images for several other entities and this is what I did:
First of all I created the Image entity (it has a name field that I needed for this case which also works):
AppBundle\Entity\Image
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Image
*
* @ORM\Table()
* @ORM\Entity()
* @Vich\Uploadable
*/
class Image
{
/**
* @var int
*
* @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;
/**
* @var string
*
* @ORM\Column(type="string", length=255)
*/
private $image;
/**
* @var File
*
* @Vich\UploadableField(mapping="images", fileNameProperty="image")
*/
private $imageFile;
/**
* @var \DateTime
*
* @ORM\Column(type="datetime", length=255)
*/
private $updatedAt;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Image
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param File|null $image
* @return Image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
/**
* @return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* @param string $image
* @return Image
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* @return string
*/
public function getImage()
{
return $this->image;
}
}
And its form type:
AppBundle\Form\ImageType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ImageType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('imageFile', VichFileType::class)
;
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Image'
));
}
}
One of the owner entities:
AppBundle\Entity\Owner
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Owner
*
* @ORM\Table()
* @ORM\Entity()
*/
class Owner
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var Image[]|ArrayCollection
*
* @ORM\ManyToMany(targetEntity="Image", cascade={"persist"})
* @ORM\JoinTable(name="owner_images",
* joinColumns={@ORM\JoinColumn(name="owner_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="image_id", referencedColumnName="id", unique=true)}
* )
*/
private $images;
/**
* Owner constructor.
*/
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* Get images
*
* @return Image[]|ArrayCollection
*/
public function getImages()
{
return $this->images;
}
}
Then I configured the Vich mapping and easy admin config:
app/config/config.yml
parameters:
app.path.images: /images/uploaded/
vich_uploader:
db_driver: orm
mappings:
images:
uri_prefix: %app.path.images%
upload_destination: %kernel.root_dir%/../web/%app.path.images%
easy_admin:
entities:
Owner:
class: AppBundle\Entity\Owner
form:
fields: [{ property: 'images', type: 'collection', type_options: { entry_type: 'AppBundle\Form\ImageType' }}]
list:
fields: ['id','images']
Haven't had time to find a better solution but I hope this could help someone.
@juaruipi I tried to follow above example to get OneToMany relation with images for rooms: RoomImage that suppose to store images for class Room
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
/**
* RoomImage
*
* @ORM\Table(name=room_images")
* @ORM\Entity(repositoryClass="AppBundle\Repository\RoomImageRepository")
* @Vich\Uploadable
*/
class RoomImage
{
/**
* @var Room
* @ORM\ManyToOne(targetEntity="Room", inversedBy="images")
*/
private $room;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="SEQUENCE")
* @ORM\SequenceGenerator(sequenceName="room_images_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* @ORM\Column(name="image", type="string", length=255, nullable=true)
* @var string
*/
private $image;
/**
* @var File
*
* @Vich\UploadableField(mapping="room_images", fileNameProperty="image")
*/
private $imageFile;
/**
* @var \DateTime
*
* @ORM\Column(type="datetime", length=255)
*/
private $updatedAt;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set room
*
* @param Room $room
*
* @return RoomImage
*/
public function setRoom(Room $room = null)
{
$this->room = $room;
return $this;
}
/**
* Get room
*
* @return Room
*/
public function getRoom()
{
return $this->room;
}
/**
* @param File|null $image
* @return Image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
/**
* @return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* @param string $image
* @return Image
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* @return string
*/
public function getImage()
{
return $this->image;
}
public function __toString()
{
return (string)$this->image;
}
}
class Room
<?php
namespace Agh\GeoEpomBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* Room
*
* @ORM\Table(name="rooms")
* @ORM\Entity(repositoryClass="AppBundle\Repository\RoomRepository")
* @Gedmo\Loggable()
*/
class Room
{
/**
* @var RoomImage[]|ArrayCollection
*
* @ORM\OneToMany(targetEntity="RoomImage", mappedBy="room", cascade={"persist"})
*/
private $images;
// other functionality of the class
/**
* Get images
*
* @return RoomImages[]|ArrayCollection
*/
public function getImages()
{
return $this->images;
}
/**
* Add image
*
* @param RoomImage $image
*
* @return Room
*/
public function addImage(RoomImage $image)
{
$image->setRoom($this);
$this->images[] = $image;
dump($image);
return $this;
}
/**
* Remove image
*
* @param RoomImage $image
*/
public function removeImage(RoomImage $image)
{
$image->setRoom(null);
$this->images->removeElement($image);
}
I modified ImageType as I do not need name field
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ImageType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ->add('name')
->add('imageFile', VichFileType::class);
dump($builder);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\RoomImage'
));
}
}
config file
parameters:
app.path.room_images: /uploads/images/rooms
vich_uploader:
db_driver: orm
mappings:
room_images:
uri_prefix: %app.path.room_images%
upload_destination: %kernel.root_dir%/../web/uploads/images/rooms
I have it partially working. I have form in EasyAdmin, can select and add many files but the only info stored in RoomImage table is:
id | room_id | image | updated_at |
---|---|---|---|
1 | NULL | image1.jpg | 2017-01-17 13:15:15 |
2 | NULL | image2.jpg | 2017-01-17 13:25:35 |
@mysiar Have you tried setting by_reference option to false for the images collection field?
easy_admin:
entities:
Owner:
class: AppBundle\Entity\Room
form:
fields: [{ property: 'images', type: 'collection', type_options: { entry_type: 'AppBundle\Form\RoomImage', by_reference: false }}]
@juaruipi THANKS FOR THAT :) working as charm now for adding images but problem is for editing
Error: Maximum function nesting level of '100' reached, aborting!
@juaruipi I found the problem. To solve it I had to add below config to php.ini
[xdebug]
xdebug.max_nesting_level = 1000
@mysiar the problem is not about the max nesting level, if you have more than 100 nested functions, you may have errors in your templates or something, you should debug that before.
@mysiar Have you removed all the calls to the dump function? If it's using var_dump or similar, you can have problems when dumping entities or other objects. In this case, your image instance is now pointing to its room and you've an infinite loop there.
@javiereguiluz I do not have any dump functions running @Pierstoval I have entity with 7 relations so maybe this was a problem
Is there any reason why below config line cause problem ?
- { property: 'image', type: 'image', base_path: %vich_uploader.mappings.room_image% }
my vich configuration is:
parameters:
app.path.room_images: /uploads/images/rooms
vich_uploader:
db_driver: orm
mappings:
room_image:
uri_prefix: %app.path.room_images%
upload_destination: %kernel.root_dir%/../web/uploads/images/rooms
namer: vich_uploader.namer_origname
directory_namer:
service: vich_uploader.directory_namer_subdir
options: {chars_per_dir: 4}
error message says
ParameterNotFoundException in ParameterBag.php line 100:
You have requested a non-existent parameter "vich_uploader.mappings.room_image".
@juaruipi I have another implementation of you above code for ManyToMany this time.
Class Room
class Room
{
/**
* @var RoomDocument[]|ArrayCollection
*
* @ORM\ManyToMany(targetEntity="RoomDocument", cascade={"persist"})
* @ORM\JoinTable(name="_room_documents",
* joinColumns={@ORM\JoinColumn(name="room_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="document_id", referencedColumnName="id")}
* )
*
*/
private $documents;
/**
* Add document
*
* @param \AppBundle\Entity\RoomDocument $document
*
* @return Room
*/
public function addDocument(\AppBundle\Entity\RoomDocument $doc)
{
$this->documents[] = $doc;
$doc->addRoom($this);
return $this;
}
/**
* Remove document
*
* @param \AppBundle\Entity\RoomDocument $doc
*/
public function removeDocument(\AppBundle\Entity\RoomDocument $doc)
{
$this->documents->removeElement($doc);
$doc->removeRoom($this);
}
/**
* Get documents
*
* @return RoomDocument[]|ArrayCollection
*/
public function getDocuments()
{
return $this->documents;
}
}
Class RoomDocument
class RoomDocument
{
/**
* @ORM\ManyToMany(targetEntity="Room", mappedBy="documents")
*/
private $rooms;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="SEQUENCE")
* @ORM\SequenceGenerator(sequenceName="geoepom.room_documents_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255, nullable=true)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="document", type="string", length=255)
*/
private $document;
/**
* @var File
*
* @Vich\UploadableField(mapping="room_doc", fileNameProperty="document")
*/
private $documentFile;
/**
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime", length=255)
*/
private $updatedAt;
/**
* RoomDocument constructor.
*
*/
public function __construct()
{
$this->rooms = new \Doctrine\Common\Collections\ArrayCollection();
}
public function __toString()
{
return (string)$this->document;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @return string
*/
public function getDocument()
{
return $this->document;
}
/**
* @return File
*/
public function getDocumentFile()
{
return $this->documentFile;
}
/**
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* @param string $title
* @return RoomDocument
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* @param string $document
* @return RoomDocument
*/
public function setDocument($document)
{
$this->document = $document;
return $this;
}
/**
* @param File $documentFile
* @return RoomDocument
*/
public function setDocumentFile(File $documentFile = null)
{
$this->documentFile = $documentFile;
if ($documentFile) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
/**
* Add room
*
* @param \AppBundle\Entity\Room $room
*
* @return RoomDocument
*/
public function addRoom(\AppBundle\Entity\Room $room)
{
$this->rooms[] = $room;
return $this;
}
/**
* Remove room
*
* @param \AppBundle\Entity\Room $room
*/
public function removeRoom(\AppBundle\Entity\Room $room)
{
$this->rooms->removeElement($room);
}
/**
* Get rooms
*
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function getRooms()
{
return $this->rooms;
}
}
The problem I'm facing now is that I can't have it write on inverse side despite updating room on document side and using easy admin config
- { property: 'documents', label: label.documents, type: 'collection', type_options: { entry_type: 'AppBundle\Form\RoomDocumentType', by_reference: false }}
Hello,
I tried @juaruipi's solution because I need to have an entity to hold images for other entities too but I have this error:
Could not load type "HeliMusicBundle\Form\ImageType"
I tried cache:clear
but still the same error.
I checked my use
but it seems fine.
Can you help me with this ?
Here's my code:
HeliMusicBundle\Entity\Image
<?php
namespace Projet\HeliMusicBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Image
*
* @ORM\Table(name="image")
* @ORM\Entity(repositoryClass="Projet\HeliMusicBundle\Repository\ImageRepository")
* @Vich\Uploadable
*/
class Image
{
/**
* @var int
*
* @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;
/**
* @var string
*/
private $image;
/**
* @var File
*
* @Vich\UploadableField(mapping="cover_img", fileNameProperty="image")
*/
private $imageFile;
/**
* @var int
*
* @ORM\Column(name="img_size", type="integer", nullable=true)
*/
private $imgSize;
/**
* @var \DateTime
*
* @ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Image
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param File|null $image
* @return Image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
/**
* @return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* @param string $image
* @return Image
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* @return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set imgSize
*
* @param integer $imgSize
*
* @return Image
*/
public function setImgSize($imgSize)
{
$this->imgSize = $imgSize;
return $this;
}
/**
* Get imgSize
*
* @return int
*/
public function getImgSize()
{
return $this->imgSize;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
*
* @return Image
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* 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;
}
HeliMusicBundle\Form\ImageType
<?php
namespace HeliMusicBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ImageType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('imageFile', VichFileType::class)
;
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'HeliMusicBundle\Entity\Image'
));
}
}
HeliMusicBundle\Entity\Image
<?php
namespace Projet\HeliMusicBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Page
*
* @ORM\Table(name="page")
* @ORM\Entity(repositoryClass="Projet\HeliMusicBundle\Repository\PageRepository")
*/
class Page
{
/**
* @var int
*
* @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;
/**
* @var string
*
* @ORM\Column(name="meta_title", type="string", length=255)
*/
private $metaTitle;
/**
* @var string
*
* @ORM\Column(name="meta_keywords", type="string", length=255, nullable=true)
*/
private $metaKeywords;
/**
* @var string
*
* @ORM\Column(name="meta_content", type="string", length=255, nullable=true)
*/
private $metaContent;
/**
* @var string
*
* @ORM\Column(name="meta_author", type="string", length=50, nullable=true)
*/
private $metaAuthor;
/**
* @var string
*
* @ORM\Column(name="page_title", type="string", length=255, nullable=true)
*/
private $pageTitle;
/**
* @var string
*
* @ORM\Column(name="footer_title", type="string", length=255, nullable=true)
*/
private $footerTitle;
/**
* @var Image[]|ArrayCollection
*
* @ORM\ManyToMany(targetEntity="Image", cascade={"persist"})
* @ORM\JoinTable(name="page_images",
* joinColumns={@ORM\JoinColumn(name="page_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="image_id", referencedColumnName="id", unique=true)}
* )
*/
private $images;
/**
* @var \DateTime
*
* @ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
/**
* @ORM\ManyToMany(targetEntity="Projet\HeliMusicBundle\Entity\Video", cascade={"persist"})
*/
private $videos;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set metaTitle
*
* @param string $metaTitle
*
* @return Page
*/
public function setMetaTitle($metaTitle)
{
$this->metaTitle = $metaTitle;
return $this;
}
/**
* Get metaTitle
*
* @return string
*/
public function getMetaTitle()
{
return $this->metaTitle;
}
/**
* Set metaKeywords
*
* @param string $metaKeywords
*
* @return Page
*/
public function setMetaKeywords($metaKeywords)
{
$this->metaKeywords = $metaKeywords;
return $this;
}
/**
* Get metaKeywords
*
* @return string
*/
public function getMetaKeywords()
{
return $this->metaKeywords;
}
/**
* Set metaContent
*
* @param string $metaContent
*
* @return Page
*/
public function setMetaContent($metaContent)
{
$this->metaContent = $metaContent;
return $this;
}
/**
* Get metaContent
*
* @return string
*/
public function getMetaContent()
{
return $this->metaContent;
}
/**
* Set metaAuthor
*
* @param string $metaAuthor
*
* @return Page
*/
public function setMetaAuthor($metaAuthor)
{
$this->metaAuthor = $metaAuthor;
return $this;
}
/**
* Get metaAuthor
*
* @return string
*/
public function getMetaAuthor()
{
return $this->metaAuthor;
}
/**
* Set pageTitle
*
* @param string $pageTitle
*
* @return Page
*/
public function setPageTitle($pageTitle)
{
$this->pageTitle = $pageTitle;
return $this;
}
/**
* Get pageTitle
*
* @return string
*/
public function getPageTitle()
{
return $this->pageTitle;
}
/**
* Set footerTitle
*
* @param string $footerTitle
*
* @return Page
*/
public function setFooterTitle($footerTitle)
{
$this->footerTitle = $footerTitle;
return $this;
}
/**
* Get footerTitle
*
* @return string
*/
public function getFooterTitle()
{
return $this->footerTitle;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
*
* @return Page
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
*
* @return Page
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* page constructor.
*/
public function __construct()
{
$this->images = new ArrayCollection();
$this->albums = new \Doctrine\Common\Collections\ArrayCollection();
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
$this->videos = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add album
*
* @param \Projet\HeliMusicBundle\Entity\Album $album
*
* @return Page
*/
public function addAlbum(\Projet\HeliMusicBundle\Entity\Album $album)
{
$this->albums[] = $album;
return $this;
}
/**
* Remove album
*
* @param \Projet\HeliMusicBundle\Entity\Album $album
*/
public function removeAlbum(\Projet\HeliMusicBundle\Entity\Album $album)
{
$this->albums->removeElement($album);
}
/**
* Get albums
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getAlbums()
{
return $this->albums;
}
/**
* Get images
*
* @return Image[]|ArrayCollection
*/
public function getImages()
{
return $this->images;
}
/**
* Add post
*
* @param \Projet\HeliMusicBundle\Entity\Post $post
*
* @return Page
*/
public function addPost(\Projet\HeliMusicBundle\Entity\Post $post)
{
$this->posts[] = $post;
return $this;
}
/**
* Remove post
*
* @param \Projet\HeliMusicBundle\Entity\Post $post
*/
public function removePost(\Projet\HeliMusicBundle\Entity\Post $post)
{
$this->posts->removeElement($post);
}
/**
* Get posts
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPosts()
{
return $this->posts;
}
/**
* Add video
*
* @param \Projet\HeliMusicBundle\Entity\Video $video
*
* @return Page
*/
public function addVideo(\Projet\HeliMusicBundle\Entity\Video $video)
{
$this->videos[] = $video;
return $this;
}
/**
* Remove video
*
* @param \Projet\HeliMusicBundle\Entity\Video $video
*/
public function removeVideo(\Projet\HeliMusicBundle\Entity\Video $video)
{
$this->videos->removeElement($video);
}
/**
* Get videos
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getVideos()
{
return $this->videos;
}
/**
* Set name
*
* @param string $name
*
* @return Page
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
And config files
#Vich config
vich_uploader:
db_driver: orm
mappings:
cover_img:
uri_prefix: /web/uploads/cover
upload_destination: '%kernel.root_dir%/../web/uploads/cover'
namer:
service: vich_uploader.namer_origname
#easy_admin config
easy_admin:
entities:
Page:
class: Projet\HeliMusicBundle\Entity\Page
label: 'Pages'
list:
title: 'Listing des %%entity_label%%'
fields:
- 'id'
- 'cover_img'
- { property: 'name', label: 'Nom de la page'}
- { property: 'createdAt', label: 'Date de création' }
- { property: 'updatedAt', label: 'Date de mise à jour' }
form:
title: 'Edition'
fields:
- { property: 'name', label: 'Nom de la page' }
- { property: 'metaTitle', help: 'Titre apparaîssant dans l''onglet' }
- { property: 'metaKeywords', help: 'SEO - Mots clé de la page (non apparants sur le site)' }
- { property: 'metaContent', help: 'SEO - Contenu général de la page (non apparant sur le site)' }
- { property: 'metaAuthor', help: 'SEO - Auteur de la page (non apparant sur le site)' }
- { property: 'pageTitle', label: 'Titre de la page', help: 'Titre apparaîssant sur la photo d''en-tête' }
- { property: 'images', type: 'collection', type_options: { entry_type: 'HeliMusicBundle\Form\ImageType' }}
Thanks !
Hi @Tinougit,
Maybe you had an error when pasting your code, but just to be sure, are you missing the php opening tag in your ImageType.php file?
<?php
namespace HeliMusicBundle\Form;
Hi,
I'm sorry, I forgot to copy the php tag, but it's in my ImageType.php file. (code edited here)
@Tinougit are you using Symfony 2.7?
If so, I think you'll need to define your form as a service and use its id instead.
@juaruipi
Thanks for reply.
I'm using Symfony 3.3.6, you were on Symfony 3.x when you posted your solution ?
@Tinougit I see you use the prefix "Projet" in all the other namespaces. Are you missing this part in your ImageType's namespace declaration?
@juaruipi yes it was missing in ImageType namespace, I added it, clear cache, but still the same error :(
@Tinougit did you change it in both the file and the config (entry_type)?
If yes, your best option would be to fork symfony-standard and commit the minimum setup to reproduce your issue.
@juaruipi well done, it's working !! I forget the prefix in the config (and 'data_class' too)...
Thanks a lot ! :)
Anyone happen to know what might be the cause when the "Delete" checkbox when ticked doesn't delete the entity nor unset the referenced entity. Referring to this checkbox.
@dsdeiz That field is part of the VichFileType and it is intended to delete the reference to the file but not the entity instance itself. This means setting the entity property which holds the path to the uploaded file (i.e. imageFile) to null. For this to work, the property must be nullable or you'll get a doctrine exception when using the checkbox.
If you don't want this field to appear, you should set the VichFileType option 'allow_delete' to false:
->add('imageFile', VichFileType::class [ 'allow_delete' => false ])
To delete an item from the collection, use the "Remove the item" link instead: http://imgur.com/meGh9I7
Hello. Thank you for sharing. Beginner on Symfony, having followed your advice with the help of your codes, I have an error when sending an image:
NoSuchPropertyException HTTP 500 Internal Server Error Could not determine access type for property "images" in class "MO\WebAppBundle\Entity\WebApp".
Here is my code :
My form :
{{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }} ... {{ form_row(form.images) }} ...
{{ form_end(form) }}
> WebApp.php = parent
<?php
namespace MO\WebAppBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; //pour le slug use Gedmo\Mapping\Annotation as Gedmo;
/**
*/ class WebApp { /**
/**
/**
/**
/**
/**
/**
/**
/**
/**
/**
/**
/**
/**
@return WebApp */ public function setDate($date) { $this->date = $date;
return $this; }
/**
/**
@return WebApp */ public function setTitle($title) { $this->title = $title;
return $this; }
/**
/**
@return WebApp */ public function setAuthor($author) { $this->author = $author;
return $this; }
/**
/**
@return WebApp */ public function setContent($content) { $this->content = $content;
return $this; }
/**
/**
/**
/**
@return WebApp */ public function addCategory(\MO\WebAppBundle\Entity\Category $category) { $this->categories[] = $category;
return $this; }
/**
/**
/**
/**
/**
@return WebApp */ public function setSlug($slug) { $this->slug = $slug;
return $this; }
/**
/----------------------------------/ /**
> Image.php = multiples children
<?php
namespace MO\WebAppBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Symfony\Component\HttpFoundation\File\File; use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
@Vich\Uploadable */ class Image { /**
/**
*/ private $id;
/**
/**
/**
/**
/**
/**
/**
/**
@return Image */ public function setTravel(\MO\WebAppBundle\Entity\WebApp $travel) { $this->travel = $travel;
return $this; }
/**
/**
/**
@return Image */ public function setUrl($url) { $this->url = $url;
return $this; }
/**
/**
@return Image */ public function setAlt($alt) { $this->alt = $alt;
return $this; }
/**
/**
@return Image */ public function setName($name) { $this->name = $name;
return $this; }
/**
/**
@return Image */ public function setImageFile(File $image = null) { $this->imageFile = $image;
if ($image) { $this->updatedAt = new \DateTime('now'); }
return $this; }
/**
/**
@return Image */ public function setImage($image) { $this->image = $image;
return $this; }
/**
/**
@return Image */ public function setImgSize($imgSize) { $this->imgSize = $imgSize;
return $this; }
/**
> WebAppType.php
<?php
namespace MO\WebAppBundle\Form;
use MO\WebAppBundle\Entity\Image; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents;
class WebAppType extends AbstractType { /**
{@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('date', DateTimeType::class) ->add('title', TextType::class) ->add('author', TextType::class) ->add('content', TextareaType::class) ->add('images', FileType::class, array( 'required' => false, 'data_class' => null, 'label' => 'MOWebAppBundle:Image',
))
//->add('images', ImageType::class)
/*->add('images', FileType::class, array(
'multiple' => true,
'label' => false,
'label_attr' => array('class' => 'MOWebAppBundle:Image'),
'required' => true,
))
/*
* Rappel :
** - 1er argument : nom du champ, ici « categories », car c'est le nom de l'attribut
** - 2e argument : type du champ, ici « CollectionType » qui est une liste de quelque chose
** - 3e argument : tableau d'options du champ
*/
->add('categories', EntityType::class, array(
'class' => 'MOWebAppBundle:Category',
'choice_label' => 'name',
'expanded' => true,
'multiple' => true,
))
->add('save', SubmitType::class);
// On ajoute une fonction qui va écouter un évènement de formulaire
$builder->addEventListener(
FormEvents::PRE_SET_DATA, // 1er argument : L'évènement qui nous intéresse : ici, PRE_SET_DATA
function(FormEvent $event) { // 2e argument : La fonction à exécuter lorsque l'évènement est déclenché
// On récupère notre objet WebApp (travel) sous-jacent
$travel = $event->getData();
// Cette condition est importante, on en reparle plus loin
if (null === $travel) {
return; // On sort de la fonction sans rien faire lorsque $advert vaut null
}
// Si l'annonce n'est pas publiée, ou si id null
if (!$travel->getPublished() || null === $travel->getId()) {
// Alors on ajoute le champ published
$event->getForm()->add('published', CheckboxType::class, array('required' => false));
} else {
// Sinon, on le supprime
$event->getForm()->remove('published');
}
}
);
}
/**
ImageType.php
<?php
namespace MO\WebAppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ImageType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('images', VichFileType::class);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MO\WebAppBundle\Entity\Image'
));
}
}
config.yml
# VichUploaderBundle
vich_uploader:
db_driver: orm
mappings:
image_webapp:
uri_prefix: /uploads/img
upload_destination: '%kernel.root_dir%/../web/uploads/img'
# le fichier doit être supprimé lorsqu'un nouveau fichier est téléchargé
delete_on_update : true
#le fichier doit être supprimé lorsque l'entité est supprimée
delete_on_remove : true
easy_admin:
entities:
WebApp:
class: MO\WebAppBundle\Entity\WebApp
form:
fields: [{ property: 'images', type: 'collection', type_options: { entry_type: 'MO\WebAppBundle\Form\ImageType' }}]
list:
fields: ['id','image_webapp']
@Anna5555 did you fix that? I have the same issue and cannot get out of it. thx
I just used the above examples to make this viche multiupload work. This multiupload works by "adding another item".
Are there any new improvemts in this field? By actually selecting whole bunch of images at the same time? And even better also have a drag and drop reorder once files are uploaded?
@Anna5555 you are missing functions addImage()
and removeImage()
in WebApp class.
@juaruipi 3 years later, your solution's still relevant. Can't thank you enough o/
Still wondering if this can be improved like @Arielblues suggestions.
Thank you @juaruipi for this!
If someone wants to use an <input type="file" multiple>
for adding a bunch of images @ the same time in Easy Admin 3, I've found a pretty straightforward solution, here it is :
Add another field after your images field, it will correspond to the input multiple component of your form.
$images = CollectionField::new('images')->setEntryType(CreationImageType::class)->allowAdd(false);
$multipleImages = MultipleImagesField::new('multipleImages')->setFormTypeOption('attr.multiple', 'multiple')->setFormTypeOption('label', " ")->setVirtual(true);
Create the corresponding Field type using a pretty simple FileType.
<?php
namespace App\Field;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; use Symfony\Component\Form\Extension\Core\Type\FileType;
final class MultipleImagesField implements FieldInterface { use FieldTrait;
public static function new(string $propertyName, ?string $label = null): self
{
return (new self())
->setProperty($propertyName)
->setLabel($label)
// this is used in 'edit' and 'new' pages to edit the field contents
// you can use your own form types too
->setFormType(FileType::class)
->setFormTypeOptions(['multiple' => true, 'data_class' => null])
->addCssClass('field-multiple-images');
}
}
- Then just add those methods correspondig to the "virtual multipleImages property" (so don't create the property, only the getter/setter).
public function getMultipleImages() { return null; }
public function setMultipleImages(array $imagesUploadedFiles) // in fact it's more like an "add" than a "set" { foreach ($imagesUploadedFiles as $uploadedFile) { $i = new CreationImage(); $i->setImageFile($uploadedFile); $this->addImage($i); } }
This way your already uploaded files list will be followed by an input tag with the multiple prop and you'll be able to upload a lot of files at the same time !
Go there to see the next step : adding drag & drop https://github.com/EasyCorp/EasyAdminBundle/issues/2662#issuecomment-832978054
Is there any possibility to manage upload multiple files? Maybe this functionality could be added to documentation.
There are some case fg. Gallery/Images in gallery with One2Many relation when it will be nice to have one form for that in EasyAdmin with possibility to manage multiple files at once.
I think that this bundle OneupUploaderBundle is very nice to do this job. Maybe somebody use this bundle with EasyAdmin and can share with community how to start ?
OneupUploaderBundle integrate many external Javascript uploaders: See examples here: http://www.plupload.com/examples/ http://blueimp.github.io/jQuery-File-Upload/ http://fineuploader.com/demos.html
Maybe it can be added to roadmap fo EasyAdmin 2.0?