symfony2- Embedded Controller can't store value to database - symfony

I can't store value to database if use Embedded Controller, if I don't use embedded controller I can store it.
Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="tbl_mail")
*/
class Mail
{
/**
* #ORM\Column(type="string", length=255)
*/
public $email;
}
Controller:
class SendContactController extends Controller
{
public function Email(Request $request)
{
$mail = new Mail();
$form = $this->createFormBuilder($mail)
->add('email','email')
->getForm();
$form->handleRequest($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($mail);
$em->flush();
//return $this->redirectToRoute('');
}
return $this->render('frontend/mail.html.twig', array('form' => $form->createView()));
}
}
View (twig template):
<div>
{{ form_start(form) }}
{{ form_widget(form.email) }}
<input type="submit" class="btn btn-primary" value="OK" />
{{ form_end(form) }}
</div>
i render this controller in my base.html.twig
{%render(controller('AppBundle\Controller\FrontEnd\SendContactController::Email ')) %}
when I press ok to submit nothing happens.

Related

Symfony, minMessage from Constraints not displaying

I'm new to Symfony and just making my first Project with the 3.4 version.
I have a problem when using Constraints on my form, I'm trying to make a field having some Length constraints, but as I test it with a short value that shouldn't be accepted, the form displays a default message in a speech bubble instead of the one I put in minMessage, and instead of displaying a message for maxLength, it just don't let me put more thant 20 characters in the field instead of displaying a message if there is more than 20. I'm not sure this is how it should work (???) and if it is can I manage error messages with another technic? I'm just putting constraints on a single field until I resolved this issue that's why the others one don't have any.
Here's the Controller Code :
<?php
namespace AppBundle\Controller;
use AppBundle\Form\UserType;
use AppBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
/**
* Class UserController
* #package AppBundle\Controller
* #Route("/user")
*/
class UserController extends Controller
{
/**
* #return \Symfony\Component\HttpFoundation\Response
* #Route("/add", name="add_user")
*/
public function addAction(Request $request)
{
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if($form->isSubmitted() && $form->isValid()){
$user->setPwd(md5($user->getPwd()));
$em->persist($user);
$em->flush();
return $this->render('#App/User/show.html.twig', array(
'user'=>$user
));
}
return $this->render('#App/User/add.html.twig', array(
'form'=>$form->createView()
));
}
}
UserType code:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class UserType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('pseudo')
->add('nom')
->add('prenom')
->add('enseignant')
->add('pwd', PasswordType::class)
->add('confirm_pwd', PasswordType::class);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_user';
}
}
User code (I'll just pute the code where my constraints are):
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User
{
/**
* #var string
*
* #ORM\Column(name="pseudo", type="string", length=100, unique=true)
* #Assert\Length(
* min = 8,
* max = 20,
* minMessage = "Your first name must be at least {{ limit }} characters long",
* maxMessage = "Your first name cannot be longer than {{ limit }} characters"
* )
*/
private $pseudo;
}
And the twig page that I render:
{% extends 'base.html.twig' %}
{% block body %}
<h1 class="display-4">Formulaire d'inscription</h1>
{{ form_start(form) }}
{{ form_row(form.pseudo, {'label': 'Pseudo', 'attr':
{'placeholder': 'Pseudonyme ...'}}) }}
{{ form_row(form.nom, {'label': 'Nom', 'attr':
{'placeholder': 'Nom ...'}}) }}
{{ form_row(form.prenom, {'label': 'Prenom', 'attr':
{'placeholder': 'Prenom ...'}}) }}
{{ form_row(form.enseignant, {'label': 'Enseignant'}) }}
{{ form_row(form.pwd, {'label': 'Mot De Passe', 'attr':
{'placeholder': 'Mot De Passe'}}) }}
{{ form_row(form.confirm_pwd, {'label': 'Confirmation Mot De Passe', 'attr':
{'placeholder': 'Mot De Passe'}}) }}
<button type="submit" class="btn btn-success">Inscription </button>
{{ form_end(form) }}
{% endblock %}
Do you know what should I do to make it work?
I am sorry if my english is bad, and tell me if you need more or less code!
Have a nice day and thank you !
PS: I'm a real beginner with Symfony so be nice pls :)
PSbis: I already checked the documentation and I made everything that should have been made, but I could have missed something (even if I checked multiple times)
Edit: I added EqualTo and Unique constraints on other fields, and both error messages are displaying, so this is comming from the Length constraint and I really don't know why
You're forgetting to add form_errors. Check this out.

My Symfony form errors are always global. Not linked to the specific field like supposed to

I have a Symfony form where I get errors when the fields are blanks.
I already try to set error_bubbling to false but it still not work (And is supposed to be false by default)
This is my code where I remove everything that is not necessary:
Controller:
/**
* #Route("/add", name="add")
*/
public function add(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { } else { }
return $this->render('blog/add.html.twig', array('form' => $form->createView()));
}
Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $Title;
/**
* #ORM\Column(type="text")
* #Assert\NotBlank
*/
private $Content;
...
FormType:
namespace App\Form;
use App\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title')
->add('content');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Post::class
]);
}
}
Form:
{% extends "base.html.twig" %}
{% block body %}
<h2>
Create a post
</h2>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" class="btn" value="Create" />
{{ form_end(form) }}
{% endblock %}
When I look at the object after the post all the errors are linked to the form and there's no errors in the childs (The form fields).
Does anyone know what can be wrong?
In buildForm(), you need to capitalize your fields. They are case sensitive and they are capitalized in your database.

Symfony2 error on render controller with relation OneToOne on same table

I have a problem to get a object from a controller called by the render controller method.
This is my Entity with the self OneToOne relation :
class Family
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Family")
* #ORM\JoinColumn(name="brother_id", referencedColumnName="id")
**/
private $brother;
/**
* #ORM\Column(type="string", length=100)
*/
private $label;
}
This is my action:
/**
* #Template()
*/
public function testAction()
{
$em = $this->getDoctrine()->getManager();
$brothers = $em->getRepository('FifaAdminBundle:Family')->findAll();
return array(
'brothers' => $brothers,
);
}
My view
{% for brother in brothers %}
{{ brother.id }} - {{ brother.label }}
<hr />
{% render controller('AdminBundle:Test:show', {'brother': brother}) %}
<hr />
{{ render(controller('AdminBundle:Test:show', { 'brother': brother })) }}
<hr />
{% endfor %}
My other controller
public function showAction($brother)
{
if (is_object($brother))
{
return new \Symfony\Component\HttpFoundation\Response('OK');
}
else
{
var_dump($brother);
return new \Symfony\Component\HttpFoundation\Response('KO');
}
}
The first element isgood.
But if it has a brother_id, this brother in not load by the showAction.
It gives me this:
array(1) { ["__isInitialized__"]=> string(1) "1" }
Please help me.
You probably want to use the #ParamConverter annotation in your case.
Your controller would go as follow:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Admin\Bundle\Entity\Family;
/**
* #Route("/show/{id}")
* #ParamConverter("family", class="AdminBundle:Family")
*/
public function showAction(Family $brother)
{
//Do your stuff
}
And the view:
{% for brother in brothers %}
{{ brother.id }} - {{ brother.label }}
<hr />
{{ render(controller('AdminBundle:Test:show', { 'brother': brother.id })) }}
<hr />
{% endfor %}
Note that if no Family object is found, a 404 Response is generated. So you don't need to check if $brother is an object or not in your controller.
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
Thank you cheesemacfly
Indeed, it's work with the #ParamConverter
But it is wird because if I remove the OneToOne relation, it works without #ParamConverter

How to submit multiple forms of same type with one button in symfony2

I have the todolist where i display three forms of task type
$task1 = new Task();
$form1 = $this->createForm(new MyForm('f1'), $task1);
$task2 = new Task('fo');
$form2 = $this->createForm(new MyForm('f2'), $task2);
$task3 = new Task();
$form3 = $this->createForm(new MyForm('f3'), $task3);
Now the problem is i have one submit button only . How can i persist these three tasks within one controller. and user can add more forms dynamically as well.
so what the way to solve this
Create a Form Model class — like TaskList — that holds a collection of Tasks. Then create TaskListType that holds a collection of TaskTypes. This way you'll have one form with as many tasks as you want.
For the sake of completeness find below a complete example.
You should create a new Model that represents the desired form. The point is that you probably don't want to affect Doctrine (eg. see doctrine:schema:update command). It might try to create a table for an entity that doesn't really exist. To avoid that, just put your model class under the Model folder (\src\Acme\Bundle\DemoBundle\Model\TaskList.php).
Assume that the following is your TaskType form class:
<?php
namespace Acme\Bundle\DemoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', null, array('read_only' => true))
->add('name');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Acme\Bundle\DemoBundle\Entity\Task'
)
);
}
/**
* #return string
*/
public function getName()
{
return 'acme_demo_task';
}
}
This should be your TaskList model class:
<?php
namespace Acme\Bundle\DemoBundle\Model;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Class TaskList
* #package Acme\Bundle\DemoBundle\Model
*
* #ORM\Entity()
*/
class TaskList
{
/**
* #var \Doctrine\Common\Collections\ArrayCollection
* #ORM\ManyToMany(targetEntity="\Acme\Bundle\DemoBundle\Entity\Task")
*/
private $tasks;
public function __construct()
{
$this->tasks = new ArrayCollection();
}
/**
* #param \Acme\Bundle\DemoBundle\Entity\Task $task
* #return $this
*/
public function addTask(\Acme\Bundle\DemoBundle\Entity\Task $task)
{
$this->tasks[] = $task;
return $this;
}
/**
* #param \Acme\Bundle\DemoBundle\Entity\Task $task
* #return $this
*/
public function removeTask(\Acme\Bundle\DemoBundle\Entity\Task $task)
{
$this->tasks->remove($task);
return $this;
}
/**
* #return ArrayCollection
*/
public function getTasks()
{
return $this->tasks;
}
/**
* #param \Doctrine\Common\Collections\Collection $tasks
* #return $this
*/
public function setTasks(\Doctrine\Common\Collections\Collection $tasks)
{
$this->tasks = $tasks;
return $this;
}
/**
* #param \Knp\Component\Pager\Pagination\PaginationInterface $pagination
* #return $this
*/
public function setFromPagination(\Knp\Component\Pager\Pagination\PaginationInterface $pagination)
{
foreach ($pagination as $task) {
$this->addTask($task);
}
return $this;
}
}
And find below the TaskListType class:
<?php
namespace Acme\Bundle\DemoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskListType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'tasks',
'collection',
array(
'type' => new \Acme\Bundle\DemoBundle\Form\TaskType(),
)
)
->add('save', 'submit');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Acme\Bundle\DemoBundle\Model\TaskList'
)
);
}
/**
* #return string
*/
public function getName()
{
return 'acme_demo_task_list';
}
}
And your services.yml (optional):
services:
acme.demo.form.type.task_list:
class: Acme\Bundle\DemoBundle\Form\TaskListType
tags:
- { name: form.type, alias: acme_demo_task_list }
And a sample controller:
public function indexAction($page)
{
ini_set('xdebug.max_nesting_level', 300); // this might be useful with deeply nested forms
$search = $this->getRequest()->get(
'search',
array(
'name' => '',
'date' => '',
'lang' => $this->container->getParameter('acme_core.default_lang')
)
);
/**
* #var \Doctrine\ORM\EntityManager $em
*/
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$em->getRepository('AcmeDemoBundle:Task')->getQueryFilteringByLangNameAndDate(
$search['lang'],
$search['name'],
$search['date'] != '' ? new \DateTime($search['date']) : null
),
$page,
$this->getRequest()->get('elementsPerPage', 10)
);
$taskList = new TaskList();
$taskList->setFromPagination($pagination);
$form = $this->createForm('acme_demo_task_list', $taskList); // "acme_demo_task_list" has been defined in the services.yml file
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
foreach ($form->getData() as $task) {
$em->merge($task);
}
$em->flush();
}
return $this->render(
'AcmeDemoBundle:Task:index.html.twig',
array(
'search' => $search,
'pagination' => $pagination,
'form' => $form->createView()
)
);
}
I hope this helps!
We followed the exapmle shown by 'Francesco Casula' and it worked perfectly.
For orur purposes we didn't need pagination, so this is how we filled our collection (in the controller):
$entities = $em->getRepository('AcmeBundle:Stock')->findAll();
$stockList = new StockList(); // This is our model, what Francesco called 'TaskList'
foreach ($entities as $entity) {
$stockList->addStock($entity);
}
// StockListType() is what Francesco called TaskListType
$form = $this->createForm(new StockListType(), $stockList, array(
'action' => $this->generateUrl('stock_take_update'),
'method' => 'POST',
'attr' => array('class' => 'form-horizontal'),
));
For those who needs to customise the output of the form collections, we managed to access the subform by iterating on {% for form in forms.children.stocks %}. 'Stocks' being the name of the field in our form builder (in StockListType):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'stocks', 'collection', array(
'type' => new StockType(),
)
)
->add('submit', 'submit')
;
}
This is what we ended up using in our twig view:
{{ form_start(forms) }}
<table class="table table-stripped table-hover">
<thead>
<th>#</th>
<th>Name</th>
<th>Code</th>
<th>Location</th>
<th>Total</th>
<th>Updated Toal</th>
</thead>
<tbody>
{% set counter = 1 %}
{% for form in forms.children.stocks %}
<tr>
<div class="hidden">
{{ form_widget(form.name) }}
{{ form_widget(form.code) }}
{{ form_widget(form.location) }}
{{ form_widget(form.available) }}
{{ form_widget(form.assigned) }}
{{ form_widget(form.minLevel) }}
{{ form_widget(form.type) }}
{{ form_widget(form.colourCode) }}
</div>
<td>{{ counter }}</td>
<td>
{% if form.vars.data.name is defined %}
{{ form.vars.data.name }}
{% endif %}
</td>
<td>
{% if form.vars.data.code is defined %}
{{ form.vars.data.code }}
{% endif %}
</td>
<td>
{% if form.vars.data.location is defined %}
{{ form.vars.data.location }}
{% endif %}
</td>
<td>
{% if form.vars.data.total is defined %}
{{ form.vars.data.total }}
{% endif %}
</td>
<td>{{ form_widget(form.total) }}</td>
</tr>
{% set counter = counter + 1 %}
{% endfor %}
</tbody>
</table>
{{ form_widget(forms.submit) }}
{{ form_end(forms) }}

New entities added using Symfony2 many-to-one embedded forms not being saved

This is related to my earlier question about embedded forms. As advised, I switched to a twig template and now everything is displaying as expected and the link to add a new empty form is working correctly. The problem is that when I try to save a new record, it doesn't work (although edits to existing entities are saved).
There are several points where I may have gone wrong, so I'm going to ask questions as I go along.
Here's some background:
A study can have many participants (i.e. a Study entity has a OneToMany relationship with the entity Participant). In the database, each Participant record has the foreign key link from the column "study" to the "study_id" column of a record in the Study table, making it the owning side of the relation. The annotation in the classes should reflect this relationship.
Study class:
namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* CRUK\MyBundle\Entity\Study
*
* #ORM\Table(name="study")
* #ORM\Entity
*/
class Study
{
/**
* #var integer $id
*
* #ORM\Column(name="study_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string $studyName
*
* #ORM\Column(name="study_name", type="string", length=50, nullable=false)
*/
private $studyName;
/*
* #ORM\OneToMany(targetEntity="Participant", mappedBy="study", cascade={"persist"})
*
* #var ArrayCollection $participants
*/
protected $participants;
public function __construct()
{
$this->participants = new ArrayCollection();
}
public function setParticipants(ArrayCollection $participants)
{
foreach($participants as $participant) {
$participant->setStudy($this);
}
$this->participants = $participants;
}
/**
* #return ArrayCollection A Doctrine ArrayCollection
*/
public function getParticipants()
{
return $this->participants;
}
}
My Participant class:
namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* CRUK\SampleTrackingBundle\Entity\Participant
*
* #ORM\Table(name="participant")
* #ORM\Entity
*/
class Participant
{
/**
* #var integer $id
*
* #ORM\Column(name="participant_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
...
/**
* #var study
*
* #ORM\ManyToOne(targetEntity="Study", inversedBy="participants")
* #ORM\JoinColumn(name="study", referencedColumnName="study_id")
*
*/
private $study;
//setters and getters...
}
First of all, are these annotations correct? (I'm pretty sure I got the whole owning/inverse many-to-one/one-to many relationship straight in my head, but I could be mistaken)
My controller:
Class StudyController extends Controller
{
...
public function addParticipantsAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('MyBundle:Study')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Study id='.$id);
}
$participantArray = $em->getRepository('MyBundle:Participant')->findByStudy($id);
//this is supposed to return a Doctrine ArrayCollection, but for some reason, returns an array
// This needs to be converted to an ArrayCollection
$participants = new ArrayCollection();
foreach ($participantArray as $participant) {
$participants->add($participant);
}
$entity->setParticipants($participants);
$form = $this->createForm(new StudyType(), $entity);
$request = $this->getRequest();
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
}
}
return $this->render('MyBundle:Study:addParticipants.html.twig', array(
'form' => $form->createView(),
'entity' => $entity
));
}
...
}
At this point I have to ask why it is neccessary to explicitly fetch the collection of participants and use it to set the collection on the study entity? Before I added that code, $entity->getParticipants() would return null (even when I know there were several participants with the foreign key set for the study). I have two other tables in a many-to-many relationship where the collections seem to come up automatically just by having the correct annotations in the entity classes. Is this a difference between a many-to-many mapping vs. a many-to-one, or have I messed up the annotation somehow?
I'm not sure if the rest of the code will help, but here's some more:
My study form class:
class StudyType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('studyName', null, array('label'=> 'Study Name:'))
->add('participants', 'collection', array(
'type'=> new ParticipantType(),
'allow_add'=>true,
'by_reference'=>false
));
}
public function getName()
{
return 'study';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'MyBundle\Entity\Study',
);
}
}
My embedded form class:
class ParticipantType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('participantId','text', array('label'=>'Participant ID'))
));
}
public function getName()
{
return 'participant';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'MyBundle\Entity\Participant',
);
}
}
My template:
{% extends 'MyBundle::base.html.twig' %}
{% block body%}
<form action="{{ path('study_addparticipants', { 'id': entity.id }) }}" method="POST" {{ form_enctype(form) }}>
<!-- renders global errors -->
{{ form_errors(form) }}
<h2>Study</h2>
{{ form_label(form.studyName) }}
{{ form_errors(form.studyName) }}
{{ form_widget(form.studyName) }}
<h3>Participants in this study</h3>
<ul class="participants" data-prototype="{{ form_widget(form.participants.get('prototype')) | e }}">
{% for participant in form.participants %}
<li>{{form_row(participant) }}</li>
{% endfor %}
</ul>
{{ form_rest(form) }}
<button type="submit">Save Changes</button>
</form>
{% endblock%}
{% block javascripts %}
{# parent block includes jQuery #}
{{ parent() }}
<script type='text/javascript'>
jQuery(document).ready(function() {
// keep track of how many participant fields have been rendered
var collectionHolder = $('ul.participants');
var $addLink = $('Add new Participant');
var $newLinkLi = $('<li></li>'). append($addLink);
collectionHolder.append($newLinkLi);
$addLink.on('click', function(e) {
e.preventDefault();
addParticipantForm(collectionHolder, $newLinkLi);
});
});
function addParticipantForm(collectionHolder, $newLinkLi) {
// Get the data-prototype we explained earlier
var prototype = collectionHolder.attr('data-prototype');
// Replace '$$name$$' in the prototype's HTML to
// instead be a number based on the current collection's length.
var newForm = prototype.replace(/\$\$name\$\$/g, collectionHolder.children().length);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
}
</script>
{% endblock %}
So, the form displays correctly and when I click the "Add new participant" link, an empty participant form is appended. Changes to the Study and exisitng participant records are saved. There are no errors, but any new participants are not saved.
I have read many similar questions to this one and as far as I know, incorporated everything that should make this work. I've obviously missed something, so would appreciate any suggestions on how to put this right.
Many Thanks.
Thanks to #Luke for the advice. I have solved the problem by looping through the pariticpants collection of my study object and saving each one individually in my controller. The new contoller code:
...
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
foreach($entity->getParticipants() as $participant) {
$em->persist($participant);
}
// flush once to commit all entities
$em->flush();
}
}
...

Resources