You have to make an AuthorPublicationType form. You put field author as an 'entity' and your others fields...
You make your PublicationType including AuthorPublication (Embedded Forms).
Then you can add new AuthorPublication with prototype and very simple javascript.
Note that when you have to save your entity Publication with an authorPublication attribut null first. And after update Publication with authorPublication defined by using temporary ArrayCollection in Publication and LifecycleCallbacks for example.
EDIT : Example.
My entities are Sport OneToMany SportParam ManyToOne ConfigParam. SportParam is composed of a Sport, a ConfigParam and a value.
I want to create a new Sport with multiple ConfigParam :
SportParamType :
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('ConfigParam', 'entity', array( 'class' => 'PPHBSportScoringBundle:ConfigParam', 'property' => 'nom', 'multiple' => false, 'label'=>'Paramètre' )) ->add('valeur','number',array('precision'=>2)) ; }
SportType
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('nom') ->add('SportParams', 'collection', array( 'type'=> new SportParamType(), 'allow_add' => true, 'allow_delete' => true, 'by_reference'=> false )) ; }
My form.html.twig :
{{ form_start(form) }} {{ form_errors(form) }} {{ form_row(form.nom) }} <ul class=SportParams data-prototype="{{ form_widget(form.SportParams.vars.prototype)|e }}"> {% for param in form.SportParams %} <li> {{ form_errors(param.ConfigParam) }} {{ form_widget(param.ConfigParam) }} {{ form_errors(param.valeur) }} {{ form_widget(param.valeur) }} </li> {% endfor %} </ul> <input type="submit" class="btn btn-primary" /> {{ form_end(form) }}
My Javascript refine because it contains more code (AJAX call). It maybe contains some mistake. If it's not clear have a look to the documentation :
<script type="text/javascript"> var $container = $('ul.SportParams'); // button to add a new SportParam var $addSportParamLink = $('<a href="#" id="ajout_param" class="btn btn-primary btn-xs">Ajouter un paramètre</a>'); var $newLinkLi = $('<li></li>').append($addSportParamLink); $(document).ready(function() { //delete button on each existing SportParam $container.find('li').each(function() { addParamFormDeleteLink($(this)); }); //add button $container.append($newLinkLi); // adding a new form when cliking Add button $addSportParamLink.on('click',function(e) { e.preventDefault(); // évite qu'un #apparaisse dans l'URL var index = $container.children().length-1; addParamForm($container,$newLinkLi); var bAffiche; return false; }); // adding a new form SportParam function addParamForm($container, $newLinkLi) { var $prototype = $container.attr('data-prototype'); var newForm = $prototype.replace(/__name__/g, $container.children().length-1); var $newFormLi = $('<li></li>').append(newForm); $newLinkLi.before($newFormLi); addParamFormDeleteLink($newFormLi); } function addParamFormDeleteLink($paramFormLi){ var $removeFormA = $('<a href="#" class="btn btn-danger btn-xs">Supprimer</a>'); $paramFormLi.append($removeFormA); $removeFormA.on('click', function(e) { e.preventDefault(); $paramFormLi.remove(); }); } }); </script>
Sport Entity call's back :
/** * sport * * @ORM\Table() * @ORM\Entity * @ORM\HasLifecycleCallbacks * @ORM\Entity(repositoryClass="SportRepository") */ class Sport { ...Entity attribut... /** * @var ArrayCollection * * To save Sport without SportParams */ private $SportParamsTMP; ...getter and setter ... /** * @ORM\PrePersist */ public function saveSportParams() { $this->SportParamsTMP = $this->SportParams; $this->SportParams = null; } /** * @ORM\PostPersist */ public function restoreSportParams() { if ($this->SportParamsTMP == !null) { $this->SportParams = $this->SportParamsTMP; } $this->SportParamsTMP = null; } }
Finally the controller 's function to add a new Sport :
public function addAction() { $sport = new Sport(); $form = $this->createForm(new SportType(), $sport); $request = $this->getRequest(); if ($request->getMethod() == "POST") { $form->bind($request); if($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($sport); //saving sport without parameter $em->flush(); //updating sport with parameter $em->flush(); return $this->redirect($this->generateUrl('pphb_sport_liste')); } } return $this->render('PPHBSportScoringBundle:Championnat/Sport:add.html.twig', array( 'form' => $form->createView(), )); }
I hope it help.
Don't know if it's the best way to do it, but it's working for me. If something to improve let me know please.