根据Doctrine

时间:2015-09-29 11:11:56

标签: php sql symfony doctrine-orm

我创建了一个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)"

我输入了登录用户的curent和一个消息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();
    }

1 个答案:

答案 0 :(得分:2)

我认为你需要一个CASE .. WHEN构造但是Doctrine在DQL中没有这个构造(参见Grammar)。所以你要么必须使用原始查询,要么就是这些行( it&#39;伪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中的->set()->when()函数(实际上所有其他函数)都不遵循特定的顺序;它们只是向Doctrine查询对象添加属性,当您调用getQuery()时,会进行SQL查询。这意味着以下结构:

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

相当于:

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

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