Symfony表单CollectionType,通过ID进行索引

3

我正在为 REST API 进行 Symfony 4 表单的嵌入类型工作。这个表单:

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而不是位置来“索引”集合?

3个回答

1
在Symfony的存储库中有一个打开的票据,以添加一个功能到CollectionType来索引自定义字段上的项目:https://github.com/symfony/symfony/issues/7828这将解决您所遇到的问题,但该票据仍然是打开状态并没有向前推进。
但是,在讨论串的下面,人们通过手动更改实体的ArrayCollection来使用ids对其进行索引找到了一种解决方法,如下所示:
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的实体。


谢谢,这非常有趣。下次我需要类似的东西时,我会尝试一下! - gmazzotti
正是我所需要的,谢谢!我与GUID结合使用。 - Wesley Abbenhuis

0

0

很遗憾,这实际上是不可能的。接受Symfony集合是基于索引的事实,并处理它。您可以使用可能的解决方案之一继续前进。

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

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

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

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

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


哦,好的!谢谢你的回答。我的问题不仅限于删除,还包括对具有可排序顺序等属性的项目进行更新,删除只是一个例子。你能指点我一些“可能的解决方案”吗?我已经谷歌了几天,但我找到的都是基础级别的教程,完全忽略了这个问题。 - gmazzotti
嗯,我不认为我能为你提供最有效的解决方案,因为我根本不知道你的API有什么要求,你打算如何使用它以及用于什么目的。这取决于你自己。只需记住,Symfony将根据索引比较通过表单提交的每个新集合与存储在数据库中的集合。如果某个集合元素缺失,它将从数据库中删除它;如果某个元素看起来不同,它将更新数据库中的元素,甚至会重写id。在发送请求的另一端进行所有必要的数据准备工作。 - Majesty
好的,谢谢。在我看来,Symfony表单并不是我可以在我的情况下使用的工具(或任何可写API场景),因为我不能指望用户强制执行这些奇怪的约束。 - gmazzotti
如果您认为表单集合不适合您,那么您可能应该考虑为添加、更新和删除“OffertaImmagine”使用单独的端点,这将增加API的一些复杂性,因为现在您有一个“Offerta”的端点和三个“OffertaImmagine”的端点,但是这将更容易理解如何处理此特定实体。 - Majesty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接