Creating embedded forms in Symfony 2.0
If you are looking to create a form from 2 or more entities, like i have been then you probably find the documentation somewhat lacking and confusing. If you follow the guides you can get your form working if you start from the least dependant entity making that the first entity you use to build your form from your controller. I find if you start from the most dependant you usually get all manner of errors.
What i have is a basic forum i am working on, in my current form situation i have 3 entities, Board entity, a Thread entity and a Post entity. If you start by creating your form with Board then chances are it won’t work, i created my forms in separate ‘Type’ classes (PostType.php ThreadType.php and BoardType.php) and then created my PostType form in my controller, which then sorts out its dependencies on its own.
My controller class is very basic and not very polished, but here is what it looks like:
// In your controller public function createAction($board_id) { $board = $this->getDoctrine() ->getRepository('CodeConsortiumForumBundle:Board') ->find($board_id); $post = new post(); $form = $this->createForm(new PostType(), $post); $formHandler = new PostFormHandler($form, $this->getRequest(), $post); if ($formHandler->process()) { $em = $this->get('doctrine')->getEntityManager(); $em->persist($form->getData()); $em->flush(); //return $this->redirect($this->generateUrl('thread_show', array( 'thread_id' => $thread_id ) )); } else { return $this->render('CodeConsortiumForumBundle:Thread:create.html.twig', array( 'board' => $board, 'form' => $form->createView(), )); } } }
It probably looks somewhat messy as i say, its a rough work in progress, but it works, and here are the FormTypes i used, starting with; PostType.php:
options = $options; } public function buildForm(FormBuilder $builder, array $options) { $builder->add('Thread', new ThreadType($this->options) ); $builder->add('body'); } // for creating and replying to threads public function getDefaultOptions(array $options) { return array( 'data_class' => 'CodeConsortium\ForumBundle\Entity\Post', ); } public function getName() { return 'Post'; } }
ThreadType.php
options = $options; } public function buildForm(FormBuilder $builder, array $options) { $builder->add('Thread', new ThreadType($this->options) ); $builder->add('body'); } // for creating and replying to threads public function getDefaultOptions(array $options) { return array( 'data_class' => 'CodeConsortium\ForumBundle\Entity\Post', ); } public function getName() { return 'Post'; } }
And lastly, the BoardType.php
options = $options; } public function buildForm(FormBuilder $builder, array $options) { } // for creating and replying to threads public function getDefaultOptions(array $options) { return array( 'data_class' => 'CodeConsortium\ForumBundle\Entity\Board', ); } public function getName() { return 'Board'; } }
Notice how i added a constructor, this is so we can easily chain feed in from the first object any changes to the form, such as wether we wish to bother including a field or not, we can opt to have any field removed if the form is used under different circumstances such as different access controls for different users with different permissions or if the form is used in another way on the site.
One more thing we need to make sure, is that all the references in our entities make use of the cascade={“persist”} option so that it maintains relations between the relevant forms as new ones get processed, so if say a thread does not exist then when processing the post, it should create one and do the association for us.
Here is an example of a snip from my Post entity class Post.php
/** * @ORM\ManyToOne(targetEntity="Thread", inversedBy="posts", cascade={"persist"}) * @ORM\JoinColumn(name="thread_id", referencedColumnName="id") */ protected $thread;
Lastly, you will need a formHandler if your not working in your controller, if using my example code then the postFormHandler.php looks like this:
form = $form; $this->request = $request; $this->post = $post; } public function process() { //$form = $this->createForm(new PostType(), $this->post); //$this->form->setData($this->post); if ($this->request->getMethod() == 'POST') { $this->form->bindRequest($this->request); if ($this->form->isValid()) { return true; } } return false; } }
time to update this blog post. hmm I ran into problems for creating multi level nested collections into a form and the js that goes with it.
@cordoval actually i have not worked with form Collections yet, however if your working with plain regular forms this should still work. If you have a working form collections example you would like me to post on here though i will do that.
Your ThreadType.php code shows PostType.php. Very interesting though.
I will right away grab your rss as I can’t find your e-mail subscription link or e-newsletter service. Do you have any? Please let me recognise in order that I could subscribe. Thanks. Phillipsburg Middle School’s second marking period http://www.zc18.com.cn/plus/view.php?aid=10643
We all have the issues creating multilevel nested embeeded forms. Should be nice to have a working example with a form to put nested data with add and remove in each level. For example aform to add movies to a cinema with the entities cinema->movies->days->hours