Embedding collection of forms and adding multiple files in Symfony 2 - forms

I'm following the tutorial here: embed collection of forms symfony2
The example works, but when I try to add filetype field, I got the following error:
Error: Call to a member function move() on a non-object
and don't know why. I can add as many fieldType fields as I want dynamically, but the problem is with the above error.
Here is my code:
TagType.php
namespace Bundle\MyBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TagType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
//$builder->add('name');
$builder->add('file','file',array('label' => 'Load file'));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bundle\MyBundle\Entity\Tag',
));
}
public function getName()
{
return 'tag';
}
}
TaskType.php
namespace Bundle\MyBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('description');
$builder->add('file', 'collection', array(
'type' => new TagType(),
'allow_add' => true,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bundle\MyBundle\Entity\Task',
));
}
public function getName()
{
return 'task';
}
}
Task.php
/**
* Task
*/
class Task
{
/**
* #var string
*/
protected $description;
/**
* #var string
*/
protected $tags;
/**
* #var integer
*/
/**
* #var string
*/
protected $file;
/**
* #var string
*/
protected $path;
protected $id;
public function __construct()
{
$this->tags = new ArrayCollection();
}
/**
* Set description
*
* #param string $description
* #return Task
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set tags
*
* #param string $tags
* #return Task
*/
public function setTags($tags)
{
$this->tags = $tags;
}
/**
* Get tags
*
* #return string
*/
public function getTags()
{
return $this->tags;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set file
*
* #param string $file
* #return Task
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file
*
* #return string
*/
public function getFile()
{
return $this->file;
}
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()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
$user_folder = $this->getId();
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/task/';
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
return $this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
}
Tag.php
class Tag
{
/**
* #var string
*/
private $file;
/**
* #var string
*/
private $path;
/**
* #var string
*/
public $name;
/**
* Set name
*
* #param string $name
* #return Task
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set file
*
* #param string $file
* #return Task
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file
*
* #return string
*/
public function getFile()
{
return $this->file;
}
/**
* Set path
*
* #param string $path
* #return Task
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
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()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
$user_folder = $this->getId();
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/task/';
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
return $this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
}
Controller:
$task = new Task();
$form = $this->createForm(new TaskType(), $task);
$form->add('submit', 'submit', array('label' => 'Create'));
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$task->upload();
//$task->setTags($val);
$em->persist($task);
$em->flush();
$em->clear();
}

Related

Symfony 4 - choice_value FormType not working

This is the method in my controller:
public function parentCat($parentId,$manOrWomen,Request $request){
$pr=new Product();
$products=$this->getDoctrine()->getRepository(Product::class)->listProductsParentCategory($parentId,$manOrWomen);
$form=$this->createForm(SearchProductType::class,$pr,['parentId'=>$parentId,'manOrWomen'=>$manOrWomen]);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$price=$request->request->get('search_product','price');
$name=$request->request->get('search_product','name');
$products=$this->getDoctrine()->getRepository(Product::class)->listSearchedProd($parentId,$manOrWomen,$price['price']
,$name['name']);
//$products=$this->getDoctrine()->getRepository(Product::class)->listSearchedProd($productPrice);
return $this->render('list_products/index.html.twig',['controller_name' => 'ListProductsController',
'form1'=>$form->createView(),'products'=>$products]);
}
else{
return $this->render('list_products/index.html.twig',['controller_name' => 'ListProductsController','form1'=>$form->createView()
,'products'=>$products]);
}
}
This is my Product class(Pruduct Entity):
class Product
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
*
* #ORM\Column(type="string", length=255)
* #Assert\Image(mimeTypes={"image/jpeg","image/png"})
*/
private $image;
/**
* #ORM\Column(type="integer")
*/
private $price;
/**
* #var Category[] | ArrayCollection
* #ORM\ManyToOne(targetEntity="App\Entity\Category")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #var ManOrWomen[] | ArrayCollection
* #ORM\ManyToOne(targetEntity="App\Entity\ManOrWomen")
* #ORM\JoinColumn(name="manorwomen_id", referencedColumnName="id")
*/
private $manorwomen;
/**
* #param \DateTime $createdDate
*
* #ORM\Column(name="createdDate", type="datetime")
*/
private $createdDate;
/**
* Product constructor.
*/
public function __construct()
{
$this->createdDate=new \DateTime('now');
$this->category = new ArrayCollection();
$this->manorwomen=new ArrayCollection();
$this->undercategory=new ArrayCollection();
$this->parentCategory=new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getPrice()
{
return $this->price;
}
public function setPrice($price): self
{
$this->price = $price;
return $this;
}
/**
* #return Category[]|ArrayCollection
*/
public function getCategory()
{
return $this->category;
}
/**
* #param Category[]|ArrayCollection $category
*/
public function setCategory($category): void
{
$this->category = $category;
}
/**
* #return ManOrWomen[]|ArrayCollection
*/
public function getManorwomen()
{
return $this->manorwomen;
}
/**
* #param ManOrWomen[]|ArrayCollection $manorwomen
*/
public function setManorwomen($manorwomen): void
{
$this->manorwomen = $manorwomen;
}
/**
* #return DateTime
*/
public function getCreatedDate(): DateTime
{
return $this->createdDate;
}
/**
* #param DateTime $createdDate
*/
public function setCreatedDate(DateTime $createdDate): void
{
$this->createdDate = $createdDate;
}
This is my FormType for class Product:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$parentId = $options['parentId'];
$manOrWomen=$options['manOrWomen'];
$builder
->add('price',EntityType::class,[
'class'=>Product::class,
'choice_label'=>'price',
'choice_value' => 'price',
'placeholder'=>'Default',
'query_builder' => function (EntityRepository $er) use ($parentId,$manOrWomen) {
return $er->createQueryBuilder('product')
->innerJoin('product.category','c')
->addSelect('c')
->innerJoin('product.manorwomen','m')
->addSelect('m')
->where('c.parent_id=:parentId')
->andWhere('m.id=:manOrWomen')
->setParameters(['parentId'=>$parentId,'manOrWomen'=>$manOrWomen]);
},
'expanded'=>false,
'multiple'=>false
])
->add('name',EntityType::class,[
'class'=>Product::class,
'choice_label'=>'name',
'choice_value' => 'name',
'placeholder'=>'Default',
'query_builder' => function (EntityRepository $er) use ($parentId,$manOrWomen) {
return $er->createQueryBuilder('product')
->innerJoin('product.category','c')
->addSelect('c')
->innerJoin('product.manorwomen','m')
->addSelect('m')
->where('c.parent_id=:parentId')
->andWhere('m.id=:manOrWomen')
->setParameters(['parentId'=>$parentId,'manOrWomen'=>$manOrWomen]);
},
'expanded'=>false,
'multiple'=>false
])
->add('submit',SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Product::class
]);
$resolver->setRequired(
['parentId','manOrWomen']
);
}
I have problem with the request:
"price" => "44" (This is the price for the current item.This is okay.)
"name" => "44" (This must be the name of current item.)
I have 'choice_value'=>'name' but I get the price instead name.
If I change first choice_value with another value,the second choice_value becomes equal to her.Why?
When I remove addSelect from first EntityType works fine,but if I add more one EntityType,then I have the same problem - thirth EntityType value becomes equal to second value ('name').
I would say your entity is returning the price in the getter of the name or the setter of the price is setting the value of name.

symfony3 image is not uploaded

In AppBundle\Etity\Image I have:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="images")
* #ORM\HasLifecycleCallbacks
*/
class Image
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $name;
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Assert\NotBlank
*/
private $path;
/**
* #Assert\Image(maxSize="10M", mimeTypes="image/jpeg", minWidth = 600, minHeight = 400)
* #Assert\NotBlank
*/
private $file;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $alt;
private $temp;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getPath()
{
return $this->path;
}
/**
* #param mixed $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* #return mixed
*/
public function getAlt()
{
return $this->alt;
}
/**
* #param mixed $alt
*/
public function setAlt($alt)
{
$this->alt = $alt;
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
}
public function getUploadRootDir()
{
return __DIR__ . '/../../../../web/' . $this->getUploadDir();
}
public function getUploadDir()
{
return 'images/full';
}
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
// check if we have an old image path
if (isset($this->path)) {
// store the old name to delete after the update
$this->temp = $this->path;
$this->path = null;
} else {
$this->path = 'initial';
}
}
/**
* #return mixed
*/
public function getFile()
{
return $this->file;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->getFile()) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename.'.'.$this->getFile()->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->getFile()) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->getFile()->move($this->getUploadRootDir(), $this->path);
// check if we have an old image
if (isset($this->temp)) {
// delete the old image
unlink($this->getUploadRootDir().'/'.$this->temp);
// clear the temp image path
$this->temp = null;
}
$this->file = null;
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
$file = $this->getAbsolutePath();
if ($file) {
unlink($file);
}
}
}
Which is used in AppBundle\Entity\Post.php like this:
/**
* #ORM\ManyToOne(targetEntity="Image", cascade="all")
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
private $teaserImage;
In AppBundle\Form\Type\PostType.php I have this:
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\Post;
/**
* Defines the form used to create and manipulate blog posts.
*/
class PostType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', null, array('label' => 'Title'))
->add('summary', null, array('label' => 'Summary'))
->add('teaserImage', 'AppBundle\Form\Type\ImageType', array('label' => 'Image'))
->add('content', null, array(
'attr' => array('rows' => 20),
'label' => 'Content',
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Post',
));
}
}
For some reason the images are not uploaded to the specified directory (or anywhere else) and I am not sure what I did wrong. I would be grateful for any insights.
Thank you.
The issue is related to
return __DIR__ . '/../../../../web/' . $this->getUploadDir();
In this case I changed the previous line to:
return __DIR__ . '/../../../web/' . $this->getUploadDir();
That is because my entity is located in src/AppBundle/Entity and to go to root directory it needs to hop 3 directories back.
Also it is a bad idea to hard-code paths in entities. I modified my example accordingly.

How to edit embedded form with file upload in symfony2 and doctrine mongodb

I have document called aboutMe
and it has another embedded document called projects to add many projects (prototype)
The projects has project name and image for the project.
i created a formType for aboutMe document and i embedded the project form inside the aboutMe form to be able to add many projects prototype.
The problem is updating the project->image when the user didn't change the old project image.
doctrine updating the old embedded project document with a null image.
I Need to keep the old image name if the user didn't upload a new one
/**
* #MongoDB\Document
* #MongoDB\HasLifecycleCallbacks
*/
class AboutMeIndex {
/**
* #var integer
*
* #MongoDB\Id(strategy="INCREMENT")
*/
protected $id;
/**
* #var array
*
* #MongoDB\EmbedMany(targetDocument="AboutMeProjects", strategy="set")
*/
protected $projects = array();
public function __construct()
{
$this->projects = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Remove project
*
* #param TimesSell\CoreBundle\Document\Profile\AboutMe\AboutMeProjects $project
*/
public function removeProject(\TimesSell\CoreBundle\Document\Profile\AboutMe\AboutMeProjects $project)
{
$this->projects->removeElement($project);
}
/**
* Get projects
*
* #return Doctrine\Common\Collections\Collection $projects
*/
public function getProjects()
{
return $this->projects;
}
/**
* Add certification
*
* #param TimesSell\CoreBundle\Document\Profile\AboutMe\AboutMeCertifications $certification
*/
public function addCertification(\TimesSell\CoreBundle\Document\Profile\AboutMe\AboutMeCertifications $certification)
{
$this->certifications[] = $certification;
}
//=================================================================================//
public function fileGetter($file){
if(method_exists($this, 'get' . ucfirst($file))) {
return call_user_func(array($this, 'get' . ucfirst($file)));
}
else {
throw new \Exception("Couldn't Find Method name get" . ucfirst($file));
}
}
protected function getUploadRootDir($uploadDir)
{
return __DIR__.'/../../../../../../web/uploads/'.$this->getUploadDir($uploadDir);
}
protected function getUploadDir($uploadDir)
{
return $uploadDir;
}
public function uploadEmbeddedPhotos($file, $uploadDir)
{
if (null === $this->fileGetter($file)) {
return;
}
foreach ($this->fileGetter($file) as $galleryPhoto){
$pictureName = uniqid().'.'.$galleryPhoto->getImage()->guessExtension();
$galleryPhoto->getImage()->move($this->getUploadRootDir($uploadDir),$pictureName);
$this->path = $galleryPhoto->getImage()->getClientOriginalName();
$galleryPhoto->setImage($pictureName);
}
}
public function deleteImage($image, $uploadDir){
#unlink($this->getUploadRootDir($uploadDir).$image);
}
//=================================================================================//
/**
* #MongoDB\EmbeddedDocument
*
*/
class AboutMeProjects {
/**
* #var integer
*
* #MongoDB\Id(strategy="INCREMENT")
*/
protected $id;
/**
* #var string
*
* #MongoDB\String
*/
protected $projectName;
/**
* #var string
*
* #Assert\Image(
* maxSize = "20000k",
* mimeTypes = {"image/gif", "image/jpeg", "image/png"},
* mimeTypesMessage = "Please upload a valid picture"
* )
* #Assert\Regex(
* pattern="/[a-zA-Z0-9]+/",
* match=true,
* message="Special characters are not allowed"
* )
*
* #MongoDB\String
*/
protected $image;
/**
* #var string
*
* #MongoDB\String
*/
protected $desc;
/**
* Get id
*
* #return int_id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set projectName
*
* #param string $projectName
* #return self
*/
public function setProjectName($projectName)
{
$this->projectName = $projectName;
return $this;
}
/**
* Get projectName
*
* #return string $projectName
*/
public function getProjectName()
{
return $this->projectName;
}
/**
* Set image
*
* #param string $image
* #return self
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string $image
*/
public function getImage()
{
return $this->image;
}
/**
* Set desc
*
* #param string $desc
* #return self
*/
public function setDesc($desc)
{
$this->desc = $desc;
return $this;
}
/**
* Get desc
*
* #return string $desc
*/
public function getDesc()
{
return $this->desc;
}
}
class AboutMeIndexType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName')
->add('projects', 'collection', array(
'type' => new ProjectsType(),
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AboutMeIndex'
));
}
/**
* #return string
*/
public function getName()
{
return 'AbourMe';
}
}
class ProjectsType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('projectName','text',array('attr'=> array('class'=>'form-control', 'placeholder' => 'Project name') ))
->add('image','file',array('data_class' => null,'attr'=> array('class'=>'form-control col-lg-2 file-inputs') ))
->add('desc','textarea',array('attr'=> array('class'=>'form-control', 'data-provide' => 'markdown', 'placeholder' => 'Description') ))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AboutMeProjects'
));
}
/**
* #return string
*/
public function getName()
{
return 'ProjectsType';
}
}
And here's the controller that i want to be able to keep the old image
/**
* Edits an existing aboutMeIndex document.
*
* #Route("/profile/about-me/update", name="profile_about_me_update")
* #Method("PUT")
* #Template()
*/
public function updateAction(Request $request)
{
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$user = $this->getUser();
$entity = $dm->getRepository('AboutMeIndex')->findOneBy(array('user.$id' => (int)$user->getId()));
if (!$entity) {
throw $this->createNotFoundException('Unable to find entity Document.');
}
$editForm = $this->createForm(new AboutMeIndexType(), $entity);
$editForm->submit($request);
if ($editForm->isValid()) {
if($entity->getProjects()->getImage() is newImage){
$entity->uploadEmbeddedPhotos('projects', 'profile/aboutMe/');
}else{
// Keep the old Image
}
$dm->persist($entity);
$dm->flush();
}
}

Doctrine Mongo document + Embed a Collection of Forms

Project with symfony 2 and mongoDB.
I'm following this tutorial: http://symfony.com/doc/current/cookbook/form/form_collections.html
But once I save the form I get this error:
Cannot create a DBRef, the document is not an object
Line of crash:
https://github.com/doctrine/mongodb-odm/blob/master/lib/Doctrine/ODM/MongoDB/DocumentManager.php#L691
Form code:
namespace Fonts\FontsBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class FamilyType extends AbstractType
{
public function __construct($dm)
{
$this->dm = $dm;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text', array('max_length' => 50, 'error_bubbling' => true));
$builder->add('fonts', 'collection', array(
'type' => new FontType($this->dm),
'allow_add' => true,
'by_reference' => false,
));
}
public function getName()
{
return 'Family';
}
}
Controller code:
namespace Fonts\FontsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Fonts\FontsBundle\Document\Family;
use Fonts\FontsBundle\Document\Font;
use Fonts\FontsBundle\Form\Type\FamilyType;
class FamilyBackendController extends BaseController
{
public function newAction()
{
try {
$family = new Family();
$form = $this->createForm(new FamilyType($this->getMongoService()), $family);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$this->persist($family);
$this->get('session')->setFlash('notice', 'Item successfully created.');
return ($request->request->get('save') === 'Save') ?
new RedirectResponse($this->generateUrl('backend_familys_list')) :
new RedirectResponse($this->generateUrl('backend_familys_new'));
}
}
return $this->render('FontsBundle:Backend:newFamily.html.twig', array(
'form' => $form->createView(),
));
} catch(\Exception $e) {
return new Response($e->getMessage());
}
}
}
Document:
<?php
namespace Fonts\FontsBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Fonts\FontsBundle\Service\SlugService;
/**
* #MongoDB\Document(repositoryClass="Fonts\FontsBundle\Repository\FamilyRepository")
*/
class Family
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
* #MongoDB\UniqueIndex(safe=true)
* #Assert\NotBlank(message="FamilyName value should not be blank.")
* #Assert\MinLength(limit=3,message="FamilyName must have at least {{ limit }} characters.")
* #Assert\MaxLength(limit=50,message="FamilyName must have maximum {{ limit }} characters.")
*/
protected $name;
/**
* #MongoDB\ReferenceMany(targetDocument="Font", simple=true)
* #Assert\NotBlank(message="Fonts should not be blank.")
*/
protected $fonts;
/**
* #MongoDB\String
* #MongoDB\UniqueIndex(safe=true)
*/
protected $slug;
/**
* #MongoDB\Int
*/
protected $createdAt;
public function __construct()
{
$this->fonts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Family
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string $name
*/
public function getName()
{
return $this->name;
}
/**
* Add fonts
*
* #param Fonts\FontsBundle\Document\Font $fonts
*/
public function addFonts(\Fonts\FontsBundle\Document\Font $fonts)
{
$this->fonts[] = $fonts;
}
/**
* Set fonts
*
* #param Doctrine\Common\Collections\Collection $fonts
*/
public function setFonts($fonts)
{
$this->fonts = $fonts;
}
/**
* Get fonts
*
* #return Doctrine\Common\Collections\Collection $fonts
*/
public function getFonts()
{
return $this->fonts;
}
/**
* Set slug
*
* #param string $slug
* #return Family
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string $slug
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set createdAt
*
* #param int $createdAt
* #return Family
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return int $createdAt
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* #MongoDB\PrePersist
*/
public function prePersist()
{
$this->setCreatedAt(time());
$slugService = new SlugService();
$this->setSlug($slugService->slug($this->getName()));
}
/**
* #MongoDB\PreUpdate
*/
public function preUpdate()
{
$slugService = new SlugService();
$this->setSlug($slugService->slug($this->getName()));
}
}
The form crash when I try to persist the object in the controller action_
$this->persist($family);
I tried lot of options but no one with good results. If you have some idea, please reply.

Symfony 2 Embedded forms using one to many db relationship

I'm have a problem embedding forms from different entities in one form, my form is being displayed with firstname [input] lastname [input] address - but the address has no input next to it.
Basically I want to create a form where the user can add first name, last name, address1, address2, city, country ect and submit it it as one, although it's different tables.
The main form is no problem the only issue I'm having is with the second embedded form. Any help would be greatly appreciated.
Here is my code:
Member class:
namespace Pomc\MembersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Pomc\MembersBundle\Entity\Member
*/
class Member
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $firstName
*/
private $firstName;
/**
* #var string $lastName
*/
private $lastName;
/**
* #var Pomc\MembersBundle\Entity\Address
*/
private $address;
/**
* #var Pomc\MembersBundle\Entity\Telephone
*/
private $telephone;
public function __construct()
{
$this->address = new \Doctrine\Common\Collections\ArrayCollection();
$this->telephone = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* #param string $lastName
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Add address
*
* #param Pomc\MembersBundle\Entity\Address $address
*/
public function addAddress(\Pomc\MembersBundle\Entity\Address $address)
{
$this->address[] = $address;
}
/**
* Get address
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAddress()
{
return $this->address;
}
/**
* Add telephone
*
* #param Pomc\MembersBundle\Entity\Telephone $telephone
*/
public function addTelephone(\Pomc\MembersBundle\Entity\Telephone $telephone)
{
$this->telephone[] = $telephone;
}
/**
* Get telephone
*
* #return Doctrine\Common\Collections\Collection
*/
public function getTelephone()
{
return $this->telephone;
}
}
Here is the address class:
namespace Pomc\MembersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Pomc\MembersBundle\Entity\Address
*/
class Address
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $addressType
*/
private $addressType;
/**
* #var string $firstLine
*/
private $firstLine;
/**
* #var string $secondLine
*/
private $secondLine;
/**
* #var string $city
*/
private $city;
/**
* #var string $postCode
*/
private $postCode;
/**
* #var string $country
*/
private $country;
/**
* #var Pomc\MembersBundle\Entity\Member
*/
private $member;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set addressType
*
* #param string $addressType
*/
public function setAddressType($addressType)
{
$this->addressType = $addressType;
}
/**
* Get addressType
*
* #return string
*/
public function getAddressType()
{
return $this->addressType;
}
/**
* Set firstLine
*
* #param string $firstLine
*/
public function setFirstLine($firstLine)
{
$this->firstLine = $firstLine;
}
/**
* Get firstLine
*
* #return string
*/
public function getFirstLine()
{
return $this->firstLine;
}
/**
* Set secondLine
*
* #param string $secondLine
*/
public function setSecondLine($secondLine)
{
$this->secondLine = $secondLine;
}
/**
* Get secondLine
*
* #return string
*/
public function getSecondLine()
{
return $this->secondLine;
}
/**
* Set city
*
* #param string $city
*/
public function setCity($city)
{
$this->city = $city;
}
/**
* Get city
*
* #return string
*/
public function getCity()
{
return $this->city;
}
/**
* Set postCode
*
* #param string $postCode
*/
public function setPostCode($postCode)
{
$this->postCode = $postCode;
}
/**
* Get postCode
*
* #return string
*/
public function getPostCode()
{
return $this->postCode;
}
/**
* Set country
*
* #param string $country
*/
public function setCountry($country)
{
$this->country = $country;
}
/**
* Get country
*
* #return string
*/
public function getCountry()
{
return $this->country;
}
/**
* Set member
*
* #param Pomc\MembersBundle\Entity\Member $member
*/
public function setMember(\Pomc\MembersBundle\Entity\Member $member)
{
$this->member = $member;
}
/**
* Get member
*
* #return Pomc\MembersBundle\Entity\Member
*/
public function getMember()
{
return $this->member;
}
}
Here is the memberform:
namespace Pomc\MembersBundle\Form\Type;
use \Symfony\Component\Form\AbstractType;
use \Symfony\Component\Form\FormBuilder;
class MemberType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('firstName');
$builder->add('lastName');
$builder->add('address','collection', array( 'type' => new AddressType(),
'allow_add' => true,
'prototype' => true,
'by_reference' => false,
));
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Pomc\MembersBundle\Entity\Member');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
function getName()
{
return 'member';
}
}
Here is the address form:
namespace Pomc\MembersBundle\Form\Type;
use \Symfony\Component\Form\AbstractType;
use \Symfony\Component\Form\FormBuilder;
class AddressType extends AbstractType
{
public function buildForm(Formbuilder $builder, array $options)
{
$builder->add('firstLine');
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Pomc\MembersBundle\Entity\Address');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
function getName()
{
return 'address';
}
function getIdentifier()
{
return 'address';
}
}
Here is the controller:
namespace Pomc\MembersBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use \Pomc\MembersBundle\Entity\Member;
use \Symfony\Component\HttpFoundation\Request;
use \Pomc\MembersBundle\Form\Type\MemberType;
class DefaultController extends Controller
{
public function indexAction($name)
{
return $this->render('PomcMembersBundle:Default:index.html.twig', array('name' => $name));
}
public function newAction(Request $request)
{
$member = new Member();
$form = $this->get('form.factory')->create(new MemberType());
if($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if($form->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($member);
$em->flush();
}
}
return $this->render('PomcMembersBundle:Default:new.html.twig', array( 'form'=> $form->createView(),));
}
}
Here is the template:
<form action="{{ path('member_new') }}" method="post" {{ form_enctype(form)}}>
{{ form_widget(form) }}
<div>
{{ form_row(form.address)}}
</div>
<input type="submit" />
</form>
Been a long time user of this site, but this is my first question.
Thank you
Oh I faced the same problem, but I found the solution, hope this will help you :-)
You're forgetting to add an Address object to the member entity.
In your action you'll need to do the following:
$member = new Member();
$member->addAddress(new Address());
$form = $this->createForm(new MemberType(), $member);
And then in your template:
{% for address in form.address %}
{{ form_widget(address.firstLine) }}
{% endfor %}
Btw your 'firstline' widget doesn't relate to an entity property.
Btw if you called addAddress two times, you would of course get two 'firstline' widgets in your form.
Hope this works. best of luck.
Llewellyn, do you mean something like thid:
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('imBundle:Inspecciones')->find($id);
$entity_valores = $em->getRepository('imBundle:ValoresInspecciones')->findByInspecciones($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Inspecciones entity.');
}
$entity->setValoresInspecciones($entity_valores);
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($id);
return $this->render('imBundle:Inspecciones:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}