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 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;
}
}
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
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
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.
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);
}