PHP Doctrine ORM的复杂WHERE子句

44

我正在使用PHP Doctrine ORM构建我的查询。然而,似乎我无法想出如何使用DQL(Doctrine Query Language)编写以下WHERE子句:

WHERE name='ABC' AND (category1 = 'X' OR category2 = 'X' OR category3 = 'X') 
AND price > 10

我该如何指定括号的位置?

我当前在我的PHP代码中有这样一段:

->where('name = ?', 'ABC')
->andWhere('category1 = ?', 'X')
->orWhere('category2 = ?', 'X')
->orWhere('category3 = ?', 'X')
->andWhere('price > ?', 10)

但这会产生类似于以下内容

WHERE name='ABC' AND category1 = 'X' OR category2 = 'X' OR category3 = 'X' 
AND price > 10
由于运算顺序的原因,它没有返回预期结果。另外,“where”、“andWhere”和“addWhere”方法之间有区别吗?更新:好的,看起来你不能使用DQL进行复杂查询,所以我一直在尝试手动编写SQL并使用andWhere()方法添加它。但是,我正在使用WHERE..IN语句,Doctrine似乎正在剥离我的括号。
$q->andWhere("(category1 IN $subcategory_in_clause
            OR category2 IN $subcategory_in_clause 
            OR category3 IN $subcategory_in_clause)");

另一个解决方案可以在这里找到 https://dev59.com/pFzUa4cB1Zd3GeqPzha5#7720723 - Jekis
7个回答

74

根据我的经验,在Doctrine 1.2.1中,每个复杂的where函数都要用括号分组。

$q->where('name = ?', 'ABC')
  ->andWhere('category1 = ? OR category2 = ? OR category3 = ?', array('X', 'X', 'X'))
  ->andWhere('price < ?', 10)

生成以下SQL:

WHERE name = 'ABC' 
  AND (category1 = 'X' OR category2 = 'X' OR category3 = 'X')
  AND price < 10

正是我所需要的! - Rico Humme

28

可以在doctrine 2 - query builder条件查询...If语句?中找到正确的做法,正如@Jekis所指出的那样。以下是如何使用表达式构建器解决这个问题,就像@anushr的示例一样。

$qb->where($qb->expr()->eq('name', ':name'))
  ->andWhere(
    $qb->expr()->orX(
      $qb->expr()->eq('category1', ':category1'),
      $qb->expr()->eq('category2', ':category2'),
      $qb->expr()->eq('category3', ':category3')
  )
  ->andWhere($qb->expr()->lt('price', ':price')
  ->setParameter('name', 'ABC')
  ->setParameter('category1', 'X')
  ->setParameter('category2', 'X')
  ->setParameter('category3', 'X')
  ->setParameter('price', 10);

8
这正是我为什么讨厌Doctrine的原因。这篇文章难以阅读和理解。 - Nicolas
1
你也可以这样做:$ors = $qb->expr()->orX(); $ors->add($qb->expr()->like('firstName', $qb->expr()->literal("%john%"))); $ors->add($qb->expr()->like('lastName', $qb->expr()->literal("%john%"))); $qb->andWhere($ors); - Nuryagdy Mustapayev
1
或者你可以跳过所有这些噪音,将一个DQL字符串连接在一起... 只要确保自己不会被SQL注入攻击。 - Jacob Thomason

1

andWhere 可以概括为:
先前添加的条件感知 WHERE 语句

您可以安全地使用 andWhere 替换 where。(它引入了非常小的开销,如下面的第二个列表项所述。)

andWhere 的实现方式是:(Doctrine 1.2.3)

public function andWhere($where, $params = array())
{
    if (is_array($params)) {
        $this->_params['where'] = array_merge($this->_params['where'], $params);
    } else {
        $this->_params['where'][] = $params;
    }

    if ($this->_hasDqlQueryPart('where')) {
        $this->_addDqlQueryPart('where', 'AND', true);
    }

    return $this->_addDqlQueryPart('where', $where, true);
}

可以理解为:

  1. 处理参数
  2. 如果之前添加了另一个where语句,则在查询的where部分附加AND语句
  3. 附加条件

1

看起来使用DQL无法执行复杂查询,因此我编写了以下SQL语句并传递给andWhere()方法:

$q->andWhere("(category1 IN $subcategory_in_clause
OR category2 IN $subcategory_in_clause 
OR category3 IN $subcategory_in_clause) AND TRUE");

请注意“AND TRUE”,这是一个技巧,使解析器不会忽略外层括号。

我认为anushr的解决方案更好。没有黑客攻击和使用预处理语句。 - Petr Peller

0
在我的经验中,有时我会看到以下方面的差异:
$q->andWhere("(category1 IN $subcategory_in_clause
            OR category2 IN $subcategory_in_clause 
            OR category3 IN $subcategory_in_clause)");

并且

$q->andWhere("(category1 IN $subcategory_in_clause OR category2 IN $subcategory_in_clause OR category3 IN $subcategory_in_clause)");

第一条语句写在3行上,第二条只写在一行上。我不敢相信,但确实有区别!


0
关于where、andwhere和addwhere之间的区别,我认为与我上次阅读源代码时没有显著差异。但我鼓励你去阅读Doctrine源代码。它非常简单,有助于填补文档中的空白(有很多)。至于复杂的where语句,我自己也曾经想过,但还没有需要用到它们。

0
$q->andWhere("category1 IN ( $subcategory_in_clause )
              OR category2 IN ( $subcategory_in_clause )
              OR category3 IN ( $subcategory_in_clause )");

你能否试一下这个变体呢?不确定是否有效,但值得一试。


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