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.
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.
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;
}
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();
},
]);
}
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,
]);
}
When 'by_reference' => false
is 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, adding InventaireActif
entities through the TypeActif
form does not persist the changes.
Ensured add
and remove
methods in both entities are synchronized.
Reviewed Doctrine configurations and annotations.
The changes should be saved to the database when InventaireActif
entities are added via the TypeActif
form, without needing to set 'by_reference' => false
.
Without 'by_reference' => false
, the changes are not persisted. No error is displayed, and the logs show nothing out of the ordinary.
If by_reference
is set to true
(or is ommitted), Symfony's form system simply adds the selected entities to the specified property using the methods of the ArrayCollection
class. If by_reference
is set to false
, 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 $lesTypesActifs
property in the InventaireActif
class.
In your form, without by_reference = false
, you're only updating the $lesActifs
property of a TypeActif
entity, which is the inverse side of the relationship, so that's why the changes are not being saved. But if you add by_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 InventaireActif
entity by modifying its $lesTypeActif
property, but if you do not, you could probably just switch the owning and inverse sides of your entities to get rid of by_reference = false
:
class TypeActif {
/**
* @ORM\ManyToMany(targetEntity=InventaireActif::class, inversedBy="lesTypesActifs")
*/
private $lesActifs;
...
}
class InventaireActif {
/**
* @ORM\ManyToMany(targetEntity=TypeActif::class, mappedBy="lesActifs")
*/
private $lesTypesActifs;
...
}