Cláusulas WHERE complejas que utilizan PHP Doctrine ORM
Pregunta
Estoy usando PHP Doctrine ORM para construir mis consultas. Sin embargo, parece que no puedo entender cómo escribir la siguiente cláusula WHERE usando DQL (Doctrine Query Language):
WHERE name='ABC' AND (category1 = 'X' OR category2 = 'X' OR category3 = 'X')
AND price > 10
¿Cómo puedo especificar dónde van los paréntesis?
Lo que tengo actualmente en mi código PHP es esto:
->where('name = ?', 'ABC')
->andWhere('category1 = ?', 'X')
->orWhere('category2 = ?', 'X')
->orWhere('category3 = ?', 'X')
->andWhere('price > ?', 10)
Pero esto produce algo así como
WHERE name='ABC' AND category1 = 'X' OR category2 = 'X' OR category3 = 'X'
AND price > 10
que, debido al orden de las operaciones, no devuelve los resultados previstos.
Además, ¿hay alguna diferencia entre " donde " ;, " yWhere " ;, y " addWhere " métodos?
ACTUALIZACIÓN Ok, parece que no puedes hacer consultas complejas usando DQL, así que he estado tratando de escribir el SQL manualmente y usar el método andWhere () para agregarlo. Sin embargo, estoy usando WHERE..IN y Doctrine parece estar eliminando mis paréntesis adjuntos:
$q->andWhere("(category1 IN $subcategory_in_clause
OR category2 IN $subcategory_in_clause
OR category3 IN $subcategory_in_clause)");
Solución
Desde mi experiencia, cada función compleja where
se agrupa entre paréntesis (estoy usando Doctrine 1.2.1).
$q->where('name = ?', 'ABC')
->andWhere('category1 = ? OR category2 = ? OR category3 = ?', array('X', 'X', 'X'))
->andWhere('price < ?', 10)
produce el siguiente SQL:
WHERE name = 'ABC'
AND (category1 = 'X' OR category2 = 'X' OR category3 = 'X')
AND price < 10
Otros consejos
La forma correcta de hacerlo se puede encontrar en doctrina 2 - consultas condicionales del generador de consultas ... Si las declaraciones? como lo señaló @Jekis. Aquí se explica cómo usar el generador de expresiones para resolver esto, como en el ejemplo de @ 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);
Como parece que no puedes hacer consultas complejas usando DQL, escribí el siguiente SQL para pasar al método andWhere ():
$q->andWhere("(category1 IN $subcategory_in_clause
OR category2 IN $subcategory_in_clause
OR category3 IN $subcategory_in_clause) AND TRUE");
Tenga en cuenta el " AND TRUE " ;, un truco para que el analizador no ignore los paréntesis externos.
yDónde se puede resumir como: Puede usar con seguridad yWhere en lugar de where . (presenta una sobrecarga muy pequeña, que se indica a continuación en el segundo elemento de la lista). La implementación de andWhere is: (Doctrine 1.2.3) que se puede leer como,
Condición (es) añadida (s) anteriormente (s) conscientes de la declaración WHERE
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);
}
En cuanto a la diferencia entre where, andwhere y addwhere, no creo que haya una diferencia significativa desde la última vez que leí la fuente. Sin embargo, le animo a leer la fuente de Doctrina. Es realmente simple y ayuda a llenar los agujeros en la documentación (hay muchos). En cuanto a las declaraciones complejas de where, me lo he preguntado yo mismo, pero aún no lo he necesitado.
En mi experiencia, a veces he visto una diferencia entre:
$q->andWhere("(category1 IN $subcategory_in_clause
OR category2 IN $subcategory_in_clause
OR category3 IN $subcategory_in_clause)");
y
$q->andWhere("(category1 IN $subcategory_in_clause OR category2 IN $subcategory_in_clause OR category3 IN $subcategory_in_clause)");
La primera declaración está escrita en 3 líneas, la segunda, en una sola. ¡No lo creía pero HAY UNA DIFERENCIA!
$q->andWhere("category1 IN ( $subcategory_in_clause )
OR category2 IN ( $subcategory_in_clause )
OR category3 IN ( $subcategory_in_clause )");
¿sería tan amable de probar esta variante, no estoy seguro si funciona, pero vale la pena intentarlo?