【问题标题】:Symfony Forms CollectionType, index by IDSymfony Forms CollectionType,按 ID 索引
【发布时间】:2019-01-21 14:40:15
【问题描述】:

我正在开发一个带有 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 只看到 1 个对象,因此删除第二个并更新第一个“url”字段。

我怎样才能通过对象的 ID 而不是它们的位置来“索引”集合?

【问题讨论】:

    标签: symfony symfony-forms


    【解决方案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 的实体。

    【讨论】:

    • 谢谢,这很有趣。下次我需要类似的东西时,我会试试!
    • 正是我想要的,谢谢!我将它与 GUID 结合使用
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 哦,好的!谢谢您的回答。我的问题不仅限于删除,还包括使用可排序订单等更新项目,删除只是一个示例。你能为我指出一些“可能的解决方案”的正确方向吗?我已经用谷歌搜索了好几天,但我发现的只是基本级别的教程,完全跳过了这个问题。
    • 好吧,我认为我无法为您推荐最有效的解决方案,因为我根本不知道您的 API 有什么要求,您将如何使用它以及用于什么目的。它是由你决定。请记住,Symfony 会将通过表单提交的每个新集合与存储在数据库中的集合进行比较,具体取决于索引。如果集合的某些元素丢失 - 它将从 DB 中删除,如果某些元素看起来不同 - 它会更新 DB 中的元素,甚至 id 也会被重写。在发送请求的另一端进行所有必需的数据准备
    • 好的,谢谢。正如我所看到的,symfony 表单不是我可以在我的场景(或任何支持写入的 API 场景)中使用的工具,因为我不能指望用户强制执行这些奇怪的约束。
    • 如果您认为表单集合不适合您,那么您可能应该考虑使用单独的端点来添加、更新和删除OffertaImmagine,这会给您的 API 增加一些复杂性,因为现在你有一个Offerta 端点和三个OffertaImmagine 端点,但更容易理解如何处理这个特定实体
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 2018-11-30
    • 2018-05-29
    • 1970-01-01
    相关资源
    最近更新 更多