Symfony2 Embedded Form and MongoDB issue - php

I try to embed a form type into another form type :
$builder->add('geolocation', new GeolocationType());
However when I try and bind the request to the form
if($request->getMethod() == 'POST') {
$form->bindRequest($request);
}
I get the error
Catchable Fatal Error: Argument 1 passed to Company\StoreBundle\Document\Place::setGeolocation()
must be an instance of Company\StoreBundle\Document\Geolocation, array given, called in
/var/www/Company/vendor/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 392 and
defined in /var/www/Company/src/Company/StoreBundle/Document/Place.php line 43
The data_class is set either for the PlaceType and the GeolocationType
Here is my code :
Place.php
namespace Company\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(collection="places")
*/
class Place
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\String
*/
private $title;
/**
* #MongoDB\EmbedOne(targetDocument="Geolocation")
*/
private $geolocation;
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set geolocation
*
* #param Company\StoreBundle\Document\Geolocation $geolocation
*/
public function setGeolocation(\Company\StoreBundle\Document\Geolocation $geolocation)
{
$this->geolocation = $geolocation;
}
/**
* Get geolocation
*
* #return Company\StoreBundle\Document\Geolocation $geolocation
*/
public function getGeolocation()
{
return $this->geolocation;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string $title
*/
public function getTitle()
{
return $this->title;
}
}
Geolocation.php
namespace Company\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\EmbeddedDocument
*/
class Geolocation
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\Float
*/
private $latitude;
/**
* #MongoDB\Float
*/
private $longitude;
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set latitude
*
* #param float $latitude
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
}
/**
* Get latitude
*
* #return float $latitude
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* Set longitude
*
* #param float $longitude
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
}
/**
* Get longitude
*
* #return float $longitude
*/
public function getLongitude()
{
return $this->longitude;
}
}
PlaceType.php
namespace Company\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Company\AdminBundle\Form\Type\GeolocationType;
class PlaceType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('title');
$builder->add('geolocation', new GeolocationType());
}
public function getName()
{
return 'place';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Company\StoreBundle\Document\Place',
);
}
}
GeolocationType.php
namespace Company\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class GeolocationType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('latitude')
->add('longitude');
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Company\StoreBundle\Document\Geolocation');
}
public function getName()
{
return 'geolocation';
}
}
Thank you all

I think you should not use dependency in your setter method in this case...
So instead of:
setGeolocation(\Company\StoreBundle\Document\Geolocation $geolocation)
Try this and see if it works correctly:
setGeolocation($geolocation)

Related

I can't upload multiple files in Symfony 3

I don't find solutions for several days to resolve my issue. I want to upload multiple images when I create a Post. My Post entity has a OneToMany relation with Image Entity. I use an embedded form in my PostType. It is a CollectionType of ImageType::class. To manage my upload functionality, I wrote an event listener called ImageUploadListener which injects my custom service called FileUploader.
This listener call a uploadFile function when a preUpdate and a prePersist event is handled. This function call my upload function from my FileUploader to move the image/file... to a target directory and to return a filename.
After that, I try to instantiate an Image Entity and to set the appropriate datas. But it doesn't work, only a file seems to be stored properly but with an additional entry unwanted in my db. (In my Image Table, for one image uploaded, I've got an entry with id 1 for example, a post_id set to NULL and file field set to /tmp/random number).
Please, could you help me ?
Post Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="UserBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="Content", type="text")
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="Image", mappedBy="post", cascade={"persist", "remove"})
*/
private $images;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Post
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set post
*
* #param \
*
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get post
*
* #return \?
*/
public function getUser()
{
return $this->user;
}
/**
* Constructor
*/
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* Add image
*
* #param \UserBundle\Entity\Image $image
*
* #return Post
*/
public function addImage(\UserBundle\Entity\Image $image)
{
$this->images[] = $image;
return $this;
}
/**
* Remove image
*
* #param \UserBundle\Entity\Image $image
*/
public function removeImage(\UserBundle\Entity\Image $image)
{
$this->images->removeElement($image);
}
/**
* Get images
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getImages()
{
return $this->images;
}
}
Image Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="UserBundle\Entity\ImageRepository")
*/
class Image
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $file;
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getFile()
{
return $this->file;
}
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="images")
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
private $post;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set post
*
* #param \UserBundle\Entity\Post $post
*
* #return Image
*/
public function setPost(\UserBundle\Entity\Post $post = null)
{
$this->post = $post;
return $this;
}
/**
* Get post
*
* #return \UserBundle\Entity\Post
*/
public function getPost()
{
return $this->post;
}
}
PostType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
//use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use UserBundle\Entity\Post;
class PostType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content')
->add('images', CollectionType::class, array (
'entry_type' => ImageType::class,
'entry_options' => array('label' => false),
'allow_add' => true,
'allow_delete' => true
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
//'data_class' => 'UserBundle\Entity\Post',
'data_class' => Post::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_post';
}
}
ImageType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use UserBundle\Entity\Image;
class ImageType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, [
'required' => false,
'data_class' => null,
])
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Image::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_image';
}
}
EDIT: ImageUploadListener
//src/UserBundle/EventListener/ImageUploadListener.php
namespace UserBundle\EventListener;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use UserBundle\Entity\User;
use UserBundle\Entity\Post;
use UserBundle\Entity\Image;
use UserBundle\Service\FileUploader;
class ImageUploadListener
{
private $uploader;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
if ($entity instanceof Post) {
$post = $entity;
$images = $post->getImages();
foreach ($images as $image) {
if ($image->getFile() instanceof UploadedFile) {
$imageName = $this->uploader->upload($image->getFile());
// to avoid persisting FileObject in DB
$post->removeImage($image);
$postImage = new Image();
$postImage->setFile($imageName);
$postImage->setPost($post);
$post->addImage($postImage);
}
}
}
return;
}
}
FileUploader
namespace UserBundle\Service;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUploader
{
private $targetDir;
public function __construct($targetDir)
{
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}

Multiple file upload Symfony2 throwing exception

Hello I'm trying to make a file upload with a oneToMany relation between an Annonce and some images; So an Annonce can contain 0 or more images and an image is still attached to an Annonce. I think everything is ok with my entities but I get this error when I submit the form.
> Annonce.php
<?php
namespace MDB\AnnonceBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Annonce
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MDB\AnnonceBundle\Entity\AnnonceRepository")
*/
class Annonce {
public function __construct() {
$this->date = new \Datetime();
$this->categories = new ArrayCollection();
$this->images= new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #ORM\Column(name="date", type="date")
*/
private $date;
/**
* #var float
*
* #ORM\Column(name="prix", type="float")
*/
private $prix;
/**
* #ORM\ManyToOne(targetEntity="MDB\AdresseBundle\Entity\Ville", inversedBy="annonces")
*/
private $ville;
/**
* #ORM\ManyToMany(targetEntity="MDB\AnnonceBundle\Entity\Category", cascade={"persist"})
*/
private $categories;
/**
* #ORM\ManyToMany(targetEntity="MDB\UserBundle\Entity\User")
*
*/
private $wishlist;
/**
* #var boolean
*
* #ORM\Column(name="telAppear", type="boolean")
*/
private $telAppear;
/**
* #ORM\ManyToOne(targetEntity="MDB\UserBundle\Entity\User", inversedBy="annonces")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="MDB\PlatformBundle\Entity\Image", mappedBy="annonce")
*/
private $images;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set titre
*
* #param string $titre
* #return Annonce
*/
public function setTitre($titre) {
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* Set description
*
* #param string $description
* #return Annonce
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* Set prix
*
* #param float $prix
* #return Annonce
*/
public function setPrix($prix) {
$this->prix = $prix;
return $this;
}
/**
* Get prix
*
* #return float
*/
public function getPrix() {
return $this->prix;
}
public function addCategory(Category $category) {
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->categories[] = $category;
return $this;
}
public function removeCategory(Category $category) {
$this->categories->removeElement($category);
}
public function getCategories() {
return $this->categories;
}
public function getDate() {
return $this->date;
}
public function setDate($date) {
$this->date = $date;
}
public function getWishlist() {
return $this->wishlist;
}
public function setWishlist($wishlist) {
$this->wishlist = $wishlist;
}
public function getVille() {
return $this->ville;
}
public function setVille($ville) {
$this->ville = $ville;
}
public function getTelAppear() {
return $this->telAppear;
}
public function setTelAppear($telAppear) {
$this->telAppear = $telAppear;
}
public function getUser() {
return $this->user;
}
public function setUser($user) {
$this->user = $user;
}
public function addImage(Image $image) {
$this->images[] = $image;
$image->setUser($this);
return $this;
}
public function removeImage(Image $image) {
$this->images->removeElement($image);
}
public function getImages() {
return $this->images;
}
}
Image.php
<?php
namespace MDB\PlatformBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Image
*
* #ORM\Table()
* #ORM\Entity
*/
class Image {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $path;
/**
* #var string
*
* #ORM\Column(name="alt", type="string", length=255)
*/
private $alt;
/**
* #ORM\ManyToOne(targetEntity="MDB\AnnonceBundle\Entity\Annonce", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $annonce;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set url
*
* #param string $url
* #return Image
*/
public function setUrl($url) {
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl() {
return $this->url;
}
/**
* Set alt
*
* #param string $alt
* #return Image
*/
public function setAlt($alt) {
$this->alt = $alt;
return $this;
}
/**
* Get alt
*
* #return string
*/
public function getAlt() {
return $this->alt;
}
public function getAnnonce() {
return $this->annonce;
}
public function setAnnonce($annonce) {
$this->annonce = $annonce;
}
public function getAbsolutePath() {
return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
}
public function getWebPath() {
return null === $this->path ? null : $this->getUploadDir() . '/' . $this->path;
}
protected function getUploadRootDir() {
// le chemin absolu du répertoire où les documents uploadés doivent être sauvegardés
return __DIR__ . '/../../../../web/' . $this->getUploadDir();
}
protected function getUploadDir() {
// on se débarrasse de « __DIR__ » afin de ne pas avoir de problème lorsqu'on affiche
// le document/image dans la vue.
return 'uploads/documents';
}
public function getFile() {
return $this->file;
}
public function setFile($file) {
$this->file = $file;
}
}
AnnonceType.php
<?php
namespace MDB\AnnonceBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class AnnonceType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('titre')
->add('description')
->add('date')
->add('prix')
->add('telAppear', 'checkbox', array('required' => false))
->add('ville', new \MDB\AdresseBundle\Form\VilleType(), array('required' => true))
->add('categories')
->add('wishlist')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'MDB\AnnonceBundle\Entity\Annonce'
));
}
/**
* #return string
*/
public function getName() {
return 'mdb_annoncebundle_annonce';
}
}
AnnonceSellType.php (who herit of AnnonceType.php)
<?php
namespace MDB\AnnonceBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use \MDB\PlatformBundle\Form\ImageType;
class AnnonceSellType extends AbstractType {
private $arrayListCat;
public function __construct($arrayListCat) {
$this->arrayListCat = $arrayListCat;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->remove('wishlist')
->remove('date')
->remove('categories')
->add('titre')
->add('description', 'textarea')
->add('prix')
->add('categories', 'choice', array(
'choices' => $this->arrayListCat,
'multiple' => true,
'mapped' => false,
))
->add('images', new ImageType())
;
}
/**
* #return string
*/
public function getName() {
return 'mdb_annoncebundle_annonce_sell';
}
public function getParent() {
return new AnnonceType();
}
}
ImageType.php
<?php
namespace MDB\PlatformBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ImageType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('file')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'MDB\PlatformBundle\Entity\Image'
));
}
/**
* #return string
*/
public function getName() {
return 'mdb_platformbundle_image';
}
}
And I have the following error at the form submit :
Neither the property "images" nor one of the methods
"addImag()"/"removeImag()", "addImage()"/"removeImage()",
"setImages()", "images()", "__set()" or "__call()" exist and have
public access in class "MDB\AnnonceBundle\Entity\Annonce".
I don't understand cause these getter exist in my entity. Anyone have an idea?
Thanks
This error was logic. Just use a collection field type solved the problem

Doctrine: referenced object always null OneToMany / ManyToOne cascade

Please tell me why the value of field 'pool_id' is always NULL in 'Question' Table?
(To add fiels 'Question' in the form used JS)
Pool.php
use Acme\ExamBundle\Entity\Question;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Pool
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="Question", mappedBy="pools",cascade={"persist"})
*/
protected $questions;
public function __construct()
{
$this->questions = new ArrayCollection();
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getQuestions()
{
return $this->questions;
}
/**
* Add question
*
* #param \Acme\ExamBundle\Entity\Question $question
* #return Pool
*/
public function addQuestion(Question $question)
{
/*$question->addPool($this);
$this->questions->add($question);*/
if (!$this->questions->contains($question)) {
$this->questions->add($question);
}
}
/**
* Remove question
*
* #param \Acme\ExamBundle\Entity\Question $question
*/
public function removeQuestion(Question $question)
{
$this->questions->removeElement($question);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set questions
*
* #param \Acme\ExamBundle\Entity\Question $questions
* #return Pool
*/
public function setQuestions(Question $questions = null)
{
$this->questions = $questions;
return $this;
}
}
Question.php
/**
* #ORM\Entity
*/
class Question
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="text")
*/
protected $description;
/**
* #ORM\Column(type="string")
*/
protected $variants;
/**
* #ORM\Column(type="string")
*/
protected $ansver;
/**
* #ORM\ManyToOne(targetEntity="Pool", inversedBy="questions")
* #ORM\JoinColumn(name="pool_Id", referencedColumnName="id")
*/
protected $pools;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set description
*
* #param string $description
* #return Question
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set variants
*
* #param string $variants
* #return Question
*/
public function setVariants($variants='ok')
{
$this->variants = $variants;
return $this;
}
/**
* Get variants
*
* #return string
*/
public function getVariants()
{
return $this->variants;
}
/**
* Set ansver
*
* #param string $ansver
* #return Question
*/
public function setAnsver($ansver)
{
$this->ansver = $ansver;
return $this;
}
/**
* Get ansver
*
* #return string
*/
public function getAnsver()
{
return $this->ansver;
}
/**
* Set pools
*
* #param string $pools
* #return Question
*/
public function setPools($pools)
{
$this->pools = $pools;
return $this;
}
/**
* Get pools
*
* #return string
*/
public function getPools()
{
return $this->pools;
}
}
QuestionType
class QuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('description');
$builder->add('variants');
$builder->add('ansver');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\ExamBundle\Entity\Question',
));
}
public function getName()
{
return 'question';
}
}
PoolType
class PoolType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('questions', 'collection', array(
'type' => new QuestionType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\ExamBundle\Entity\Pool',
));
}
public function getName()
{
return 'pool';
}
}
Thanks for your thoughts. Hope you can help me.
class Pool
/**
* #ORM\OneToMany(targetEntity="Question", mappedBy="pools",cascade={"persist"})
*/
protected $questions;
class Question
/**
* #ORM\ManyToOne(targetEntity="Pool", inversedBy="questions")
* #ORM\JoinColumn(name="pool_Id", referencedColumnName="id")
*/
protected $pools;
Using $pools to represent one pool is a bit confusing. You do understand that a OneToMany relation implies that a question can belong to at most one pool?
The reason that Question::pool_id is null is that you are not calling Question::setPools. Fix this by tweaking Pool:addQuestion
class Pool
public function addQuestion(Question $question)
{
/*$question->addPool($this);
$this->questions->add($question);*/
if (!$this->questions->contains($question)) {
$this->questions->add($question);
$question->setPools($this); // ADD THIS LINE
}
}
Looks like you almost had it in there. Maybe you changed the relation from a ManyToMany?
Also, change the column name from pool_Id to pool_id. SQL is not always consistent when dealing with mixed case names.
Finally, might want to change Ansver to Answer

Symfony2 save embedding collection form

I got problem to save embedded collection form field. Let say I have program, and the program have many levels. In my form type:
ProgramType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProgramType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id','text',array(
'label'=>'Program Code',
))
->add('program','text',array(
'label'=>'Program Name',
))
->add('levels','collection', array(
'type'=>new LevelType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference' => false,
))
->add('save','submit', array(
'label'=>'Save',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Program',
));
}
public function getName()
{
return 'Program';
}
}
LevelType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('level','text',array(
'label'=>'Level Name',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Level',
));
}
public function getName()
{
return 'level';
}
}
I made the form that can add a new collection level field using jquery, following the tutorial from this link, and the result of my form is same with the picture in this link.
Now my problem is I can't save more than 2 levels. The collection form only save 2 levels.
e.g. I want to save a program name as English, and after that I add 3 levels(basic,intermediate,advanced). The levels that saved successfully to database only the first level(basic) and the last level(Advanced). All I want is to save all the levels to database. Can anyone help me?
In addition, here is part of my controller:
DefaultController.php
public function programAction(Request $request)
{
$program = new Program();
$level = new Level();
$program->getLevels()->add($level);
$newProgram = $this->createForm(new ProgramType(),$program);
$newProgram->handleRequest($request);
if($newProgram->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($program);
$em->flush();
return $this->redirect($this->generateUrl('eifl_admin_program'));
}
return $this->render('EiflAdminBundle:Default:program.html.twig',array("new_program_form"=>$newProgram->createView()));
}
UPDATE
Here is my entity class.
Program.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Program
*
* #ORM\Entity
* #ORM\Table(name="tbl_program")
*/
Class Program
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="string")
*/
protected $id;
/**
*#ORM\Column(name="program_name",type="string")
*/
public $program;
/**
* #ORM\OneToMany(targetEntity="Eifl\AdminBundle\Entity\Level", mappedBy="program", cascade={"remove","persist"})
*/
public $levels;
public function __construct(){
$this->levels = new ArrayCollection();
}
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevels(\Eifl\AdminBundle\Entity\Level $level) {
$this->levels[] = $level;
$levels->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levels as $level) {
$level->setProgram($this);
}
$this->levels = $levels;
}
public function removeLevels(\Eifl\AdminBundle\Entity\Level $level)
{
$this->levels->removeElement($level);
}
/**
* #param string $program
* #return string
*/
public function setProgram($program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
/**
* #return string
*/
public function getLevels()
{
return $this->levels;
}
}
Level.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Level
*
* #ORM\Entity
* #ORM\Table(name="tbl_level")
*/
Class Level
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*#ORM\Column(name="level",type="string")
*/
public $level;
/**
* #ORM\ManyToOne(targetEntity="Eifl\AdminBundle\Entity\Program", inversedBy="levels")
* #ORM\JoinColumn(name="program", referencedColumnName="id")
*/
public $program;
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #param string $level
* #return string
*/
public function setLevel($level)
{
$this->level = $level;
return $this;
}
/**
* #param mixed $program
* #return string
*/
public function setProgram(\Eifl\AdminBundle\Entity\Program $program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getLevel()
{
return $this->level;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
}
Check if you put addLevel and setLevel in your program entity
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevel(\Eifl\AdminBundle\Entity\Level $levels) {
$this->levels[] = $levles;
$level->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levles as $level) {
$level->setPogram($this);
}
$this->levels = $levels;
}
remove this code from the controllor
$level = new Level();
$program->getLevels()->add($level);
Your getter and setter $levels in Entity Program are confused...
make :
public function addLevel(Level $level) { //without s
$this->levels[] = $level;
}
public function removeLevel(Level $level) //without s
{
$this->levels->removeElement($level);
}
public function getLevels()
{
return $this->levels;
}
Remove otherspublic function setLevels, public function removeLevels, etc
Like Adene Choubi said, $program->getLevels()->add($level); make no sense too.
Don't know if it's the only trouble but begin with that.

The file "" does not exist after editing the form with file upload

I'm trying to do file upload for my Task entity. I working with two sources:
http://symfony.com/doc/3.4/controller/upload_file.html and Symfony2 file upload step by step but I can't figure out how to keep uploaded file during editing.
I'm not sure if I implemented right the part:
My form was complaining when I tried to edit any Task:
The form's view data is expected to be an instance of class
Symfony\Component\HttpFoundation\File\File, but is a(n) string. You
can avoid this error by setting the "data_class" option to null or by
adding a view transformer that transforms a(n) string to an instance
of Symfony\Component\HttpFoundation\File\File.
So I modified getter getBrochure() in my Task entity which I want to expand of PDF file. Please, see below my getBrochure method This is code of my entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Task
*
* #ORM\Table(name="task")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TaskRepository")
*/
class Task
{
/**
* #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 \DateTime
*
* #ORM\Column(name="datetime", type="datetime")
*/
private $datetime;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories_tasks")
*/
private $categories;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="string")
*
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $brochure;
public function __construct() {
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Task
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set datetime
*
* #param \DateTime $datetime
*
* #return Task
*/
public function setDatetime($datetime)
{
$this->datetime = $datetime;
return $this;
}
/**
* Get datetime
*
* #return \DateTime
*/
public function getDatetime()
{
return $this->datetime;
}
public function getCategories()
{
return $this->categories;
}
public function setCategories(Category $categories)
{
$this->categories = $categories;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getBrochure()
{
//return $this->brochure;
return new File($this->brochure);
}
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
public function __toString() {
return $this->name;
}
}
?>
The result is that I can load edit page, but the field of file upload is empty, there is no information that I uploaded any file. I'm not sure if there should be any information but I see in database that the filename is there and also in web folder there is uploaded file. When I change anything in Task and save of file is cleared and when I try to launch edit page I see:
The file "" does not exis
What is clear for me, because file column for this Task was cleared. So, how to keep file during editing when I don't want to upload new file?
This is my TaskType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class TaskType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')->add('datetime')->add('categories')
->add('description', 'Ivory\CKEditorBundle\Form\Type\CKEditorType', array())
->add('brochure', FileType::class, array('label' => 'Broszurka (PDF)', 'required' => false));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Task'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_task';
}
}
and this is my TaskController (only edit action)
/**
* Displays a form to edit an existing task entity.
*
* #Route("/{id}/edit", name="task_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Task $task)
{
$deleteForm = $this->createDeleteForm($task);
$editForm = $this->createForm('AppBundle\Form\TaskType', $task);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('task_edit', array('id' => $task->getId()));
}
return $this->render('task/edit.html.twig', array(
'task' => $task,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Task Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Task
*
* #ORM\Table(name="task")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TaskRepository")
*/
class Task
{
/**
* #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 \DateTime
*
* #ORM\Column(name="datetime", type="datetime")
*/
private $datetime;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories_tasks")
*/
private $categories;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="string")
*
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $brochure;
public function __construct() {
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Task
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set datetime
*
* #param \DateTime $datetime
*
* #return Task
*/
public function setDatetime($datetime)
{
$this->datetime = $datetime;
return $this;
}
/**
* Get datetime
*
* #return \DateTime
*/
public function getDatetime()
{
return $this->datetime;
}
public function getCategories()
{
return $this->categories;
}
public function setCategories(Category $categories)
{
$this->categories = $categories;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getBrochure()
{
//return $this->brochure;
return new File($this->brochure);
}
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
public function __toString() {
return $this->name;
}
}
?>
You should check the file and then,If the user did not select the file,Select the file name from the database
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$form->handleRequest($request);
$TaskRepo=$em->getRepository('AppBundle:Task');
$Taskdata = $TaskRepo->find($id);///id task
$Taskdata->setName($form->get('name')->getData());
$Taskdata->setDescription($form->get('description(')->getData());
$Taskdata->setDatetime(new \DateTime('now'));
if($form->get('brochure')->getData() != ""){////Check the file selection status
$file2 = $form->get('brochure')->getData();
$fileName2 = md5(uniqid()).'.'.$file2->guessExtension();
$file2->move(
$this->getParameter('brochures_directory'), $fileName2);
$Taskdata->setBrochure($fileName2);
}
$em->flush();
}
OK I found the problem few seconds after I wrote comment and added Task entity code. In getBrochure method I tried to create File object for every Task instance even it hasn't any brochure so the solution is to use:
public function getBrochure()
{
return $this->brochure;
//return new File($this->brochure);
}

Categories