Inicio > Bases de datos, Doctrine > Un vistazo al QueryBuilder de Doctrine 2

Un vistazo al QueryBuilder de Doctrine 2

Acabado el capítulo sobre DQL pasamos a ver el capítulo que trata el QueryBuilder, un objeto que proporciona una API fluida para construir consultas DQL por pasos. Para quién no sepáis qué es una API fluida podéis consultar la Wikipedia

De la misma forma que instanciamos un objeto Query podemos instanciar un objeto QueryBuilder:

<?php
$qb = $em->createQueryBuilder();

Podemos inspeccionar que tipo de objeto es el QueryBuilder:

<?php
echo $qb->getType(); // imprime: 0

Hay tres tipos:

  • QueryBuilder::SELECT, retorna el valor 0
  • QueryBuilder::DELETE, retorna el valor 1
  • QueryBuilder::UPDATE, retorna el valor 2

También podemos obtener el EntityManager asociado, el string DQL definido en el QueryBuilder, y el objeto Query con el DQL procesado:

<?php
$em = $qb->getEntityManager();
 
$dql = $qb->getDql();
 
$q = $qb->getQuery();

Internamente, QueryBuilder trabaja con una cache DQL para incrementar el rendimiento. Cualquier cambio que afecte al DQL generado modifica el estado del QueryBuilder a STATE_DIRTY. Hay dos estados:

  • QueryBuilder::STATE_CLEAN, que significa que no han habido cambios
  • QueryBuilder::STATE_DIRTY, que significa que la consulta DQL debe ser procesada

El método add de QueryBuilder es el responsable de construir pieza a pieza la consulta DQL. Toma tres parámetros:

  1. $dqlPartName: El nombre de la parte a la que hace referencia $dqlPart. Puede ser select, from, where, groupBy, having, orderBy.
  2. $dqlPart: El valor que acompaña a $dqlPartName. Puede ser un string o una instancia de Doctrine\ORM\Query\Expr\*
  3. $append: Flag opcional (por defecto FALSE) que establece si $dqlPart debe sobreescribir el valor si ya se había aplicado antes.
<?php
$qb->add('select', 'u')
   ->add('from', 'User u')
   ->add('where', 'u.id = ?1')
   ->add('orderBy', 'u.name ASC');

Este QueryBuilder crea la consulta DQL siguiente:

"SELECT u FROM User u WHERE u.id = ?1 ORDER BY u.name ASC"

Doctrine 2 nos permite establecer parámetros en las consultas para ayudarnos a construir consultas dinámicas:

<?php
 
$qb->add('select', 'u')
   ->add('from', 'User u')
   ->add('where', 'u.id = ?1')
   ->add('orderBy', 'u.name ASC');
   ->setParameter(1, 100); // Sustituye ?1 por 100
 
$qb->add('select', 'u')
   ->add('from', 'User u')
   ->add('where', 'u.id = :identifier')
   ->add('orderBy', 'u.name ASC');
   ->setParameter('identifier', 100); //Sustituye :identifier por 100

Si tenemos que establecer más de un parámetro podemos utilizar setParameters() en lugar de setParameter():

<?php 
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));

Para limitar un conjunto de resultados podemos utilizar los métodos setFirstResult() y setMaxResults():

<?php
$offset = (int)$_GET['offset'];
$limit = (int)$_GET['limit'];
 
$qb->add('select', 'u')
   ->add('from', 'User u')
   ->add('orderBy', 'u.name ASC')
   ->setFirstResult( $offset )
   ->setMaxResults( $limit );

El QueryBuilder solamente es un objeto que nos construye la consulta DQL, por lo que si queremos ejecutar la consulta primero tenemos que obtenerla con el método getQuery:

<?php
$query = $qb->getQuery();
 
// Establecer opciones adicionales
$query->setQueryHint('foo', 'bar');
$query->useResultCache('my_cache_id');
 
// Ejecutar la consulta
$result = $query->getResult();
$single = $query->getSingleResult();
$array = $query->getArrayResult();
$scalar = $query->getScalarResult();
$singleScalar = $query->getSingleScalarResult();

Si invocamos el método add con un string, internamente se evalúa a una expresión. Si no queremos utilizar los atajos podemos crear las expresiones adecuadas para cada opción:

<?php
$qb->add('select', new Expr\Select(array('u')))
   ->add('from', new Expr\From('User', 'u'))
   ->add('where', new Expr\Comparison('u.id', '=', '?1'))
   ->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));

Desde luego, esta manera es bastante engorrosa de crear consultas DQL, por lo que la clase Expr provee de un conjunto de métodos que ayudan a crear este tipo de expresiones:

$qb->add('select', $qb->expr()->select('u'))
   ->add('from', $qb->expr()->from('User', 'u'))
   ->add('where', $qb->expr()->orx(
       $qb->expr()->eq('u.id', '?1'),
       $qb->expr()->like('u.nickname', '?2')
   ))
   ->add('orderBy', $qb->expr()->orderBy('u.surname', 'ASC'));

NOTA del traductor: No creo que utilizar esta forma de construir consultas a través de expresiones de este tipo aumente mucho el rendimiento de la aplicación, debido a que si utilizamos una cache para cachear las consultas SQL procesadas se procesarán muy pocas veces. Por mi parte, prefiero legibilidad antes que escirbir tal cantidad de código para escribir una simple consulta DQL. Supongo que habrá veces que no quede más remedio que utilizar ciertas expresiones pero si puedo evitar esta forma de crear DQLs mucho mejor.

Os adjunto los métodos con los que cuenta la clase Expr para crear expresiones. No voy a comentar tal cantidad de métodos, dado que la mayoría son autodescriptivos.

<?php
class Expr
{
    /** Base objects **/
 
    // Example usage - $qb->expr()->select('u')
    public function select($select = null); // Returns Expr\Select instance
 
    // Example - $qb->expr()->from('User', 'u')
    public function from($from, $alias); // Returns Expr\From instance
 
    // Example - $qb->expr()->leftJoin('u.Phonenumbers', 'p', Expr\Join::ON, 'p.user_id = u.id AND p.country_code = 55');
    // Example - $qb->expr()->leftJoin('u.Phonenumbers', 'p', 'ON', $qb->expr()->andx($qb->expr()->eq('p.user_id', 'u.id'), $qb->expr()->eq('p.country_code', '55'));
    public function leftJoin($join, $alias, $conditionType = null, $condition = null); // Returns Expr\Join instance
 
    // Example - $qb->expr()->innerJoin('u.Group', 'g', Expr\Join::WITH, 'g.manager_level = 100');
    // Example - $qb->expr()->innerJoin('u.Group', 'g', 'WITH', $qb->expr()->eq('g.manager_level', '100'));
    public function innerJoin($join, $alias, $conditionType = null, $condition = null); // Returns Expr\Join instance
 
    // Example - $qb->expr()->orderBy('u.surname', 'ASC')->add('u.firstname', 'ASC')->...
    public function orderBy($sort = null, $order = null); // Returns Expr\OrderBy instance
 
    // Example - $qb->expr()->groupBy()->add('u.id')->...
    public function groupBy($groupBy = null); // Returns Expr\GroupBy instance
 
 
    /** Conditional objects **/
 
    // Example - $qb->expr()->andx($cond1 [, $condN])->add(...)->...
    public function andx($x = null); // Returns Expr\Andx instance
 
    // Example - $qb->expr()->orx($cond1 [, $condN])->add(...)->...
    public function orx($x = null); // Returns Expr\Orx instance
 
 
    /** Comparison objects **/
 
    // Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1
    public function eq($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->neq('u.id', '?1') => u.id <> ?1
    public function neq($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->lt('u.id', '?1') => u.id < ?1
    public function lt($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->lte('u.id', '?1') => u.id <= ?1
    public function lte($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->gt('u.id', '?1') => u.id > ?1
    public function gt($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1
    public function gte($x, $y); // Returns Expr\Comparison instance
 
 
    /** Arithmetic objects **/
 
    // Example - $qb->expr()->prod('u.id', '2') => u.id * 2
    public function prod($x, $y); // Returns Expr\Math instance
 
    // Example - $qb->expr()->diff('u.id', '2') => u.id - 2
    public function diff($x, $y); // Returns Expr\Math instance
 
    // Example - $qb->expr()->sum('u.id', '2') => u.id + 2
    public function sum($x, $y); // Returns Expr\Math instance
 
    // Example - $qb->expr()->quot('u.id', '2') => u.id / 2
    public function quot($x, $y); // Returns Expr\Math instance
 
 
    /** Pseudo-function objects **/
 
    // Example - $qb->expr()->exists($qb2->getDql())
    public function exists($subquery); // Returns Expr\Func instance
 
    // Example - $qb->expr()->all($qb2->getDql())
    public function all($subquery); // Returns Expr\Func instance
 
    // Example - $qb->expr()->some($qb2->getDql())
    public function some($subquery); // Returns Expr\Func instance
 
    // Example - $qb->expr()->any($qb2->getDql())
    public function any($subquery); // Returns Expr\Func instance
 
    // Example - $qb->expr()->not($qb->expr()->eq('u.id', '?1'))
    public function not($restriction); // Returns Expr\Func instance
 
    // Example - $qb->expr()->in('u.id', array(1, 2, 3))
    // Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception.
    // Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above)
    public function in($x, $y); // Returns Expr\Func instance
 
    // Example - $qb->expr()->notIn('u.id', '2')
    public function notIn($x, $y); // Returns Expr\Func instance
 
    // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))
    public function like($x, $y); // Returns Expr\Comparison instance
 
    // Example - $qb->expr()->between('u.id', '1', '10')
    public function between($val, $x, $y); // Returns Expr\Func
 
 
    /** Function objects **/
 
    // Example - $qb->expr()->trim('u.firstname')
    public function trim($x); // Returns Expr\Func
 
    // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat(' ', 'u.lastname'))
    public function concat($x, $y); // Returns Expr\Func
 
    // Example - $qb->expr()->substr('u.firstname', 0, 1)
    public function substr($x, $from, $len); // Returns Expr\Func
 
    // Example - $qb->expr()->lower('u.firstname')
    public function lower($x); // Returns Expr\Func
 
    // Example - $qb->expr()->upper('u.firstname')
    public function upper($x); // Returns Expr\Func
 
    // Example - $qb->expr()->length('u.firstname')
    public function length($x); // Returns Expr\Func
 
    // Example - $qb->expr()->avg('u.age')
    public function avg($x); // Returns Expr\Func
 
    // Example - $qb->expr()->max('u.age')
    public function max($x); // Returns Expr\Func
 
    // Example - $qb->expr()->min('u.age')
    public function min($x); // Returns Expr\Func
 
    // Example - $qb->expr()->abs('u.currentBalance')
    public function abs($x); // Returns Expr\Func
 
    // Example - $qb->expr()->sqrt('u.currentBalance')
    public function sqrt($x); // Returns Expr\Func
 
    // Example - $qb->expr()->count('u.firstname')
    public function count($x); // Returns Expr\Func
 
    // Example - $qb->expr()->countDistinct('u.surname')
    public function countDistinct($x); // Returns Expr\Func
}

Como hemos visto, la forma anterior es una forma un poco engorrosa de crear consultas DQL. Para simplificar más las cosas la clase QueryBuilder provee de un conjunto de métodos de ayuda que son atajos a los vistos anteriormente para hacer la vida del programador mucho más fácil y llevadera.

<?php
$qb->select('u')
   ->from('User', 'u')
   ->where('u.id = ?1')
   ->orderBy('u.name ASC');

Os adjunto la lista de métodos disponibles:

<?php
class QueryBuilder
{
    // Example - $qb->select('u')
    // Example - $qb->select(array('u', 'p'))
    // Example - $qb->select($qb->expr()->select('u', 'p'))
    public function select($select = null);
 
    // Example - $qb->delete('User', 'u')
    public function delete($delete = null, $alias = null);
 
    // Example - $qb->update('Group', 'g')
    public function update($update = null, $alias = null);
 
    // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold'))
    // Example - $qb->set('u.numChilds', 'u.numChilds + ?1')
    // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1'))
    public function set($key, $value);
 
    // Example - $qb->from('Phonenumber', 'p')
    public function from($from, $alias = null);
 
    // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::ON, $qb->expr()->and($qb->expr()->eq('u.group_id', 'g.id'), 'g.name = ?1'))
    // Example - $qb->innerJoin('u.Group', 'g', 'ON', 'u.group_id = g.id AND g.name = ?1')
    public function innerJoin($join, $alias = null, $conditionType = null, $condition = null);
 
    // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55))
    // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')
    public function leftJoin($join, $alias = null, $conditionType = null, $condition = null);
 
    // NOTE: ->where() overrides all previously set conditions
    //
    // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2'))
    // Example - $qb->where($qb->expr()->andx($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))
    // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')
    public function where($where);
 
    // Example - $qb->andWhere($qb->expr()->orx($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
    public function andWhere($where);
 
    // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10));
    public function orWhere($where);
 
    // NOTE: -> groupBy() overrides all previously set grouping conditions
    //
    // Example - $qb->groupBy('u.id')
    public function groupBy($groupBy);
 
    // Example - $qb->addGroupBy('g.name')
    public function addGroupBy($groupBy);
 
    // NOTE: -> having() overrides all previously set having conditions
    //
    // Example - $qb->having('u.salary >= ?1')
    // Example - $qb->having($qb->expr()->gte('u.salary', '?1'))
    public function having($having);
 
    // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0))
    public function andHaving($having);
 
    // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100'))
    public function orHaving($having);
 
    // NOTE: -> orderBy() overrides all previously set ordering conditions
    //
    // Example - $qb->orderBy('u.surname', 'DESC')
    public function orderBy($sort, $order = null);
 
    // Example - $qb->addOrderBy('u.firstName')
    public function addOrderBy($sort, $order = null); // Default $order = 'ASC'
}

Como podéis ver tenemos métodos de ayuda para realizar todo tipos de consultas en DQL.

Categorías: Bases de datos, Doctrine
  1. No hay comentarios aún.
  1. No trackbacks yet.

Deja un comentario