Context:
I'm working on a Symfony 5 application with entities InventaireActif and TypeActif having a ManyToMany relationship. An issue arises when managing this relationship through a Symfony form.
Problem:
In my form for TypeActif, when I add InventaireActif entities, they are not persisted to the database unless I set the 'by_reference' => false option in the form builder. I want to achieve the correct behavior without relying on this option.
Entity Configuration:
InventaireActif.php:
/**
* @ORM\ManyToMany(targetEntity=TypeActif::class, inversedBy="lesActifs")
*/
private $lesTypesActifs;
public function __construct()
{
$this->lesTypesActifs = new ArrayCollection();
// ... other initializations ...
}
/**
* @return Collection<int, TypeActif>
*/
public function getLesTypesActifs(): Collection
{
return $this->lesTypesActifs;
}
public function addLesTypesActif(TypeActif $lesTypesActif): self
{
if (!$this->lesTypesActifs->contains($lesTypesActif)) {
$this->lesTypesActifs[] = $lesTypesActif;
}
return $this;
}
public function removeLesTypesActif(TypeActif $lesTypesActif): self
{
$this->lesTypesActifs->removeElement($lesTypesActif);
return $this;
}
TypeActif.php:
/**
* @ORM\ManyToMany(targetEntity=InventaireActif::class, mappedBy="lesTypesActifs")
*/
private $lesActifs;
public function __construct()
{
$this->lesActifs = new ArrayCollection();
// ... other initializations ...
}
public function getLesActifs(): Collection
{
return $this->lesActifs;
}
public function addLesActif(InventaireActif $lesActif): self
{
if (!$this->lesActifs->contains($lesActif)) {
$this->lesActifs[] = $lesActif;
$lesActif->addLesTypesActif($this);
}
return $this;
}
public function removeLesActif(InventaireActif $lesActif): self
{
if ($this->lesActifs->removeElement($lesActif)) {
$lesActif->removeLesTypesActif($this);
}
return $this;
}
Form Configuration in TypeActifType.php:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
// ... other fields ...
->add('lesActifs', EntityType::class, [
'required' => false,
'class' => InventaireActif::class,
'choice_label' => 'libelle',
// 'by_reference' => false,
'multiple' => true,
'query_builder' => function (InventaireActifRepository $repo) {
return $repo->lesActifsReformesSortedASC();
},
]);
}
edit route in TypeActifController.php:
public function new(Request $request, TypeActifRepository $typeActifRepository, EntityManagerInterface $entityManager): Response
{
$typeActif = new TypeActif();
$form = $this->createForm(TypeActifType::class, $typeActif);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$typeActifRepository->add($typeActif, true);
return $this->redirectToRoute('app_type_actif_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('type_actif/new.html.twig', [
'type_actif' => $typeActif,
'form' => $form,
]);
}
Issue with 'by_reference':
When
'by_reference' => falseis uncommented, the form works perfectly, and the ManyToMany relationship is updated as expected.However, I want to avoid using
'by_reference' => false. Without this option, addingInventaireActifentities through theTypeActifform does not persist the changes.
Attempts to Resolve:
Ensured
addandremovemethods in both entities are synchronized.Reviewed Doctrine configurations and annotations.
Expected Behavior:
The changes should be saved to the database when InventaireActif entities are added via the TypeActif form, without needing to set 'by_reference' => false.
Current Behavior:
Without 'by_reference' => false, the changes are not persisted. No error is displayed, and the logs show nothing out of the ordinary.
If
by_referenceis set totrue(or is ommitted), Symfony's form system simply adds the selected entities to the specified property using the methods of theArrayCollectionclass. Ifby_referenceis set tofalse, it uses the custom add/remove methods you specified instead.And when saving the entity to the database, only changes made to the owning side of the ManyToMany relationship are saved. The owning side in your code is the
$lesTypesActifsproperty in theInventaireActifclass.In your form, without
by_reference = false, you're only updating the$lesActifsproperty of aTypeActifentity, which is the inverse side of the relationship, so that's why the changes are not being saved. But if you addby_reference = false, the custom add/remove methods make sure the owning side is also updated, so then the changes are saved.I'm not sure if you also have a form that updates the
InventaireActifentity by modifying its$lesTypeActifproperty, but if you do not, you could probably just switch the owning and inverse sides of your entities to get rid ofby_reference = false: