Symfony表单post - 添加一个新的自引用ArrayCollection项返回空实体

时间:2015-10-23 09:18:43

标签: php forms symfony collections

情况

我有一个实体,它可以包含具有相同类的项(前提条件)的集合。我使用this文档制作了collection类型的表单。我想在文档中做几乎相同的事情。但我的情况是:

  • 我的收集项目会自行引用主要项目

  • 我想从现有列表中添加现有项目,而不是创建新项目

使用prototyping,我正在添加一个新的选择集合项,其中JS具有相同的HTML结构,就像symfony表单中的默认重新填充一样。但自然会被替换的选择关系ID。

坚持关系是有效的,但只有手动破解,并且 $ form 对象中缺少数据。

问题

当我使用js prototyping添加关系并发布数据时,会在ArrayCollection中添加具有正确 Item 类的新实体。这个新实体返回的是已发布的ID作为ArrayCollection项的键,但是一个空实体本身作为值。 (新的一个是下面的密钥1)

#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 ▶}
      }
    ]
  }
  • 问题1:这导致持久存在一个未知的新实体想要插入(因为DB中不存在ID为null的实体),然后是无效的空数据。它不能插入,因为它实际上已经存在。

  • 问题2:在持久化之前我必须手动修改ArrayCollection属性,我必须重新填充无效实体。因此条目对于持久关系有效。

  • 问题3:当我以这种方式修改ArrayCollection时,我需要这个数据也可以在twig中访问。而对 $ form 的更新将导致异常,即我无法在提交后修改 $ form 。然后我看到了here一个事件监听器的建议,但即使有一个事件监听器我也不能'更新 $ form 因为 $ form 没有准备好或者为时已晚,无法更新。

问题

  • 是按照正确的方式来实现我的目标,还是确实存在更好的变体?

  • 如何在帖子后正确填充ArrayCollection中的新实体?

代码

控制器:

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';
    }
}
?>

前提条件类型:

<?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';
    }
}
?>

1 个答案:

答案 0 :(得分:1)

我有可能与sensiolabs的员工讨论这个问题。它正式无法使用内置工具自动执行此操作,您必须循环收集条目并重新获取不完整的实体。

所以我的问题的答案:

  • 是的,这是获取遗失实体的正确方法
  • 不,它不可能自动获得