【问题标题】:Update certain fields based on condition in Doctrine根据 Doctrine 中的条件更新某些字段
【发布时间】:2015-12-26 20:11:59
【问题描述】:

我创建了一个支持用户之间私人消息的 Symfony 2 Bundle。我让他们能够从inboxsent 文件夹向trash 文件夹发送消息。邮件将通过isRTrashisSTrash 字段标记为垃圾邮件,分别由收件人和发件人标记。那是因为,作为我的数据库中的同一条消息,如果我在这里有一个字段,一个用户将其标记为垃圾,也会将其标记为另一个字段。

现在,我想让他们也可以从垃圾文件夹中delete 他们。邮件不会被删除,但与垃圾邮件类似​​,只是它们永远从标准用户视图中消失。我无法像这样标记它们,因为我必须标记用户发送和接收的两个消息。

我在实体的存储库中进行了以下查询:

    public function delete($user, $msg)
    {
        $qb = $this->createQueryBuilder('a')
            ->update('PrivateMessageBundle:Message', 'a')
            ->where('a IN(:msg)')
            ->andwhere('a.receiver = :user AND a.isRTrash IS NOT null AND a.isRDeleted = false')->set('a.isRDeleted', true)
            ->orWhere('a.sender = :user AND a.isSTrash IS NOT null AND a.isSDeleted = false')->set('a.isSDeleted', true)
            ->setParameters(
                array('user' => $user, 'msg' => $msg)
            );
echo '<pre>';
\Doctrine\Common\Util\Debug::dump($qb->getQuery()->getSQL()); exit;
echo '</pre>';
        return $qb->getQuery();
    }

输出查询是string(196) "UPDATE message SET isRDeleted = 1, isSDeleted = 1 WHERE (id IN (?) AND (receiver_id = ? AND isRTrash IS NOT NULL AND isRDeleted = 0)) OR (sender_id = ? AND isSTrash IS NOT NULL AND isSDeleted = 0)"

我将当前登录用户和一组消息 ID 作为输入。然后,我检查垃圾箱中的邮件,未标记为已删除,当前用户为收件人或发件人,并希望将其标记为已删除。

问题是两个条件都满足了,并且SET 都被调用了,将消息的isRDeletedisSDeleted 标记为真,无论如何。

我非常接近,但不知道如何使这些字段单独标记,只有满足它们的条件。

同时,我正在使用 foreach 循环,但我认为使用查询可以更快地完成

   $em = $this->getDoctrine()->getManager();
    foreach ($msgs as $msgid) {
        $msg = $messages->findOneBy(array('id' => $msgid));


        if ($msg->getSender() == $this->getUser() && $msg->getIsSTrash() && $msg->getIsSDeleted() == false) {
            $msg->setIsSDeleted(true);
            $changedno++;
        } else if ($msg->getReceiver() == $this->getUser() && $msg->getIsRTrash() && $msg->getIsRDeleted() == false) {
            $msg->setIsRDeleted(true);
            $changedno++;
        }

        $em->flush();
    }

【问题讨论】:

    标签: php sql symfony doctrine-orm


    【解决方案1】:

    我认为您需要 CASE .. WHEN 构造,但 Doctrine 在 DQL 中没有这种构造(请参阅 Grammar)。因此,您要么必须使用原始查询,类似以下内容(它是伪 MySQL):

    UPDATE PrivateMessageBundle:Message a
    SET a.isRDeleted = CASE
        WHEN a.receiver = :user AND a.isRTrash IS NOT null THEN TRUE
        ELSE a.isRDeleted = FALSE
    END, 
    SET a.isSSDeleted = CASE
        WHEN a.receiver = :user AND a.isRTrash IS NOT null THEN TRUE
        ELSE a.isSDeleted = FALSE
    END
    

    ...或者使用两个标准查询,一个用于isRDeleted,一个用于isSDeleted,就像您已经做过的那样。老实说,我认为在您的情况下这是一个非常简单的解决方案,如果您需要在六个月内再次阅读您的代码,它看起来更维护友好

    注意:附带说明,Doctrine 中的 -&gt;set()-&gt;when() 函数(以及所有其他函数,实际上)不遵循特定顺序;他们只是向 Doctrine 查询对象添加属性,当您调用 getQuery() 时,会进行 SQL 查询。这意味着以下构造:

     ->when()->set()
     ->orwhen()->set()
    

    相当于:

     ->set()->set()
     ->when()->orWhen() 
    

    这就是您的解决方案无法工作的原因。在调用set() 之前没有条件要满足(如果我不清楚,请告诉我

    【讨论】:

    • 是的,我发现查询中的顺序无关紧要。我还查找了 CASE..WHEN 语句,但找不到任何适用于 UPDATE 的有效解决方案,仅适用于 INSERT。
    • 如果不满足条件,您不应该保持 isR/SDeleted 值不变吗?似乎您可以错误地恢复消息;)。不明白为什么需要检查值是否为假,因为无论如何您都会将它们设置为真,对性能有什么影响?
    • @GeorgeIrimiciuc 你只需要在 Doctrine 中使用原生 SQL 查询:return $this-&gt;getEntityManager()-&gt;getConnection()-&gt;executeQuery($sql, $params);$sql 你的查询和 $params 参数。
    • @tchap 关于使用两个标准查询,这意味着我必须检查相同的消息数组两次。我一直在寻找替代方案。同时,我正在对数组进行 foreach 并检查每条消息,但是单个查询会比使用 ORM 的 foreach 快得多。
    • 不,我的意思是在您的 delete() 函数中复制您的更新查询:一个带有 -&gt;where('a.receiver = :user AND a.isRTrash IS NOT null AND a.isRDeleted = false')-&gt;set('a.isRDeleted', true) 和一个带有 -&gt;where('a.sender = :user AND a.isSTrash IS NOT null AND a.isSDeleted = false')-&gt;set('a.isSDeleted', true) 。这是两个UPDATE 而不是一个,我认为这没什么大不了的,就你而言。
    猜你喜欢
    • 1970-01-01
    • 2011-11-29
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 2020-07-30
    相关资源
    最近更新 更多