My Setup is a Symfony 3.4 App with the typical 'ManyToMany
'-Relation with additional fields, something like this:
Entity Article
Entity Specialty
Entity ArticleSpecialtyRelation
In a Form
for an Article
i wanted it to look like as if it were a ManyToMany
-Relation rendered as an EntityType
with multiple=true
and expanded=true
, so all entries of Specialty
are rendered as checkboxes.
To achieve that i created a non orm-mapped property specialties
that is an ArrayCollection, gets initialized in the Constructor and has a Getter, Adder and Remover.
/**
*
* @var ArrayCollection;
*
*/
protected $specialties;
public function __construct()
{
$this->specialties = new ArrayCollection();
}
/**
* @return Collection|Specialty[]
*/
public function getSpecialties()
{
return $this->specialties;
}
/**
* @param Specialty $specialties
*/
public function addSpecialties(Specialty $specialties)
{
$this->specialties->add($specialties);
}
/**
* @param Specialty $specialties
*/
public function removeSpecialties(Specialty $specialties)
{
$this->specialties->removeElement($specialties);
}
This property is used to render the Specialty
Entity as checkboxes:
add('specialties', EntityType::class,array(
'class' => Specialty::class,
'expanded'=>true,
'multiple'=>true,
'label'=>'Specialties',
'required' => false,
'mapped'=>true,
));
To populate it with the data from SpecialtyRelation
i added a PreSetData Formevent
:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$article = $event->getData();
if ($article instanceof Article) {
$form->get('specialties')->setData($article->getUsedSpecialties());
}
});
The used Getter of $artikel
just iterates over $article->getArtikelSpecialties
and returns a collection of Specialty
.
It all works until the submit. Because the formfield is mapped=true
, somewhere in handleRequest($form)
where the entity is hydrated with the form data, it explodes when the Adder for $specialty
is called:
Call to a member function add() on null
Because as i just learned, the Constructor
is never called by Doctrine and obviously initializes all ORM-ArrayCollections
but not the ArrayCollection
for the non-mapped property specialties
-
Of course I can check if the ArrayCollection
is initialized in the Adder and Remover and initialize it there if it is null, but that just feels a bit hacky in a already at least hacky-felt setup and i am wondering if my setup is completely stupid, especially since i didn't find anybody trying to do that (or getting problems with that) on here or elsewhere.
Is there a better solution to this or should i just check the ArrayCollection
in Adder and Remover and live happily ever after?
Also, just curious, is there any other way to initialize the ArrayCollection
?
P.S. If there are typos in the names it's because i translated the names into english.
Partial Stacktrace
Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function add() on null
at src/Test/Bundle/TestBundle/Entity/Article.php:312 at Test\Bundle\TestBundle\Entity\Article->addSpecialties(object(Specialty)) (vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:674) at Symfony\Component\PropertyAccess\PropertyAccessor->writeCollection(array(object(Article), object(Article)), 'specialties', object(ArrayCollection), 'addSpecialties', 'removeSpecialties') (vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:622) at Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(array(object(Article), object(Article)), 'specialties', object(ArrayCollection)) (vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:216) at Symfony\Component\PropertyAccess\PropertyAccessor->setValue(object(Article), object(PropertyPath), object(ArrayCollection)) (vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php:86) at Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper->mapFormsToData(object(RecursiveIteratorIterator), object(Article)) (vendor/symfony/symfony/src/Symfony/Component/Form/Form.php:636) at Symfony\Component\Form\Form->submit(array(), true) (vendor/symfony/symfony/src/Symfony/Component/Form/Form.php:580)