I have a problem with the cascade persist or merge, I have an application with two entities task and table (task_implements)
When I create a task I choose cultures that are linked:
- If the user chooses 3 cultures => 3 tasks are created
- If the user chooses 3 cultures then 2 vehicles => 3 tasks must be created and each task 2 vehicles bind
TaskController new()
if ( $form->isSubmitted() && $form->isValid() ) {
// Cultures
foreach ( $form->get('cultures')->getData() as $culture) {
// Set correct status
if ( $task->getUser() === null ) {
$task->setStatus( 2 );
} else {
$task->setStatus( 3 );
}
$task->setCulture( $culture );
// Implements
foreach ( $form->get('implements')->getData() as $implement) {
$taskImplement = new TaskImplement();
$taskImplement->setImplement( $implement );
$task->addImplement( $taskImplement );
}
$this->em->merge( $task );
$this->em->flush();
}
$this->addFlash('success', 'Nouvelle tache ajoutée avec succès');
return $this->redirectToRoute('admin_task_index');
}
TaskNewType
->add('implements', EntityType::class, [
'class' => Implement::class,
'query_builder' => function( ImplementRepository $ir ) {
return $ir->createQueryBuilder('i');
},
'choice_label' => function( Implement $implement ) {
return $implement->getName();
},
'mapped' => false,
'required' => false,
'expanded' => true,
'multiple' => true
])
TaskEntity
/**
* @ORMOneToMany(targetEntity=TaskImplement::class, mappedBy="task", orphanRemoval=true, cascade={"persist", "merge"})
*/
private $implements;
public function addImplement(TaskImplement $taskImplement): self
{
if (!$this->implements->contains($taskImplement)) {
$this->implements[] = $taskImplement;
$taskImplement->setTask($this);
}
return $this;
}
Desired behavior
If I take example, if I choose 3 cultures application creates 3 tasks and in every task create 2 implement bind
id / culture_id
#1 10
#2 11
#3 12
in task_implement
id / task_id / implement_id
#1 1 20
#2 1 21
#3 2 20
#4 2 21
#5 3 20
#6 3 21
What I get
id / culture_id
#1 10
#2 11
#3 12
on task_implement
id / task_id / implement_id
#1 | 1 | 20 @
#2 | 1 | 21
#3 | 2 | 20 @
#4 | 2 | 21 @
#5 | 2 | 20 @
#6 | 2 | 21
#7 | 3 | 20 @
#8 | 3 | 21 @
#9 | 3 | 20 @
#10 | 3 | 21 @
#11 | 3 | 20 @
#12 | 3 | 21
I don’t understand why it doesn’t clear between each record in DB and it duplicates if I put 4 cultures I have 4 entries on the last TaskImplement
Thank you for your help
Advertisement
Answer
Apparently, merge
creates a new task entry every time you call it (this probably means, that your $task
doesn’t have an id.
On the first cycle of your outer foreach
you add 2 TaskImplements, which also don’t have ids. Your task now have 2 TaskImplements (which is what you want at this point).
On the second cycle of your outer foreach
you add another 2 TaskImplements – now you have 4 TaskImplements on your task.
Solution options:
- you either need to stop adding TaskImplements after the first outer foreach (which would be weird, but would work) or
- you set your task implements instead of adding them or
- clear the task object of task implements before adding new ones or
- you “clone” the task before adding task implements (it might be sufficient to just create a new task?).
afterthought
You got a very interesting use of merge
. With persist, you should only would have had one Task that contains 6 TaskImplement and only has the last culture set. To be honest, merge without having ids on the task feels like an anti-pattern.
I would assume, that your form itself has data_type Task, which is essentially wrong, since you’re editing/creating multiple tasks. You handle the form as if you have multiple tasks, but you only have one. The root of your problem stems from the wrong semantics …