【问题标题】:doctrine/symfony 4 - avoid duplicate entry when persisting child entities教义/symfony 4 - 持久化子实体时避免重复输入
【发布时间】:2019-12-08 01:17:11
【问题描述】:

我有以下设置:我解析了一个结构与此类似的 api 响应(我有点过于简单化了):

{
    orders: [
        {
            // some data
            customers: [
                {
                    // some data
                    addresses: [
                        {
                            id: 436456445
                            // some data
                        },
                        // Other addresses
                     ]
                }
            ]                
        },
        {
            customers: [
                {
                    addresses: [
                        {
                            id: 436456445
                            // some data
                        }
                    ]
                }
            ]                
        }
    ]
}

请注意,特定地址在响应中出现了两次(相同的 id)。

我深入旅行(希望是正确的翻译)结构并制作实体。一切顺利,直到插入时的保存部分抛出重复键异常。

一个明显的解决方案是规范化结构,使地址在订单之前并重新映射周围的所有内容,但正如我所说的,我过于简单化了,实际情况有数百个结构动态映射到实体中,并有许多特殊情况人工处理。这使得“寻找”逻辑映射变得不可能。捕获异常(如此处所建议的 insert ignore on duplicate entries in Doctrine2/Symfony2)会失去我的实体经理,这意味着从头开始处理相同的数据,出现相同的问题(对此不是 100% 确定......我在这里遗漏了什么吗?)。

我需要做的是使用 mysql 在重复键上插入 [做某事] 或插入忽略(我更喜欢在重复键上插入,因为插入忽略忽略了太多东西)。知道该怎么做吗?

重要提示:我正在持久化并刷新“订单”,其余实体正在通过级联进行持久化。

【问题讨论】:

    标签: php symfony doctrine-orm doctrine symfony4


    【解决方案1】:

    我想我用下面的代码解决了我的问题(在我写这个答案时它没有优化,但意图很清楚):

    protected function removeDuplicates($insertions) {
        foreach ($insertions as $key => $insertion) {
            $shortClassName = (new ReflectionClass($insertion))->getShortName();
            // TODO: The search can be heavily optimized
            foreach ($insertions as $possibleDupeKey => $possibleDupeInsertion) {
                $shortDupeClassName = (new ReflectionClass($insertion))->getShortName();
                // TODO: Treat case when unique is on a field not called 'id'
                if ($shortClassName === $shortDupeClassName && $insertion->getId() === $possibleDupeInsertion->getId() && $key !== $possibleDupeKey) {
                    $this->em->remove($possibleDupeInsertion);
                }
            }
        }
    }
    
    protected function saveStuff($order) {
        $this->em->persist($order);
        $this->removeDuplicates($this->em->getUnitOfWork()->getScheduledEntityInsertions());
        $this->em->flush();
    }
    

    注意:我确信在 90% 的情况下会有更好的方法(但我认为在这种特殊情况下我属于 10%,所以...为 10% 分享:P)。

    【讨论】:

    • 通常,如果您要发布数据,则不会有 ID。如果您 PUT、UPDATE、DELETE 和 GET,将会有一个 ID。因此,如果您发布并遇到一个 ID,它涉及一个现有对象并且只是一个新关联。如果以这种方式处理,则不需要删除重复项。可以像if ($request->getMethod('POST') && ! is_null($request->getData()->get('id')) { $user->linkAddress($request->getData()); } else { $this->userService->getUser($user)->setAddress(new Address($request->getData())); } 一样简单地处理它(某种伪代码;-))
    • 我在 API 客户端工作,而不是 API 服务器。我解析一个响应。 id在中间层的某个地方,它可能在之前的db中,也可能是双插入。差别很大。
    猜你喜欢
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    • 2016-04-13
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多