Upload multiple pictures in Symfony 2.8 - php

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))

Related

Symfony 4 - VichUploaderBundle - file input not rendered

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.

Unable to save data from a One To Many

I created a one to many relationship between a society and its employees. Everything worked great, I can see the user list when I'm editing the society page and I can select some of them, but the problem is that I can't save / persist those data, and i don't know why.
I tried a lot of things, like adding allow_add =>true or by_reference => false inside the FormType, but it doesn't work even though it still saves the rest of the form, such as the society's name and its address.
Here are the entities :
Societe.php
<?php
namespace SocieteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Societe
*
* #ORM\Table(name="societe")
* #ORM\Entity(repositoryClass="SocieteBundle\Repository\SocieteRepository")
*/
class Societe
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="RaisonSociale", type="string", length=255, unique=true)
*/
private $raisonSociale;
/**
* #var string
*
* #ORM\Column(name="adresse", type="string", length=255, unique=true)
*/
private $adresse;
/**
* #ORM\OneToMany(targetEntity="UserBundle\Entity\User", mappedBy="entreprise", cascade={"persist"})
*/
private $salaries;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set raisonSociale
*
* #param string $raisonSociale
*
* #return Societe
*/
public function setRaisonSociale($raisonSociale)
{
$this->raisonSociale = $raisonSociale;
return $this;
}
/**
* Get raisonSociale
*
* #return string
*/
public function getRaisonSociale()
{
return $this->raisonSociale;
}
/**
* Set adresse
*
* #param string $adresse
*
* #return Societe
*/
public function setAdresse($adresse)
{
$this->adresse = $adresse;
return $this;
}
/**
* Get adresse
*
* #return string
*/
public function getAdresse()
{
return $this->adresse;
}
/**
* Constructor
*/
public function __construct()
{
$this->salaries = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add salary
*
* #param \UserBundle\Entity\User $salary
*
* #return Societe
*/
public function addSalary(\UserBundle\Entity\User $salary)
{
$this->salaries[] = $salary;
return $this;
}
/**
* Remove salary
*
* #param \UserBundle\Entity\User $salary
*/
public function removeSalary(\UserBundle\Entity\User $salary)
{
$this->salaries->removeElement($salary);
}
/**
* Get salaries
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSalaries()
{
return $this->salaries;
}
/**
* Set salaries
* Something I tried, didn't work
*/
public function setSalaries($salaries)
{
$this->salaries = $salaries;
return $this;
}
}
User.php
<?php
namespace UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="prenom", type="string", length=255, nullable=true)
*/
protected $prenom;
/**
* #var string
* #ORM\Column(name="nom", type="string", length=255, nullable=true)
*/
protected $nom;
/**
* #var string
* #ORM\Column(name="telephone", type="string", length=255, nullable=true)
*/
protected $telephone;
/**
* #ORM\OneToMany(targetEntity="MissionBundle\Entity\Mission", mappedBy="createur", cascade={"remove", "persist"})
*
*/
protected $post;
/**
* #ORM\ManyToOne(targetEntity="EcoleBundle\Entity\Ecole", inversedBy="representant", cascade={"persist"})
* #ORM\JoinColumn(name="ecole_id", referencedColumnName="id")
*/
protected $ecole;
/**
* #ORM\ManyToOne(targetEntity="SocieteBundle\Entity\Societe", inversedBy="salaries", cascade={"persist"})
* #ORM\JoinColumn(name="salaries_id", referencedColumnName="id")
*/
private $entreprise;
/**
* Set prenom
*
* #param string $prenom
*
* #return User
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
return $this;
}
/**
* Get prenom
*
* #return string
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* Set nom
*
* #param string $nom
*
* #return User
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set telephone
*
* #param string $telephone
*
* #return User
*/
public function setTelephone($telephone)
{
$this->telephone = $telephone;
return $this;
}
/**
* Get telephone
*
* #return string
*/
public function getTelephone()
{
return $this->telephone;
}
/**
* Add post
*
* #param \MissionBundle\Entity\Mission $post
*
* #return User
*/
public function addPost(\MissionBundle\Entity\Mission $post)
{
$this->post[] = $post;
return $this;
}
/**
* Remove post
*
* #param \MissionBundle\Entity\Mission $post
*/
public function removePost(\MissionBundle\Entity\Mission $post)
{
$this->post->removeElement($post);
}
/**
* Get post
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPost()
{
return $this->post;
}
/**
* Set post
*
* #param \Doctrine\Common\Collections\Collection $comments
*/
public function setDeskComment(\Doctrine\Common\Collections\Collection $post){
$this->post = $post;
}
/**
* Set ecole
*
* #param \EcoleBundle\Entity\Ecole $ecole
*
* #return User
*/
public function setEcole(\EcoleBundle\Entity\Ecole $ecole = null)
{
$this->ecole = $ecole;
return $this;
}
/**
* Get ecole
*
* #return \EcoleBundle\Entity\Ecole
*/
public function getEcole()
{
return $this->ecole;
}
/**
* Set entreprise
*
* #param \SocieteBundle\Entity\Societe $entreprise
*
* #return User
*/
public function setEntreprise(\SocieteBundle\Entity\Societe $entreprise = null)
{
$this->entreprise = $entreprise;
return $this;
}
/**
* Get entreprise
*
* #return \SocieteBundle\Entity\Societe
*/
public function getEntreprise()
{
return $this->entreprise;
}
}
SocieteType.php
<?php
namespace SocieteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
//use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SocieteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('raisonSociale')
->add('adresse')
->add('salaries', null, array(
'by_reference' => false,
)
)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SocieteBundle\Entity\Societe'
));
}
}
SocieteController.php
<?php
namespace SocieteBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use SocieteBundle\Entity\Societe;
use SocieteBundle\Form\SocieteType;
/**
* Societe controller.
*
* #Route("/societe")
*/
class SocieteController extends Controller
{
/**
* Lists all Societe entities.
*
* #Route("/", name="societe_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$societes = $em->getRepository('SocieteBundle:Societe')->findAll();
return $this->render('societe/index.html.twig', array(
'societes' => $societes,
));
}
/**
* Creates a new Societe entity.
*
* #Route("/new", name="societe_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$societe = new Societe();
$form = $this->createForm('SocieteBundle\Form\SocieteType', $societe);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($societe);
$em->flush();
return $this->redirectToRoute('societe_show', array('id' => $societe->getId()));
}
return $this->render('societe/new.html.twig', array(
'societe' => $societe,
'form' => $form->createView(),
));
}
/**
* Finds and displays a Societe entity.
*
* #Route("/{id}", name="societe_show")
* #Method("GET")
*/
public function showAction(Societe $societe)
{
$deleteForm = $this->createDeleteForm($societe);
return $this->render('societe/show.html.twig', array(
'societe' => $societe,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing Societe entity.
*
* #Route("/{id}/edit", name="societe_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Societe $societe)
{
$deleteForm = $this->createDeleteForm($societe);
$editForm = $this->createForm('SocieteBundle\Form\SocieteType', $societe);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($societe);
$em->flush();
return $this->redirectToRoute('societe_edit', array('id' => $societe->getId()));
}
return $this->render('societe/edit.html.twig', array(
'societe' => $societe,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a Societe entity.
*
* #Route("/{id}", name="societe_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Societe $societe)
{
$form = $this->createDeleteForm($societe);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($societe);
$em->flush();
}
return $this->redirectToRoute('societe_index');
}
/**
* Creates a form to delete a Societe entity.
*
* #param Societe $societe The Societe entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Societe $societe)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('societe_delete', array('id' => $societe->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
Please tell me if you want me to add a file to the question, I'll edit it. Thank you in advance
edit: I can see the array with the user I selected if I dump the form content
In your SocieteType.php change:
->add('salaries', null, array(
'by_reference' => false,
)
to
->add('salaries', 'entity', array(
'class' => 'SocieteBundle\Entity\Societe',
'property' => 'raisonSociale',
)

VichUploaderBundle: adding Image through Post From not working

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.

CollectionType entities issue

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.

Upload file with symfony2

im new with symfony and i work with Symfony 2.7 and wamp server. I try to implement the upload method following the Symfony doc/cookbook.
Here is my entity:
<?php
namespace NoticiaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraint as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Imagem
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="NoticiaBundle\Repository\ImagemRepository")
* #ORM\HasLifecycleCallbacks
*/
class Imagem
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #ORM\OneToMany(targetEntity="Noticia", mappedBy="imagem")
*/
private $noticias = array();
public function __construct()
{
$this->noticias = new ArrayCollection();
}
public $file;
public function getUploadRootDir()
{
return __dir__.'/../../../../web/uploads';
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
/**
* #ORM\Prepersist()
* #ORM\Preupdate()
*/
public function preUpload()
{
$this->tempFile = $this->getAbsolutePath();
$this->oldFile = $this->getPath();
if(null !== $this->file) $this->path = sha1(uniqid(mt_rand(),true)).'.'.$this->file->guessExtension();
}
/**
* #ORM\Postpersist()
* #ORM\Postupdate()
*/
public function upload()
{
if(null !==$this->file){
$this->file->move($this->getUploadRootDir(),$this->path);
unset($this->file);
if($this->oldFile !==null) unlink($this->tempFile);
}
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFile = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if(file_exists($this->tempFile)) unlink($this->tempFile);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath()
{
return $this->path;
}
public function getName()
{
return $this->name;
}
}
My entity whith foreign key imagem
<?php
namespace NoticiaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Noticia
*
* #ORM\Table(name="NOTICIA")
* #ORM\Entity(repositoryClass="NoticiaBundle\Repository\NoticiaRepository")
* #ORM\HasLifecycleCallbacks
*/
class Noticia
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titulo", type="string", length=255)
*/
private $titulo;
/**
* #var string
*
* #ORM\Column(name="noticia", type="string", length=255)
*/
private $noticia;
/**
* #ORM\ManyToOne(targetEntity="Imagem", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $imagem;
/**
* #ORM\ManyToOne(targetEntity="Categoria", inversedBy="noticias")
* #ORM\JoinColumn(name="categoria_id", referencedColumnName="id")
*/
private $categoria;
/**
* #ORM\OneToMany(targetEntity="Comentario", mappedBy="noticia")
*/
private $comentarios = array();
public function __construct()
{
$this->comentarios = new ArrayCollection();
$this->setDtCadastro(new \DateTime());
$this->setDtAtualizacao(new \DateTime());
}
/**
* #var \DateTime
*
* #ORM\Column(name="dt_cadastro", type="datetime")
*/
private $dtCadastro;
/**
* #var \DateTime
*
* #ORM\Column(name="dt_atualizacao", type="datetime")
*/
private $dtAtualizacao;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set titulo
*
* #param string $titulo
* #return Noticia
*/
public function setTitulo($titulo)
{
$this->titulo = $titulo;
return $this;
}
/**
* Get titulo
*
* #return string
*/
public function getTitulo()
{
return $this->titulo;
}
/**
* Set noticia
*
* #param string $noticia
* #return Noticia
*/
public function setNoticia($noticia)
{
$this->noticia = $noticia;
return $this;
}
/**
* Get noticia
*
* #return string
*/
public function getNoticia()
{
return $this->noticia;
}
/**
* Set imagem
*
* #param string $imagem
* #return Noticia
*/
public function setImagem($imagem)
{
$this->imagem = $imagem;
return $this;
}
/**
* Get imagem
*
* #return string
*/
public function getImagem()
{
return $this->imagem;
}
/**
* Set categoria
*
* #param string $categoria
* #return Noticia
*/
public function setCategoria($categoria)
{
$this->categoria = $categoria;
return $this;
}
/**
* Get categoria
*
* #return string
*/
public function getCategoria()
{
return $this->categoria;
}
public function addComentario(Comentario $comentario)
{
$this->comentarios[] = $comentario;
return $this;
}
public function getComentarios()
{
return $this->comentarios;
}
/**
* Set dtCadastro
*
* #param \DateTime $dtCadastro
* #return Noticia
*/
public function setDtCadastro($dtCadastro)
{
$this->dtCadastro = $dtCadastro;
return $this;
}
/**
* Get dtCadastro
*
* #return \DateTime
*/
public function getDtCadastro()
{
return $this->dtCadastro;
}
/**
* Set dtAtualizacao
*
* #param \DateTime $dtAtualizacao
* #return Noticia
*/
public function setDtAtualizacao($dtAtualizacao)
{
$this->dtAtualizacao = $dtAtualizacao;
return $this;
}
/**
* Get dtAtualizacao
*
* #return \DateTime
*/
public function getdtAtualizacao()
{
return $this->dtAtualizacao;
}
/**
* #ORM\PreUpdate
*/
public function setDtAtualizacaoValue()
{
$this->setDtAtualizacao(new \DateTime());
}
/**
* Remove comentarios
*
* #param \NoticiaBundle\Entity\Comentario $comentarios
*/
public function removeComentario(\NoticiaBundle\Entity\Comentario $comentarios)
{
$this->comentarios->removeElement($comentarios);
}
}
my imagem form type
<?php
namespace NoticiaBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ImagemType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file','file',array('required' => false));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'NoticiaBundle\Entity\Imagem'
));
}
/**
* #return string
*/
public function getName()
{
return 'noticiabundle_imagem';
}
}
Noticia FormType
<?php
namespace NoticiaBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use NoticiaBundle\Form\ImagemType;
class NoticiaType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('titulo',null, array('attr' => array('class' => 'form-control'),
'label_attr' => array('class' => 'control-label col-lg-2')
))
->add('imagem', new ImagemType(),
array('label_attr' => array('class' => 'control-label col-lg-2')
))
->add('noticia','textarea', array('attr' => array('class' => 'form-control', 'rows' => 15),
'label_attr' => array('class' => 'control-label col-lg-2')
))
->add('categoria', 'entity', array('attr' => array('class' => 'form-control'),
'label_attr' => array('class' => 'control-label col-lg-2'),
'class' => 'NoticiaBundle:Categoria',
'property' => 'nome',
'placeholder' => 'Escolha...',
))
->add('salvar', 'submit', array('attr' => array('class' => 'btn btn-primary pull-right')));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'NoticiaBundle\Entity\Noticia'
));
}
/**
* #return string
*/
public function getName()
{
return 'noticiabundle_noticia';
}
}
And the controler function to persist Noticias and imagens(upload files)
/**
* Add new Noticia.
*
* #Route("/noticia/novo", name="nova_noticia")
* #Template()
*/
public function novoAction(Request $request)
{
$form = $this->createForm(new NoticiaType(), new Noticia());
$form->handleRequest($request);
if ($form->isValid()) {
$noticia = $form->getData();
$noticia->setLogin($_SESSION['colaborador_login']);
$noticiaRepository = $this->getDoctrine()->getRepository('NoticiaBundle:Noticia');
try {
$noticiaRepository->novo($noticia);
$this->get('session')->getFlashBag()
->add('success', 'Iserido com sucesso!');
return $this->redirect($this->generateUrl('noticias_index'));
} catch (\Exception $e) {
$this->get('session')->getFlashBag()->add(
'danger', $e->getMessage()
);
}
}
return $this->render("NoticiaBundle:Noticia:formulario.html.twig",
array('form' => $form->createView()));
}
I change my entity upload file and now he is saving the right path, but the file is bot saving into the project directory
There is no easy way to test your code but just looking at it, the most obvious issue I see is that you don't call the function preUpload before saving the file.
You may need the following annotation for the preUpload function.
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/

Categories