fabpico
fabpico

Reputation: 2917

Symfony form post - add a new self-referenced ArrayCollection item returns empty entity

Situation

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:

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.

Problems

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 ▶}
      }
    ]
  }

Questions

Code

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

Answers (1)

fabpico
fabpico

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:

  • Yes, it is the correct way go get the missing entities
  • No, its not possible to get those automatically

Upvotes: 1

Related Questions