vendor/doctrine/lib/Doctrine/ORM/QueryBuilder.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 /*
       
     3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
     4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
     5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
     6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
     7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
     8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
     9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    14  *
       
    15  * This software consists of voluntary contributions made by many individuals
       
    16  * and is licensed under the LGPL. For more information, see
       
    17  * <http://www.doctrine-project.org>.
       
    18  */
       
    19 
       
    20 namespace Doctrine\ORM;
       
    21 
       
    22 use Doctrine\ORM\Query\Expr;
       
    23 
       
    24 /**
       
    25  * This class is responsible for building DQL query strings via an object oriented
       
    26  * PHP interface.
       
    27  *
       
    28  * @since 2.0
       
    29  * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
       
    30  * @author Jonathan Wage <jonwage@gmail.com>
       
    31  * @author Roman Borschel <roman@code-factory.org>
       
    32  */
       
    33 class QueryBuilder
       
    34 {
       
    35     /* The query types. */
       
    36     const SELECT = 0;
       
    37     const DELETE = 1;
       
    38     const UPDATE = 2;
       
    39 
       
    40     /** The builder states. */
       
    41     const STATE_DIRTY = 0;
       
    42     const STATE_CLEAN = 1;
       
    43 
       
    44     /**
       
    45      * @var EntityManager The EntityManager used by this QueryBuilder.
       
    46      */
       
    47     private $_em;
       
    48 
       
    49     /**
       
    50      * @var array The array of DQL parts collected.
       
    51      */
       
    52     private $_dqlParts = array(
       
    53         'select'  => array(),
       
    54         'from'    => array(),
       
    55         'join'    => array(),
       
    56         'set'     => array(),
       
    57         'where'   => null,
       
    58         'groupBy' => array(),
       
    59         'having'  => null,
       
    60         'orderBy' => array()
       
    61     );
       
    62 
       
    63     /**
       
    64      * @var integer The type of query this is. Can be select, update or delete.
       
    65      */
       
    66     private $_type = self::SELECT;
       
    67 
       
    68     /**
       
    69      * @var integer The state of the query object. Can be dirty or clean.
       
    70      */
       
    71     private $_state = self::STATE_CLEAN;
       
    72 
       
    73     /**
       
    74      * @var string The complete DQL string for this query.
       
    75      */
       
    76     private $_dql;
       
    77     
       
    78     /**
       
    79      * @var array The query parameters.
       
    80      */
       
    81     private $_params = array();
       
    82 
       
    83     /**
       
    84      * @var array The parameter type map of this query.
       
    85      */
       
    86     private $_paramTypes = array();
       
    87     
       
    88     /**
       
    89      * @var integer The index of the first result to retrieve.
       
    90      */
       
    91     private $_firstResult = null;
       
    92     
       
    93     /**
       
    94      * @var integer The maximum number of results to retrieve.
       
    95      */
       
    96     private $_maxResults = null;
       
    97 
       
    98     /**
       
    99      * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
       
   100      * 
       
   101      * @param EntityManager $em The EntityManager to use.
       
   102      */
       
   103     public function __construct(EntityManager $em)
       
   104     {
       
   105         $this->_em = $em;
       
   106     }
       
   107 
       
   108     /**
       
   109      * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
       
   110      * This producer method is intended for convenient inline usage. Example:
       
   111      *
       
   112      * <code>
       
   113      *     $qb = $em->createQueryBuilder()
       
   114      *         ->select('u')
       
   115      *         ->from('User', 'u')
       
   116      *         ->where($qb->expr()->eq('u.id', 1));
       
   117      * </code>
       
   118      *
       
   119      * For more complex expression construction, consider storing the expression
       
   120      * builder object in a local variable.
       
   121      *
       
   122      * @return Expr
       
   123      */
       
   124     public function expr()
       
   125     {
       
   126         return $this->_em->getExpressionBuilder();
       
   127     }
       
   128 
       
   129     /**
       
   130      * Get the type of the currently built query.
       
   131      *
       
   132      * @return integer
       
   133      */
       
   134     public function getType()
       
   135     {
       
   136         return $this->_type;
       
   137     }
       
   138 
       
   139     /**
       
   140      * Get the associated EntityManager for this query builder.
       
   141      *
       
   142      * @return EntityManager
       
   143      */
       
   144     public function getEntityManager()
       
   145     {
       
   146         return $this->_em;
       
   147     }
       
   148 
       
   149     /**
       
   150      * Get the state of this query builder instance.
       
   151      *
       
   152      * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
       
   153      */
       
   154     public function getState()
       
   155     {
       
   156         return $this->_state;
       
   157     }
       
   158 
       
   159     /**
       
   160      * Get the complete DQL string formed by the current specifications of this QueryBuilder.
       
   161      *
       
   162      * <code>
       
   163      *     $qb = $em->createQueryBuilder()
       
   164      *         ->select('u')
       
   165      *         ->from('User', 'u')
       
   166      *     echo $qb->getDql(); // SELECT u FROM User u
       
   167      * </code>
       
   168      *
       
   169      * @return string The DQL query string.
       
   170      */
       
   171     public function getDQL()
       
   172     {
       
   173         if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) {
       
   174             return $this->_dql;
       
   175         }
       
   176 
       
   177         $dql = '';
       
   178 
       
   179         switch ($this->_type) {
       
   180             case self::DELETE:
       
   181                 $dql = $this->_getDQLForDelete();
       
   182                 break;
       
   183 
       
   184             case self::UPDATE:
       
   185                 $dql = $this->_getDQLForUpdate();
       
   186                 break;
       
   187 
       
   188             case self::SELECT:
       
   189             default:
       
   190                 $dql = $this->_getDQLForSelect();
       
   191                 break;
       
   192         }
       
   193 
       
   194         $this->_state = self::STATE_CLEAN;
       
   195         $this->_dql = $dql;
       
   196 
       
   197         return $dql;
       
   198     }
       
   199 
       
   200     /**
       
   201      * Constructs a Query instance from the current specifications of the builder.
       
   202      *
       
   203      * <code>
       
   204      *     $qb = $em->createQueryBuilder()
       
   205      *         ->select('u')
       
   206      *         ->from('User', 'u');
       
   207      *     $q = $qb->getQuery();
       
   208      *     $results = $q->execute();
       
   209      * </code>
       
   210      *
       
   211      * @return Query
       
   212      */
       
   213     public function getQuery()
       
   214     {
       
   215         return $this->_em->createQuery($this->getDQL())
       
   216                 ->setParameters($this->_params, $this->_paramTypes)
       
   217                 ->setFirstResult($this->_firstResult)
       
   218                 ->setMaxResults($this->_maxResults);
       
   219     }
       
   220     
       
   221     /**
       
   222      * Gets the FIRST root alias of the query. This is the first entity alias involved
       
   223      * in the construction of the query.
       
   224      *
       
   225      * <code>
       
   226      * $qb = $em->createQueryBuilder()
       
   227      * ->select('u')
       
   228      * ->from('User', 'u');
       
   229      *
       
   230      * echo $qb->getRootAlias(); // u
       
   231      * </code>
       
   232      *
       
   233      * @deprecated Please use $qb->getRootAliases() instead.
       
   234      * @return string $rootAlias
       
   235      */
       
   236     public function getRootAlias()
       
   237     {
       
   238         $aliases = $this->getRootAliases();
       
   239         return $aliases[0];
       
   240     }
       
   241 
       
   242     /**
       
   243      * Gets the root aliases of the query. This is the entity aliases involved
       
   244      * in the construction of the query.
       
   245      *
       
   246      * <code>
       
   247      *     $qb = $em->createQueryBuilder()
       
   248      *         ->select('u')
       
   249      *         ->from('User', 'u');
       
   250      *
       
   251      *     $qb->getRootAliases(); // array('u')
       
   252      * </code>
       
   253      *
       
   254      * @return array $rootAliases
       
   255      */
       
   256     public function getRootAliases()
       
   257     {
       
   258         $aliases = array();
       
   259         
       
   260         foreach ($this->_dqlParts['from'] as &$fromClause) {
       
   261             if (is_string($fromClause)) {
       
   262                 $spacePos = strrpos($fromClause, ' ');
       
   263                 $from     = substr($fromClause, 0, $spacePos);
       
   264                 $alias    = substr($fromClause, $spacePos + 1);
       
   265 
       
   266                 $fromClause = new Query\Expr\From($from, $alias);
       
   267             }
       
   268             
       
   269             $aliases[] = $fromClause->getAlias();
       
   270         }
       
   271         
       
   272         return $aliases;
       
   273     }
       
   274 
       
   275     /**
       
   276      * Gets the root entities of the query. This is the entity aliases involved
       
   277      * in the construction of the query.
       
   278      *
       
   279      * <code>
       
   280      *     $qb = $em->createQueryBuilder()
       
   281      *         ->select('u')
       
   282      *         ->from('User', 'u');
       
   283      *
       
   284      *     $qb->getRootEntities(); // array('User')
       
   285      * </code>
       
   286      *
       
   287      * @return array $rootEntities
       
   288      */
       
   289     public function getRootEntities()
       
   290     {
       
   291         $entities = array();
       
   292         
       
   293         foreach ($this->_dqlParts['from'] as &$fromClause) {
       
   294             if (is_string($fromClause)) {
       
   295                 $spacePos = strrpos($fromClause, ' ');
       
   296                 $from     = substr($fromClause, 0, $spacePos);
       
   297                 $alias    = substr($fromClause, $spacePos + 1);
       
   298 
       
   299                 $fromClause = new Query\Expr\From($from, $alias);
       
   300             }
       
   301             
       
   302             $entities[] = $fromClause->getFrom();
       
   303         }
       
   304         
       
   305         return $entities;
       
   306     }
       
   307 
       
   308     /**
       
   309      * Sets a query parameter for the query being constructed.
       
   310      *
       
   311      * <code>
       
   312      *     $qb = $em->createQueryBuilder()
       
   313      *         ->select('u')
       
   314      *         ->from('User', 'u')
       
   315      *         ->where('u.id = :user_id')
       
   316      *         ->setParameter(':user_id', 1);
       
   317      * </code>
       
   318      *
       
   319      * @param string|integer $key The parameter position or name.
       
   320      * @param mixed $value The parameter value.
       
   321      * @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
       
   322      * @return QueryBuilder This QueryBuilder instance.
       
   323      */
       
   324     public function setParameter($key, $value, $type = null)
       
   325     {
       
   326         if ($type === null) {
       
   327             $type = Query\ParameterTypeInferer::inferType($value);
       
   328         }
       
   329         
       
   330         $this->_paramTypes[$key] = $type;
       
   331         $this->_params[$key] = $value;
       
   332         
       
   333         return $this;
       
   334     }
       
   335     
       
   336     /**
       
   337      * Sets a collection of query parameters for the query being constructed.
       
   338      *
       
   339      * <code>
       
   340      *     $qb = $em->createQueryBuilder()
       
   341      *         ->select('u')
       
   342      *         ->from('User', 'u')
       
   343      *         ->where('u.id = :user_id1 OR u.id = :user_id2')
       
   344      *         ->setParameters(array(
       
   345      *             ':user_id1' => 1,
       
   346      *             ':user_id2' => 2
       
   347      *         ));
       
   348      * </code>
       
   349      *
       
   350      * @param array $params The query parameters to set.
       
   351      * @return QueryBuilder This QueryBuilder instance.
       
   352      */
       
   353     public function setParameters(array $params, array $types = array())
       
   354     {
       
   355         foreach ($params as $key => $value) {
       
   356             if (isset($types[$key])) {
       
   357                 $this->setParameter($key, $value, $types[$key]);
       
   358             } else {
       
   359                 $this->setParameter($key, $value);
       
   360             }
       
   361         }
       
   362         return $this;
       
   363     }
       
   364 
       
   365     /**
       
   366      * Gets all defined query parameters for the query being constructed.
       
   367      *
       
   368      * @return array The currently defined query parameters.
       
   369      */
       
   370     public function getParameters()
       
   371     {
       
   372         return $this->_params;
       
   373     }
       
   374 
       
   375     /**
       
   376      * Gets a (previously set) query parameter of the query being constructed.
       
   377      * 
       
   378      * @param mixed $key The key (index or name) of the bound parameter.
       
   379      * @return mixed The value of the bound parameter.
       
   380      */
       
   381     public function getParameter($key)
       
   382     {
       
   383         return isset($this->_params[$key]) ? $this->_params[$key] : null;
       
   384     }
       
   385 
       
   386     /**
       
   387      * Sets the position of the first result to retrieve (the "offset").
       
   388      *
       
   389      * @param integer $firstResult The first result to return.
       
   390      * @return QueryBuilder This QueryBuilder instance.
       
   391      */
       
   392     public function setFirstResult($firstResult)
       
   393     {
       
   394         $this->_firstResult = $firstResult;
       
   395         return $this;
       
   396     }
       
   397 
       
   398     /**
       
   399      * Gets the position of the first result the query object was set to retrieve (the "offset").
       
   400      * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
       
   401      * 
       
   402      * @return integer The position of the first result.
       
   403      */
       
   404     public function getFirstResult()
       
   405     {
       
   406         return $this->_firstResult;
       
   407     }
       
   408     
       
   409     /**
       
   410      * Sets the maximum number of results to retrieve (the "limit").
       
   411      * 
       
   412      * @param integer $maxResults The maximum number of results to retrieve.
       
   413      * @return QueryBuilder This QueryBuilder instance.
       
   414      */
       
   415     public function setMaxResults($maxResults)
       
   416     {
       
   417         $this->_maxResults = $maxResults;
       
   418         return $this;
       
   419     }
       
   420     
       
   421     /**
       
   422      * Gets the maximum number of results the query object was set to retrieve (the "limit").
       
   423      * Returns NULL if {@link setMaxResults} was not applied to this query builder.
       
   424      * 
       
   425      * @return integer Maximum number of results.
       
   426      */
       
   427     public function getMaxResults()
       
   428     {
       
   429         return $this->_maxResults;
       
   430     }
       
   431 
       
   432     /**
       
   433      * Either appends to or replaces a single, generic query part.
       
   434      *
       
   435      * The available parts are: 'select', 'from', 'join', 'set', 'where',
       
   436      * 'groupBy', 'having' and 'orderBy'.
       
   437      *
       
   438      * @param string $dqlPartName 
       
   439      * @param string $dqlPart 
       
   440      * @param string $append 
       
   441      * @return QueryBuilder This QueryBuilder instance.
       
   442      */
       
   443     public function add($dqlPartName, $dqlPart, $append = false)
       
   444     {
       
   445         $isMultiple = is_array($this->_dqlParts[$dqlPartName]);
       
   446         
       
   447         // This is introduced for backwards compatibility reasons.
       
   448         // TODO: Remove for 3.0
       
   449         if ($dqlPartName == 'join') {
       
   450             $newDqlPart = array();
       
   451             foreach ($dqlPart AS $k => $v) {
       
   452                 if (is_numeric($k)) {
       
   453                     $newDqlPart[$this->getRootAlias()] = $v;
       
   454                 } else {
       
   455                     $newDqlPart[$k] = $v;
       
   456                 }
       
   457             }
       
   458             $dqlPart = $newDqlPart;
       
   459         }
       
   460     
       
   461         if ($append && $isMultiple) {
       
   462             if (is_array($dqlPart)) {
       
   463                 $key = key($dqlPart);
       
   464                 
       
   465                 $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
       
   466             } else {
       
   467                 $this->_dqlParts[$dqlPartName][] = $dqlPart;
       
   468             }
       
   469         } else {
       
   470             $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
       
   471         }
       
   472 
       
   473         $this->_state = self::STATE_DIRTY;
       
   474 
       
   475         return $this;
       
   476     }
       
   477 
       
   478     /**
       
   479      * Specifies an item that is to be returned in the query result.
       
   480      * Replaces any previously specified selections, if any.
       
   481      *
       
   482      * <code>
       
   483      *     $qb = $em->createQueryBuilder()
       
   484      *         ->select('u', 'p')
       
   485      *         ->from('User', 'u')
       
   486      *         ->leftJoin('u.Phonenumbers', 'p');
       
   487      * </code>
       
   488      *
       
   489      * @param mixed $select The selection expressions.
       
   490      * @return QueryBuilder This QueryBuilder instance.
       
   491      */
       
   492     public function select($select = null)
       
   493     {
       
   494         $this->_type = self::SELECT;
       
   495         
       
   496         if (empty($select)) {
       
   497             return $this;
       
   498         }
       
   499         
       
   500         $selects = is_array($select) ? $select : func_get_args();
       
   501 
       
   502         return $this->add('select', new Expr\Select($selects), false);
       
   503     }
       
   504 
       
   505     /**
       
   506      * Adds an item that is to be returned in the query result.
       
   507      *
       
   508      * <code>
       
   509      *     $qb = $em->createQueryBuilder()
       
   510      *         ->select('u')
       
   511      *         ->addSelect('p')
       
   512      *         ->from('User', 'u')
       
   513      *         ->leftJoin('u.Phonenumbers', 'p');
       
   514      * </code>
       
   515      *
       
   516      * @param mixed $select The selection expression.
       
   517      * @return QueryBuilder This QueryBuilder instance.
       
   518      */
       
   519     public function addSelect($select = null)
       
   520     {
       
   521         $this->_type = self::SELECT;
       
   522         
       
   523         if (empty($select)) {
       
   524             return $this;
       
   525         }
       
   526         
       
   527         $selects = is_array($select) ? $select : func_get_args();
       
   528 
       
   529         return $this->add('select', new Expr\Select($selects), true);
       
   530     }
       
   531 
       
   532     /**
       
   533      * Turns the query being built into a bulk delete query that ranges over
       
   534      * a certain entity type.
       
   535      *
       
   536      * <code>
       
   537      *     $qb = $em->createQueryBuilder()
       
   538      *         ->delete('User', 'u')
       
   539      *         ->where('u.id = :user_id');
       
   540      *         ->setParameter(':user_id', 1);
       
   541      * </code>
       
   542      *
       
   543      * @param string $delete The class/type whose instances are subject to the deletion.
       
   544      * @param string $alias The class/type alias used in the constructed query.
       
   545      * @return QueryBuilder This QueryBuilder instance.
       
   546      */
       
   547     public function delete($delete = null, $alias = null)
       
   548     {
       
   549         $this->_type = self::DELETE;
       
   550 
       
   551         if ( ! $delete) {
       
   552             return $this;
       
   553         }
       
   554 
       
   555         return $this->add('from', new Expr\From($delete, $alias));
       
   556     }
       
   557 
       
   558     /**
       
   559      * Turns the query being built into a bulk update query that ranges over
       
   560      * a certain entity type.
       
   561      *
       
   562      * <code>
       
   563      *     $qb = $em->createQueryBuilder()
       
   564      *         ->update('User', 'u')
       
   565      *         ->set('u.password', md5('password'))
       
   566      *         ->where('u.id = ?');
       
   567      * </code>
       
   568      *
       
   569      * @param string $update The class/type whose instances are subject to the update.
       
   570      * @param string $alias The class/type alias used in the constructed query.
       
   571      * @return QueryBuilder This QueryBuilder instance.
       
   572      */
       
   573     public function update($update = null, $alias = null)
       
   574     {
       
   575         $this->_type = self::UPDATE;
       
   576 
       
   577         if ( ! $update) {
       
   578             return $this;
       
   579         }
       
   580 
       
   581         return $this->add('from', new Expr\From($update, $alias));
       
   582     }
       
   583 
       
   584     /**
       
   585      * Create and add a query root corresponding to the entity identified by the given alias,
       
   586      * forming a cartesian product with any existing query roots.
       
   587      *
       
   588      * <code>
       
   589      *     $qb = $em->createQueryBuilder()
       
   590      *         ->select('u')
       
   591      *         ->from('User', 'u')
       
   592      * </code>
       
   593      *
       
   594      * @param string $from   The class name.
       
   595      * @param string $alias  The alias of the class.
       
   596      * @return QueryBuilder This QueryBuilder instance.
       
   597      */
       
   598     public function from($from, $alias)
       
   599     {
       
   600         return $this->add('from', new Expr\From($from, $alias), true);
       
   601     }
       
   602 
       
   603     /**
       
   604      * Creates and adds a join over an entity association to the query.
       
   605      *
       
   606      * The entities in the joined association will be fetched as part of the query
       
   607      * result if the alias used for the joined association is placed in the select
       
   608      * expressions.
       
   609      *
       
   610      * <code>
       
   611      *     $qb = $em->createQueryBuilder()
       
   612      *         ->select('u')
       
   613      *         ->from('User', 'u')
       
   614      *         ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
       
   615      * </code>
       
   616      *
       
   617      * @param string $join The relationship to join
       
   618      * @param string $alias The alias of the join
       
   619      * @param string $conditionType The condition type constant. Either ON or WITH.
       
   620      * @param string $condition The condition for the join
       
   621      * @param string $indexBy The index for the join
       
   622      * @return QueryBuilder This QueryBuilder instance.
       
   623      */
       
   624     public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
       
   625     {
       
   626         return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
       
   627     }
       
   628 
       
   629     /**
       
   630      * Creates and adds a join over an entity association to the query.
       
   631      * 
       
   632      * The entities in the joined association will be fetched as part of the query
       
   633      * result if the alias used for the joined association is placed in the select
       
   634      * expressions.
       
   635      *
       
   636      *     [php]
       
   637      *     $qb = $em->createQueryBuilder()
       
   638      *         ->select('u')
       
   639      *         ->from('User', 'u')
       
   640      *         ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
       
   641      *
       
   642      * @param string $join The relationship to join
       
   643      * @param string $alias The alias of the join
       
   644      * @param string $conditionType The condition type constant. Either ON or WITH.
       
   645      * @param string $condition The condition for the join
       
   646      * @param string $indexBy The index for the join
       
   647      * @return QueryBuilder This QueryBuilder instance.
       
   648      */
       
   649     public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
       
   650     {
       
   651         $rootAlias = substr($join, 0, strpos($join, '.'));
       
   652         if (!in_array($rootAlias, $this->getRootAliases())) {
       
   653             $rootAlias = $this->getRootAlias();
       
   654         }
       
   655         
       
   656         return $this->add('join', array(
       
   657             $rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
       
   658         ), true);
       
   659     }
       
   660 
       
   661     /**
       
   662      * Creates and adds a left join over an entity association to the query.
       
   663      *
       
   664      * The entities in the joined association will be fetched as part of the query
       
   665      * result if the alias used for the joined association is placed in the select
       
   666      * expressions.
       
   667      *
       
   668      * <code>
       
   669      *     $qb = $em->createQueryBuilder()
       
   670      *         ->select('u')
       
   671      *         ->from('User', 'u')
       
   672      *         ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
       
   673      * </code>
       
   674      *
       
   675      * @param string $join The relationship to join
       
   676      * @param string $alias The alias of the join
       
   677      * @param string $conditionType The condition type constant. Either ON or WITH.
       
   678      * @param string $condition The condition for the join
       
   679      * @param string $indexBy The index for the join
       
   680      * @return QueryBuilder This QueryBuilder instance.
       
   681      */
       
   682     public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
       
   683     {
       
   684         $rootAlias = substr($join, 0, strpos($join, '.'));
       
   685         if (!in_array($rootAlias, $this->getRootAliases())) {
       
   686             $rootAlias = $this->getRootAlias();
       
   687         }
       
   688         
       
   689         return $this->add('join', array(
       
   690             $rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
       
   691         ), true);
       
   692     }
       
   693 
       
   694     /**
       
   695      * Sets a new value for a field in a bulk update query.
       
   696      *
       
   697      * <code>
       
   698      *     $qb = $em->createQueryBuilder()
       
   699      *         ->update('User', 'u')
       
   700      *         ->set('u.password', md5('password'))
       
   701      *         ->where('u.id = ?');
       
   702      * </code>
       
   703      *
       
   704      * @param string $key The key/field to set.
       
   705      * @param string $value The value, expression, placeholder, etc.
       
   706      * @return QueryBuilder This QueryBuilder instance.
       
   707      */
       
   708     public function set($key, $value)
       
   709     {
       
   710         return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true);
       
   711     }
       
   712 
       
   713     /**
       
   714      * Specifies one or more restrictions to the query result.
       
   715      * Replaces any previously specified restrictions, if any.
       
   716      *
       
   717      * <code>
       
   718      *     $qb = $em->createQueryBuilder()
       
   719      *         ->select('u')
       
   720      *         ->from('User', 'u')
       
   721      *         ->where('u.id = ?');
       
   722      *
       
   723      *     // You can optionally programatically build and/or expressions
       
   724      *     $qb = $em->createQueryBuilder();
       
   725      *
       
   726      *     $or = $qb->expr()->orx();
       
   727      *     $or->add($qb->expr()->eq('u.id', 1));
       
   728      *     $or->add($qb->expr()->eq('u.id', 2));
       
   729      *
       
   730      *     $qb->update('User', 'u')
       
   731      *         ->set('u.password', md5('password'))
       
   732      *         ->where($or);
       
   733      * </code>
       
   734      *
       
   735      * @param mixed $predicates The restriction predicates.
       
   736      * @return QueryBuilder This QueryBuilder instance.
       
   737      */
       
   738     public function where($predicates)
       
   739     {
       
   740         if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) {
       
   741             $predicates = new Expr\Andx(func_get_args());
       
   742         }
       
   743         
       
   744         return $this->add('where', $predicates);
       
   745     }
       
   746 
       
   747     /**
       
   748      * Adds one or more restrictions to the query results, forming a logical
       
   749      * conjunction with any previously specified restrictions.
       
   750      *
       
   751      * <code>
       
   752      *     $qb = $em->createQueryBuilder()
       
   753      *         ->select('u')
       
   754      *         ->from('User', 'u')
       
   755      *         ->where('u.username LIKE ?')
       
   756      *         ->andWhere('u.is_active = 1');
       
   757      * </code>
       
   758      *
       
   759      * @param mixed $where The query restrictions.
       
   760      * @return QueryBuilder This QueryBuilder instance.
       
   761      * @see where()
       
   762      */
       
   763     public function andWhere($where)
       
   764     {
       
   765         $where = $this->getDQLPart('where');
       
   766         $args = func_get_args();
       
   767         
       
   768         if ($where instanceof Expr\Andx) {
       
   769             $where->addMultiple($args);
       
   770         } else { 
       
   771             array_unshift($args, $where);
       
   772             $where = new Expr\Andx($args);
       
   773         }
       
   774         
       
   775         return $this->add('where', $where, true);
       
   776     }
       
   777 
       
   778     /**
       
   779      * Adds one or more restrictions to the query results, forming a logical
       
   780      * disjunction with any previously specified restrictions.
       
   781      *
       
   782      * <code>
       
   783      *     $qb = $em->createQueryBuilder()
       
   784      *         ->select('u')
       
   785      *         ->from('User', 'u')
       
   786      *         ->where('u.id = 1')
       
   787      *         ->orWhere('u.id = 2');
       
   788      * </code>
       
   789      *
       
   790      * @param mixed $where The WHERE statement
       
   791      * @return QueryBuilder $qb
       
   792      * @see where()
       
   793      */
       
   794     public function orWhere($where)
       
   795     {
       
   796         $where = $this->getDqlPart('where');
       
   797         $args = func_get_args();
       
   798         
       
   799         if ($where instanceof Expr\Orx) {
       
   800             $where->addMultiple($args);
       
   801         } else {            
       
   802             array_unshift($args, $where);
       
   803             $where = new Expr\Orx($args);
       
   804         }
       
   805         
       
   806         return $this->add('where', $where, true);
       
   807     }
       
   808 
       
   809     /**
       
   810      * Specifies a grouping over the results of the query.
       
   811      * Replaces any previously specified groupings, if any.
       
   812      *
       
   813      * <code>
       
   814      *     $qb = $em->createQueryBuilder()
       
   815      *         ->select('u')
       
   816      *         ->from('User', 'u')
       
   817      *         ->groupBy('u.id');
       
   818      * </code>
       
   819      *
       
   820      * @param string $groupBy The grouping expression.
       
   821      * @return QueryBuilder This QueryBuilder instance.
       
   822      */
       
   823     public function groupBy($groupBy)
       
   824     {
       
   825         return $this->add('groupBy', new Expr\GroupBy(func_get_args()));
       
   826     }
       
   827 
       
   828 
       
   829     /**
       
   830      * Adds a grouping expression to the query.
       
   831      *
       
   832      * <code>
       
   833      *     $qb = $em->createQueryBuilder()
       
   834      *         ->select('u')
       
   835      *         ->from('User', 'u')
       
   836      *         ->groupBy('u.lastLogin');
       
   837      *         ->addGroupBy('u.createdAt')
       
   838      * </code>
       
   839      *
       
   840      * @param string $groupBy The grouping expression.
       
   841      * @return QueryBuilder This QueryBuilder instance.
       
   842      */
       
   843     public function addGroupBy($groupBy)
       
   844     {
       
   845         return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true);
       
   846     }
       
   847 
       
   848     /**
       
   849      * Specifies a restriction over the groups of the query.
       
   850      * Replaces any previous having restrictions, if any.
       
   851      *
       
   852      * @param mixed $having The restriction over the groups.
       
   853      * @return QueryBuilder This QueryBuilder instance.
       
   854      */
       
   855     public function having($having)
       
   856     {
       
   857         if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) {
       
   858             $having = new Expr\Andx(func_get_args());
       
   859         }
       
   860         
       
   861         return $this->add('having', $having);
       
   862     }
       
   863 
       
   864     /**
       
   865      * Adds a restriction over the groups of the query, forming a logical
       
   866      * conjunction with any existing having restrictions.
       
   867      *
       
   868      * @param mixed $having The restriction to append.
       
   869      * @return QueryBuilder This QueryBuilder instance.
       
   870      */
       
   871     public function andHaving($having)
       
   872     {
       
   873         $having = $this->getDqlPart('having');
       
   874         $args = func_get_args();
       
   875         
       
   876         if ($having instanceof Expr\Andx) {
       
   877             $having->addMultiple($args);
       
   878         } else { 
       
   879             array_unshift($args, $having);
       
   880             $having = new Expr\Andx($args);
       
   881         }
       
   882         
       
   883         return $this->add('having', $having);
       
   884     }
       
   885 
       
   886     /**
       
   887      * Adds a restriction over the groups of the query, forming a logical
       
   888      * disjunction with any existing having restrictions.
       
   889      *
       
   890      * @param mixed $having The restriction to add.
       
   891      * @return QueryBuilder This QueryBuilder instance.
       
   892      */
       
   893     public function orHaving($having)
       
   894     {
       
   895         $having = $this->getDqlPart('having');
       
   896         $args = func_get_args();
       
   897         
       
   898         if ($having instanceof Expr\Orx) {
       
   899             $having->addMultiple($args);
       
   900         } else { 
       
   901             array_unshift($args, $having);
       
   902             $having = new Expr\Orx($args);
       
   903         }
       
   904 
       
   905         return $this->add('having', $having);
       
   906     }
       
   907 
       
   908     /**
       
   909      * Specifies an ordering for the query results.
       
   910      * Replaces any previously specified orderings, if any.
       
   911      *
       
   912      * @param string $sort The ordering expression.
       
   913      * @param string $order The ordering direction.
       
   914      * @return QueryBuilder This QueryBuilder instance.
       
   915      */
       
   916     public function orderBy($sort, $order = null)
       
   917     {
       
   918         return $this->add('orderBy',  $sort instanceof Expr\OrderBy ? $sort
       
   919                 : new Expr\OrderBy($sort, $order));
       
   920     }
       
   921 
       
   922     /**
       
   923      * Adds an ordering to the query results.
       
   924      *
       
   925      * @param string $sort The ordering expression.
       
   926      * @param string $order The ordering direction.
       
   927      * @return QueryBuilder This QueryBuilder instance.
       
   928      */
       
   929     public function addOrderBy($sort, $order = null)
       
   930     {
       
   931         return $this->add('orderBy', new Expr\OrderBy($sort, $order), true);
       
   932     }
       
   933 
       
   934     /**
       
   935      * Get a query part by its name.
       
   936      *
       
   937      * @param string $queryPartName
       
   938      * @return mixed $queryPart
       
   939      * @todo Rename: getQueryPart (or remove?)
       
   940      */
       
   941     public function getDQLPart($queryPartName)
       
   942     {
       
   943         return $this->_dqlParts[$queryPartName];
       
   944     }
       
   945 
       
   946     /**
       
   947      * Get all query parts.
       
   948      *
       
   949      * @return array $dqlParts
       
   950      * @todo Rename: getQueryParts (or remove?)
       
   951      */
       
   952     public function getDQLParts()
       
   953     {
       
   954         return $this->_dqlParts;
       
   955     }
       
   956 
       
   957     private function _getDQLForDelete()
       
   958     {
       
   959          return 'DELETE'
       
   960               . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
       
   961               . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
       
   962               . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
       
   963     }
       
   964 
       
   965     private function _getDQLForUpdate()
       
   966     {
       
   967          return 'UPDATE'
       
   968               . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
       
   969               . $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', '))
       
   970               . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
       
   971               . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
       
   972     }
       
   973 
       
   974     private function _getDQLForSelect()
       
   975     {
       
   976         $dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
       
   977         
       
   978         $fromParts   = $this->getDQLPart('from');
       
   979         $joinParts   = $this->getDQLPart('join');
       
   980         $fromClauses = array();
       
   981         
       
   982         // Loop through all FROM clauses
       
   983         if ( ! empty($fromParts)) {
       
   984             $dql .= ' FROM ';
       
   985             
       
   986             foreach ($fromParts as $from) {
       
   987                 $fromClause = (string) $from;
       
   988 
       
   989                 if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) {
       
   990                     foreach ($joinParts[$from->getAlias()] as $join) {
       
   991                         $fromClause .= ' ' . ((string) $join);
       
   992                     }
       
   993                 }
       
   994 
       
   995                 $fromClauses[] = $fromClause;
       
   996             }
       
   997         }
       
   998         
       
   999         $dql .= implode(', ', $fromClauses) 
       
  1000               . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
       
  1001               . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
       
  1002               . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING '))
       
  1003               . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
       
  1004         
       
  1005         return $dql;
       
  1006     }
       
  1007 
       
  1008     private function _getReducedDQLQueryPart($queryPartName, $options = array())
       
  1009     {
       
  1010         $queryPart = $this->getDQLPart($queryPartName);
       
  1011         
       
  1012         if (empty($queryPart)) {
       
  1013             return (isset($options['empty']) ? $options['empty'] : '');
       
  1014         }
       
  1015         
       
  1016         return (isset($options['pre']) ? $options['pre'] : '')
       
  1017              . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart)
       
  1018              . (isset($options['post']) ? $options['post'] : '');
       
  1019     }
       
  1020 
       
  1021     /**
       
  1022      * Reset DQL parts
       
  1023      *
       
  1024      * @param array $parts
       
  1025      * @return QueryBuilder
       
  1026      */
       
  1027     public function resetDQLParts($parts = null)
       
  1028     {
       
  1029         if (is_null($parts)) {
       
  1030             $parts = array_keys($this->_dqlParts);
       
  1031         }
       
  1032         foreach ($parts as $part) {
       
  1033             $this->resetDQLPart($part);
       
  1034         }
       
  1035         return $this;
       
  1036     }
       
  1037 
       
  1038     /**
       
  1039      * Reset single DQL part
       
  1040      *
       
  1041      * @param string $part
       
  1042      * @return QueryBuilder;
       
  1043      */
       
  1044     public function resetDQLPart($part)
       
  1045     {
       
  1046         if (is_array($this->_dqlParts[$part])) {
       
  1047             $this->_dqlParts[$part] = array();
       
  1048         } else {
       
  1049             $this->_dqlParts[$part] = null;
       
  1050         }
       
  1051         $this->_state = self::STATE_DIRTY;
       
  1052         return $this;
       
  1053     }
       
  1054 
       
  1055     /**
       
  1056      * Gets a string representation of this QueryBuilder which corresponds to
       
  1057      * the final DQL query being constructed.
       
  1058      *
       
  1059      * @return string The string representation of this QueryBuilder.
       
  1060      */
       
  1061     public function __toString()
       
  1062     {
       
  1063         return $this->getDQL();
       
  1064     }
       
  1065 
       
  1066     /**
       
  1067      * Deep clone of all expression objects in the DQL parts.
       
  1068      *
       
  1069      * @return void
       
  1070      */
       
  1071     public function __clone()
       
  1072     {
       
  1073         foreach ($this->_dqlParts AS $part => $elements) {
       
  1074             if (is_array($this->_dqlParts[$part])) {
       
  1075                 foreach ($this->_dqlParts[$part] AS $idx => $element) {
       
  1076                     if (is_object($element)) {
       
  1077                         $this->_dqlParts[$part][$idx] = clone $element;
       
  1078                     }
       
  1079                 }
       
  1080             } else if (\is_object($elements)) {
       
  1081                 $this->_dqlParts[$part] = clone $elements;
       
  1082             }
       
  1083         }
       
  1084     }
       
  1085 }