Symfony Forms CollectionType,按ID索引

时间:2019-01-21 14:40:16

标签: symfony symfony-forms

我正在使用Symfony 4表单,该表单具有用于REST API的嵌入式类型。形式:

class OffertaType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('pulsanti_social_attivi', CheckboxType::class, [ ])
            ->add('immagini', CollectionType::class, [
                'entry_type' => OffertaImmagineType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'prototype' => 'immagini'
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class'         => Offerta::class,
                'allow_extra_fields' => true,
                'csrf_protection'    => false,
            ]
        );
    }
}

该集合包含2个对象

[
    {
        "id": 1,
        "url": "https://via.placeholder.com/150?text=1"
    },
    {
        "id": 2,
        "url": "https://via.placeholder.com/150?text=2"
    }
]

所有“有效”,如果我发布一些数据,则创建一个新对象,并且如果我更新一个对象,则数据库也将更新。奇怪的是,行似乎是根据集合中的对象位置而不是其ID进行更新/删除。如果我删除第一个对象,结果应该是

[
    {
        "id": 2,
        "url": "https://via.placeholder.com/150?text=2"
    }
]

但是它

[
    {
        "id": 1,
        "url": "https://via.placeholder.com/150?text=2"
    }
]

Symfony仅看到一个对象,因此删除第二个对象并更新第一个“ url”字段。

如何通过对象的ID而不是它们的位置来“索引”集合?

2 个答案:

答案 0 :(得分:1)

Symfony的存储库中有一个已打开的票证,用于向CollectionType添加一个功能以对自定义字段中的项目建立索引:https://github.com/symfony/symfony/issues/7828可以解决您遇到的问题,但该票证仍处于打开状态,并且还没有前进。

但是,在线程的更深处,人们找到了一种变通方法,方法是手动更改实体的ArrayCollection以使用id对其进行索引,如下所示:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $entity = $builder->getData();

    // Reindex collection using id
    $indexedCollection = new ArrayCollection();
    foreach ($entity->getCollectionField() as $collectionItem) {
        $indexedCollection->set($collectionItem->getId(), $collectionItem);
    }

    $builder
        ->add('collectionField', CollectionType::class, [
            'data' => $indexedCollection,
        ])
    ;
}

(从Seb33300的答案https://github.com/symfony/symfony/issues/7828#issuecomment-579608260复制)。

然后您可以发送回此JSON:

{
    "immagini": {
        "2": {"url": "http://new-url.com"}
    }
}

它将被映射到集合中ID为2的实体。

答案 1 :(得分:0)

不幸的是,这实际上是不可能的。只需接受Symfony Collections基于索引并加以处理即可。您可以选择一种可能的解决方案。

例如,您可以为收集项目创建单独的删除按钮,然后设置

'delete_empty' => false,
'allow_delete' => false,

仅将表单用于添加新条目。

或者您可以将delete_emptyallow_delete设置为true,但是在这种情况下,应该为要实际删除的集合位置设置空元素。

另一种选择是根据您的特定要求为您的表单创建一个唯一的处理程序,但是由于我目前不知道您需要什么,因此我无法为您提供特定的示例。