Reputation: 2917
I have an entity Item that can has a collection of Items (preconditions) with the same class Item. I used this documentation for making a form with the collection
type. I want to make almost the same like in the documentation. But my situation is:
My collecting items are self-referenced to the main item
I want to add already existing items from an existing list, not creating new ones
With prototyping
, i'm adding a new choosed collection item with JS with the same HTML structure like the default repopulation from symfony's form does. But naturally with the replaced choosed relation ID.
Persisting the relations is working, but only with a manual hack, and with missing datas into the $form object.
When i add an relation, with js prototyping
and post the data, a new entity with the correct Item class is added in the ArrayCollection. This new entity returns with the posted ID as key as ArrayCollection item, but an empty entity itself as value. (New one is with key 1 below)
#collection: ArrayCollection {#829 ▼
-elements: array:2 [▼
5 => Item {#834 ▼
#id: 5
-title: "foo"
+preconditions: PersistentCollection {#836 ▶}
}
1 => Item {#890 ▼
#id: null
-title: null
+preconditions: ArrayCollection {#886 ▶}
}
]
}
Problem 1: This results on persisting that an unknown new entity want to be inserted (Because entity with ID null doesn't exists in the DB), and then with invalid null datas. It must not be inserted because its actually already existing.
Problem 2: Before persist i must modify the ArrayCollection property by hand, invalid entities i have to refill. So the entries are valid for relation persisting.
Problem 3: When i modify the ArrayCollection this way, i need this datas to be accessable also in twig. And an update into $form will result an Exception that i can’t modify the $form after it was submitted. Then i saw here the suggestion of an event listener, but even with an event listener i cant’ update the $form because either $form is not prepared or is too late to update.
Is following the right way to perform my goal, or does exist a better variant?
How can the new entities in the ArrayCollection be already be filled correctly after a post?
Controller:
public function newesiAction($id, Request $request)
{
$em = $this->getDoctrine()->getManager();
// For repopulating an existing entity
$item = 0 < $id ? $em->getRepository('AppBundle:Item')->find($id) : new Item();
$form = $this->createForm(new ItemType($this->container), $item);
$form->add('submit', 'submit', array('label' => 'speichern'));
$form->handleRequest($request);
/* After handleRequest, the posted (also new) precondition entities are given. But not as expected. So we update the array collection.
* Not as expected meant: New relation entities becomes new entities with NULL as id. So entities with NULL as id wantet to be INSERTED, what is not our goal.
*/
foreach($item->preconditions as $precondition_entity_id => $precondition_entity) {
$precondition_entity_inner_id = $precondition_entity->getId();
if(null === $precondition_entity_inner_id) {
$item->preconditions[$precondition_entity_id] = $em->getRepository('AppBundle:Item')->find($precondition_entity_id);
}
}
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($item);
$em->flush();
}
return $this->render('AppBundle:Item:edit.html.twig', array(
'form' => $form->createView(),
));
}
ItemType:
<?php
// src/AppBundle/Form/Type/ItemType.php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
class ItemType extends AbstractType
{
public function __construct($container) {
$this->container = $container;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title');
$builder->add('preconditions', 'collection', array(
'type' => new PreconditionType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
// $em = $this->container->get('doctrine')->getManager();
// $form = $event->getForm();
// // Replace the new related entity that have NULL in its ID, with an fully loaded entity
// $entity = $event->getData();
// foreach($entity->preconditions as $precondition_entity_id_posted => $possible_empty_entity) {
// $precondition_entity_id_loaded = $possible_empty_entity->getId();
// if(null === $precondition_entity_id_loaded) {
// $entity->preconditions[$precondition_entity_id_posted] = $em->getRepository('AppBundle:Item')->find($precondition_entity_id_posted);
// }
// }
// $form->get('preconditions')->setData($entity->preconditions);
});
$builder->add('available_items', 'entity', array(
'class' => 'AppBundle\Entity\Item',
'multiple' => true,
'expanded' => false,
'mapped' => false
));
$builder->add('add_item_precondition', 'button', array(
'attr' => array(
'onclick' => "addPreconditionForm('" . $this->getName() . "_available_items')"
)
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Item',
));
}
public function getName()
{
return 'item';
}
}
?>
Precondition type:
<?php
// src/AppBundle/Form/Type/PreconditionType.php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PreconditionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('relation', 'hidden', array(
'mapped' => false,
'required' => false
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Item'
));
}
public function getName()
{
return 'precondition';
}
}
?>
Upvotes: 0
Views: 657
Reputation: 2917
I had the possibility to discuss this with an employee of sensiolabs. Its officially not possible to automate this with the built-in tools, you must loop the collection entries and re-get the incomplete entities.
So the answers to my questions:
Upvotes: 1