vendor/doctrine-dbal/lib/Doctrine/DBAL/Query/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\DBAL\Query;
       
    21 
       
    22 use Doctrine\DBAL\Query\Expression\CompositeExpression,
       
    23     Doctrine\DBAL\Connection;
       
    24 
       
    25 /**
       
    26  * QueryBuilder class is responsible to dynamically create SQL queries.
       
    27  * 
       
    28  * Important: Verify that every feature you use will work with your database vendor.
       
    29  * SQL Query Builder does not attempt to validate the generated SQL at all.
       
    30  * 
       
    31  * The query builder does no validation whatsoever if certain features even work with the
       
    32  * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
       
    33  * even if some vendors such as MySQL support it.
       
    34  *
       
    35  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
       
    36  * @link        www.doctrine-project.com
       
    37  * @since       2.1
       
    38  * @author      Guilherme Blanco <guilhermeblanco@hotmail.com>
       
    39  * @author      Benjamin Eberlei <kontakt@beberlei.de>
       
    40  */
       
    41 class QueryBuilder
       
    42 {
       
    43     /* The query types. */
       
    44     const SELECT = 0;
       
    45     const DELETE = 1;
       
    46     const UPDATE = 2;
       
    47 
       
    48     /** The builder states. */
       
    49     const STATE_DIRTY = 0;
       
    50     const STATE_CLEAN = 1;
       
    51 
       
    52     /**
       
    53      * @var Doctrine\DBAL\Connection DBAL Connection
       
    54      */
       
    55     private $connection = null;
       
    56 
       
    57     /**
       
    58      * @var array The array of SQL parts collected.
       
    59      */
       
    60     private $sqlParts = array(
       
    61         'select'  => array(),
       
    62         'from'    => array(),
       
    63         'join'    => array(),
       
    64         'set'     => array(),
       
    65         'where'   => null,
       
    66         'groupBy' => array(),
       
    67         'having'  => null,
       
    68         'orderBy' => array()
       
    69     );
       
    70 
       
    71     /**
       
    72      * @var string The complete SQL string for this query.
       
    73      */
       
    74     private $sql;
       
    75 
       
    76     /**
       
    77      * @var array The query parameters.
       
    78      */
       
    79     private $params = array();
       
    80 
       
    81     /**
       
    82      * @var array The parameter type map of this query.
       
    83      */
       
    84     private $paramTypes = array();
       
    85 
       
    86     /**
       
    87      * @var integer The type of query this is. Can be select, update or delete.
       
    88      */
       
    89     private $type = self::SELECT;
       
    90 
       
    91     /**
       
    92      * @var integer The state of the query object. Can be dirty or clean.
       
    93      */
       
    94     private $state = self::STATE_CLEAN;
       
    95 
       
    96     /**
       
    97      * @var integer The index of the first result to retrieve.
       
    98      */
       
    99     private $firstResult = null;
       
   100 
       
   101     /**
       
   102      * @var integer The maximum number of results to retrieve.
       
   103      */
       
   104     private $maxResults = null;
       
   105     
       
   106     /**
       
   107      * The counter of bound parameters used with {@see bindValue)
       
   108      * 
       
   109      * @var int
       
   110      */
       
   111     private $boundCounter = 0;
       
   112 
       
   113     /**
       
   114      * Initializes a new <tt>QueryBuilder</tt>.
       
   115      *
       
   116      * @param Doctrine\DBAL\Connection $connection DBAL Connection
       
   117      */
       
   118     public function __construct(Connection $connection)
       
   119     {
       
   120         $this->connection = $connection;
       
   121     }
       
   122 
       
   123     /**
       
   124      * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
       
   125      * This producer method is intended for convenient inline usage. Example:
       
   126      *
       
   127      * <code>
       
   128      *     $qb = $conn->createQueryBuilder()
       
   129      *         ->select('u')
       
   130      *         ->from('users', 'u')
       
   131      *         ->where($qb->expr()->eq('u.id', 1));
       
   132      * </code>
       
   133      *
       
   134      * For more complex expression construction, consider storing the expression
       
   135      * builder object in a local variable.
       
   136      *
       
   137      * @return Doctrine\DBAL\Query\ExpressionBuilder
       
   138      */
       
   139     public function expr()
       
   140     {
       
   141         return $this->connection->getExpressionBuilder();
       
   142     }
       
   143 
       
   144     /**
       
   145      * Get the type of the currently built query.
       
   146      *
       
   147      * @return integer
       
   148      */
       
   149     public function getType()
       
   150     {
       
   151         return $this->type;
       
   152     }
       
   153 
       
   154     /**
       
   155      * Get the associated DBAL Connection for this query builder.
       
   156      *
       
   157      * @return Doctrine\DBAL\Connection
       
   158      */
       
   159     public function getConnection()
       
   160     {
       
   161         return $this->connection;
       
   162     }
       
   163 
       
   164     /**
       
   165      * Get the state of this query builder instance.
       
   166      *
       
   167      * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
       
   168      */
       
   169     public function getState()
       
   170     {
       
   171         return $this->state;
       
   172     }
       
   173     
       
   174     /**
       
   175      * Execute this query using the bound parameters and their types.
       
   176      * 
       
   177      * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
       
   178      * for insert, update and delete statements.
       
   179      * 
       
   180      * @return mixed 
       
   181      */
       
   182     public function execute()
       
   183     {
       
   184         if ($this->type == self::SELECT) {
       
   185             return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
       
   186         } else {
       
   187             return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes);
       
   188         }
       
   189     }
       
   190 
       
   191     /**
       
   192      * Get the complete SQL string formed by the current specifications of this QueryBuilder.
       
   193      *
       
   194      * <code>
       
   195      *     $qb = $em->createQueryBuilder()
       
   196      *         ->select('u')
       
   197      *         ->from('User', 'u')
       
   198      *     echo $qb->getSQL(); // SELECT u FROM User u
       
   199      * </code>
       
   200      *
       
   201      * @return string The sql query string.
       
   202      */
       
   203     public function getSQL()
       
   204     {
       
   205         if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
       
   206             return $this->sql;
       
   207         }
       
   208 
       
   209         $sql = '';
       
   210 
       
   211         switch ($this->type) {
       
   212             case self::DELETE:
       
   213                 $sql = $this->getSQLForDelete();
       
   214                 break;
       
   215 
       
   216             case self::UPDATE:
       
   217                 $sql = $this->getSQLForUpdate();
       
   218                 break;
       
   219 
       
   220             case self::SELECT:
       
   221             default:
       
   222                 $sql = $this->getSQLForSelect();
       
   223                 break;
       
   224         }
       
   225 
       
   226         $this->state = self::STATE_CLEAN;
       
   227         $this->sql = $sql;
       
   228 
       
   229         return $sql;
       
   230     }
       
   231 
       
   232     /**
       
   233      * Sets a query parameter for the query being constructed.
       
   234      *
       
   235      * <code>
       
   236      *     $qb = $conn->createQueryBuilder()
       
   237      *         ->select('u')
       
   238      *         ->from('users', 'u')
       
   239      *         ->where('u.id = :user_id')
       
   240      *         ->setParameter(':user_id', 1);
       
   241      * </code>
       
   242      *
       
   243      * @param string|integer $key The parameter position or name.
       
   244      * @param mixed $value The parameter value.
       
   245      * @param string|null $type PDO::PARAM_*
       
   246      * @return QueryBuilder This QueryBuilder instance.
       
   247      */
       
   248     public function setParameter($key, $value, $type = null)
       
   249     {
       
   250         if ($type !== null) {
       
   251             $this->paramTypes[$key] = $type;
       
   252         }
       
   253 
       
   254         $this->params[$key] = $value;
       
   255 
       
   256         return $this;
       
   257     }
       
   258 
       
   259     /**
       
   260      * Sets a collection of query parameters for the query being constructed.
       
   261      *
       
   262      * <code>
       
   263      *     $qb = $conn->createQueryBuilder()
       
   264      *         ->select('u')
       
   265      *         ->from('users', 'u')
       
   266      *         ->where('u.id = :user_id1 OR u.id = :user_id2')
       
   267      *         ->setParameters(array(
       
   268      *             ':user_id1' => 1,
       
   269      *             ':user_id2' => 2
       
   270      *         ));
       
   271      * </code>
       
   272      *
       
   273      * @param array $params The query parameters to set.
       
   274      * @return QueryBuilder This QueryBuilder instance.
       
   275      */
       
   276     public function setParameters(array $params, array $types = array())
       
   277     {
       
   278         $this->paramTypes = $types;
       
   279         $this->params = $params;
       
   280 
       
   281         return $this;
       
   282     }
       
   283 
       
   284     /**
       
   285      * Gets all defined query parameters for the query being constructed.
       
   286      *
       
   287      * @return array The currently defined query parameters.
       
   288      */
       
   289     public function getParameters()
       
   290     {
       
   291         return $this->params;
       
   292     }
       
   293 
       
   294     /**
       
   295      * Gets a (previously set) query parameter of the query being constructed.
       
   296      *
       
   297      * @param mixed $key The key (index or name) of the bound parameter.
       
   298      * @return mixed The value of the bound parameter.
       
   299      */
       
   300     public function getParameter($key)
       
   301     {
       
   302         return isset($this->params[$key]) ? $this->params[$key] : null;
       
   303     }
       
   304 
       
   305     /**
       
   306      * Sets the position of the first result to retrieve (the "offset").
       
   307      *
       
   308      * @param integer $firstResult The first result to return.
       
   309      * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
       
   310      */
       
   311     public function setFirstResult($firstResult)
       
   312     {
       
   313         $this->state = self::STATE_DIRTY;
       
   314         $this->firstResult = $firstResult;
       
   315         return $this;
       
   316     }
       
   317 
       
   318     /**
       
   319      * Gets the position of the first result the query object was set to retrieve (the "offset").
       
   320      * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
       
   321      *
       
   322      * @return integer The position of the first result.
       
   323      */
       
   324     public function getFirstResult()
       
   325     {
       
   326         return $this->firstResult;
       
   327     }
       
   328 
       
   329     /**
       
   330      * Sets the maximum number of results to retrieve (the "limit").
       
   331      *
       
   332      * @param integer $maxResults The maximum number of results to retrieve.
       
   333      * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
       
   334      */
       
   335     public function setMaxResults($maxResults)
       
   336     {
       
   337         $this->state = self::STATE_DIRTY;
       
   338         $this->maxResults = $maxResults;
       
   339         return $this;
       
   340     }
       
   341 
       
   342     /**
       
   343      * Gets the maximum number of results the query object was set to retrieve (the "limit").
       
   344      * Returns NULL if {@link setMaxResults} was not applied to this query builder.
       
   345      *
       
   346      * @return integer Maximum number of results.
       
   347      */
       
   348     public function getMaxResults()
       
   349     {
       
   350         return $this->maxResults;
       
   351     }
       
   352 
       
   353     /**
       
   354      * Either appends to or replaces a single, generic query part.
       
   355      *
       
   356      * The available parts are: 'select', 'from', 'set', 'where',
       
   357      * 'groupBy', 'having' and 'orderBy'.
       
   358      *
       
   359      * @param string $sqlPartName
       
   360      * @param string $sqlPart
       
   361      * @param string $append
       
   362      * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
       
   363      */
       
   364     public function add($sqlPartName, $sqlPart, $append = false)
       
   365     {
       
   366         $isArray = is_array($sqlPart);
       
   367         $isMultiple = is_array($this->sqlParts[$sqlPartName]);
       
   368 
       
   369         if ($isMultiple && !$isArray) {
       
   370             $sqlPart = array($sqlPart);
       
   371         }
       
   372 
       
   373         $this->state = self::STATE_DIRTY;
       
   374 
       
   375         if ($append) {
       
   376             if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") {
       
   377                 foreach ($sqlPart AS $part) {
       
   378                     $this->sqlParts[$sqlPartName][] = $part;
       
   379                 }
       
   380             } else if ($isArray && is_array($sqlPart[key($sqlPart)])) {
       
   381                 $key = key($sqlPart);
       
   382                 $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
       
   383             } else if ($isMultiple) {
       
   384                 $this->sqlParts[$sqlPartName][] = $sqlPart;
       
   385             } else {
       
   386                 $this->sqlParts[$sqlPartName] = $sqlPart;
       
   387             }
       
   388 
       
   389             return $this;
       
   390         }
       
   391 
       
   392         $this->sqlParts[$sqlPartName] = $sqlPart;
       
   393         
       
   394         return $this;
       
   395     }
       
   396 
       
   397     /**
       
   398      * Specifies an item that is to be returned in the query result.
       
   399      * Replaces any previously specified selections, if any.
       
   400      *
       
   401      * <code>
       
   402      *     $qb = $conn->createQueryBuilder()
       
   403      *         ->select('u.id', 'p.id')
       
   404      *         ->from('users', 'u')
       
   405      *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
       
   406      * </code>
       
   407      *
       
   408      * @param mixed $select The selection expressions.
       
   409      * @return QueryBuilder This QueryBuilder instance.
       
   410      */
       
   411     public function select($select = null)
       
   412     {
       
   413         $this->type = self::SELECT;
       
   414 
       
   415         if (empty($select)) {
       
   416             return $this;
       
   417         }
       
   418 
       
   419         $selects = is_array($select) ? $select : func_get_args();
       
   420 
       
   421         return $this->add('select', $selects, false);
       
   422     }
       
   423 
       
   424     /**
       
   425      * Adds an item that is to be returned in the query result.
       
   426      *
       
   427      * <code>
       
   428      *     $qb = $conn->createQueryBuilder()
       
   429      *         ->select('u.id')
       
   430      *         ->addSelect('p.id')
       
   431      *         ->from('users', 'u')
       
   432      *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
       
   433      * </code>
       
   434      *
       
   435      * @param mixed $select The selection expression.
       
   436      * @return QueryBuilder This QueryBuilder instance.
       
   437      */
       
   438     public function addSelect($select = null)
       
   439     {
       
   440         $this->type = self::SELECT;
       
   441 
       
   442         if (empty($select)) {
       
   443             return $this;
       
   444         }
       
   445 
       
   446         $selects = is_array($select) ? $select : func_get_args();
       
   447 
       
   448         return $this->add('select', $selects, true);
       
   449     }
       
   450 
       
   451     /**
       
   452      * Turns the query being built into a bulk delete query that ranges over
       
   453      * a certain table.
       
   454      *
       
   455      * <code>
       
   456      *     $qb = $conn->createQueryBuilder()
       
   457      *         ->delete('users', 'u')
       
   458      *         ->where('u.id = :user_id');
       
   459      *         ->setParameter(':user_id', 1);
       
   460      * </code>
       
   461      *
       
   462      * @param string $delete The table whose rows are subject to the deletion.
       
   463      * @param string $alias The table alias used in the constructed query.
       
   464      * @return QueryBuilder This QueryBuilder instance.
       
   465      */
       
   466     public function delete($delete = null, $alias = null)
       
   467     {
       
   468         $this->type = self::DELETE;
       
   469 
       
   470         if ( ! $delete) {
       
   471             return $this;
       
   472         }
       
   473 
       
   474         return $this->add('from', array(
       
   475             'table' => $delete,
       
   476             'alias' => $alias
       
   477         ));
       
   478     }
       
   479 
       
   480     /**
       
   481      * Turns the query being built into a bulk update query that ranges over
       
   482      * a certain table
       
   483      *
       
   484      * <code>
       
   485      *     $qb = $conn->createQueryBuilder()
       
   486      *         ->update('users', 'u')
       
   487      *         ->set('u.password', md5('password'))
       
   488      *         ->where('u.id = ?');
       
   489      * </code>
       
   490      *
       
   491      * @param string $update The table whose rows are subject to the update.
       
   492      * @param string $alias The table alias used in the constructed query.
       
   493      * @return QueryBuilder This QueryBuilder instance.
       
   494      */
       
   495     public function update($update = null, $alias = null)
       
   496     {
       
   497         $this->type = self::UPDATE;
       
   498 
       
   499         if ( ! $update) {
       
   500             return $this;
       
   501         }
       
   502 
       
   503         return $this->add('from', array(
       
   504             'table' => $update,
       
   505             'alias' => $alias
       
   506         ));
       
   507     }
       
   508 
       
   509     /**
       
   510      * Create and add a query root corresponding to the table identified by the
       
   511      * given alias, forming a cartesian product with any existing query roots.
       
   512      *
       
   513      * <code>
       
   514      *     $qb = $conn->createQueryBuilder()
       
   515      *         ->select('u.id')
       
   516      *         ->from('users', 'u')
       
   517      * </code>
       
   518      *
       
   519      * @param string $from   The table
       
   520      * @param string $alias  The alias of the table
       
   521      * @return QueryBuilder This QueryBuilder instance.
       
   522      */
       
   523     public function from($from, $alias)
       
   524     {
       
   525         return $this->add('from', array(
       
   526             'table' => $from,
       
   527             'alias' => $alias
       
   528         ), true);
       
   529     }
       
   530 
       
   531     /**
       
   532      * Creates and adds a join to the query.
       
   533      *
       
   534      * <code>
       
   535      *     $qb = $conn->createQueryBuilder()
       
   536      *         ->select('u.name')
       
   537      *         ->from('users', 'u')
       
   538      *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
       
   539      * </code>
       
   540      *
       
   541      * @param string $fromAlias The alias that points to a from clause
       
   542      * @param string $join The table name to join
       
   543      * @param string $alias The alias of the join table
       
   544      * @param string $condition The condition for the join
       
   545      * @return QueryBuilder This QueryBuilder instance.
       
   546      */
       
   547     public function join($fromAlias, $join, $alias, $condition = null)
       
   548     {
       
   549         return $this->innerJoin($fromAlias, $join, $alias, $condition);
       
   550     }
       
   551 
       
   552     /**
       
   553      * Creates and adds a join to the query.
       
   554      *
       
   555      * <code>
       
   556      *     $qb = $conn->createQueryBuilder()
       
   557      *         ->select('u.name')
       
   558      *         ->from('users', 'u')
       
   559      *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
       
   560      * </code>
       
   561      *
       
   562      * @param string $fromAlias The alias that points to a from clause
       
   563      * @param string $join The table name to join
       
   564      * @param string $alias The alias of the join table
       
   565      * @param string $condition The condition for the join
       
   566      * @return QueryBuilder This QueryBuilder instance.
       
   567      */
       
   568     public function innerJoin($fromAlias, $join, $alias, $condition = null)
       
   569     {
       
   570         return $this->add('join', array(
       
   571             $fromAlias => array(
       
   572                 'joinType'      => 'inner',
       
   573                 'joinTable'     => $join,
       
   574                 'joinAlias'     => $alias,
       
   575                 'joinCondition' => $condition
       
   576             )
       
   577         ), true);
       
   578     }
       
   579 
       
   580     /**
       
   581      * Creates and adds a left join to the query.
       
   582      *
       
   583      * <code>
       
   584      *     $qb = $conn->createQueryBuilder()
       
   585      *         ->select('u.name')
       
   586      *         ->from('users', 'u')
       
   587      *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
       
   588      * </code>
       
   589      *
       
   590      * @param string $fromAlias The alias that points to a from clause
       
   591      * @param string $join The table name to join
       
   592      * @param string $alias The alias of the join table
       
   593      * @param string $condition The condition for the join
       
   594      * @return QueryBuilder This QueryBuilder instance.
       
   595      */
       
   596     public function leftJoin($fromAlias, $join, $alias, $condition = null)
       
   597     {
       
   598         return $this->add('join', array(
       
   599             $fromAlias => array(
       
   600                 'joinType'      => 'left',
       
   601                 'joinTable'     => $join,
       
   602                 'joinAlias'     => $alias,
       
   603                 'joinCondition' => $condition
       
   604             )
       
   605         ), true);
       
   606     }
       
   607     
       
   608     /**
       
   609      * Creates and adds a right join to the query.
       
   610      *
       
   611      * <code>
       
   612      *     $qb = $conn->createQueryBuilder()
       
   613      *         ->select('u.name')
       
   614      *         ->from('users', 'u')
       
   615      *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
       
   616      * </code>
       
   617      *
       
   618      * @param string $fromAlias The alias that points to a from clause
       
   619      * @param string $join The table name to join
       
   620      * @param string $alias The alias of the join table
       
   621      * @param string $condition The condition for the join
       
   622      * @return QueryBuilder This QueryBuilder instance.
       
   623      */
       
   624     public function rightJoin($fromAlias, $join, $alias, $condition = null)
       
   625     {
       
   626         return $this->add('join', array(
       
   627             $fromAlias => array(
       
   628                 'joinType'      => 'right',
       
   629                 'joinTable'     => $join,
       
   630                 'joinAlias'     => $alias,
       
   631                 'joinCondition' => $condition
       
   632             )
       
   633         ), true);
       
   634     }
       
   635 
       
   636     /**
       
   637      * Sets a new value for a column in a bulk update query.
       
   638      *
       
   639      * <code>
       
   640      *     $qb = $conn->createQueryBuilder()
       
   641      *         ->update('users', 'u')
       
   642      *         ->set('u.password', md5('password'))
       
   643      *         ->where('u.id = ?');
       
   644      * </code>
       
   645      *
       
   646      * @param string $key The column to set.
       
   647      * @param string $value The value, expression, placeholder, etc.
       
   648      * @return QueryBuilder This QueryBuilder instance.
       
   649      */
       
   650     public function set($key, $value)
       
   651     {
       
   652         return $this->add('set', $key .' = ' . $value, true);
       
   653     }
       
   654 
       
   655     /**
       
   656      * Specifies one or more restrictions to the query result.
       
   657      * Replaces any previously specified restrictions, if any.
       
   658      *
       
   659      * <code>
       
   660      *     $qb = $conn->createQueryBuilder()
       
   661      *         ->select('u.name')
       
   662      *         ->from('users', 'u')
       
   663      *         ->where('u.id = ?');
       
   664      *
       
   665      *     // You can optionally programatically build and/or expressions
       
   666      *     $qb = $conn->createQueryBuilder();
       
   667      *
       
   668      *     $or = $qb->expr()->orx();
       
   669      *     $or->add($qb->expr()->eq('u.id', 1));
       
   670      *     $or->add($qb->expr()->eq('u.id', 2));
       
   671      *
       
   672      *     $qb->update('users', 'u')
       
   673      *         ->set('u.password', md5('password'))
       
   674      *         ->where($or);
       
   675      * </code>
       
   676      *
       
   677      * @param mixed $predicates The restriction predicates.
       
   678      * @return QueryBuilder This QueryBuilder instance.
       
   679      */
       
   680     public function where($predicates)
       
   681     {
       
   682         if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression) ) {
       
   683             $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
       
   684         }
       
   685 
       
   686         return $this->add('where', $predicates);
       
   687     }
       
   688 
       
   689     /**
       
   690      * Adds one or more restrictions to the query results, forming a logical
       
   691      * conjunction with any previously specified restrictions.
       
   692      *
       
   693      * <code>
       
   694      *     $qb = $conn->createQueryBuilder()
       
   695      *         ->select('u')
       
   696      *         ->from('users', 'u')
       
   697      *         ->where('u.username LIKE ?')
       
   698      *         ->andWhere('u.is_active = 1');
       
   699      * </code>
       
   700      *
       
   701      * @param mixed $where The query restrictions.
       
   702      * @return QueryBuilder This QueryBuilder instance.
       
   703      * @see where()
       
   704      */
       
   705     public function andWhere($where)
       
   706     {
       
   707         $where = $this->getQueryPart('where');
       
   708         $args = func_get_args();
       
   709 
       
   710         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
       
   711             $where->addMultiple($args);
       
   712         } else {
       
   713             array_unshift($args, $where);
       
   714             $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
       
   715         }
       
   716 
       
   717         return $this->add('where', $where, true);
       
   718     }
       
   719 
       
   720     /**
       
   721      * Adds one or more restrictions to the query results, forming a logical
       
   722      * disjunction with any previously specified restrictions.
       
   723      *
       
   724      * <code>
       
   725      *     $qb = $em->createQueryBuilder()
       
   726      *         ->select('u.name')
       
   727      *         ->from('users', 'u')
       
   728      *         ->where('u.id = 1')
       
   729      *         ->orWhere('u.id = 2');
       
   730      * </code>
       
   731      *
       
   732      * @param mixed $where The WHERE statement
       
   733      * @return QueryBuilder $qb
       
   734      * @see where()
       
   735      */
       
   736     public function orWhere($where)
       
   737     {
       
   738         $where = $this->getQueryPart('where');
       
   739         $args = func_get_args();
       
   740 
       
   741         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
       
   742             $where->addMultiple($args);
       
   743         } else {
       
   744             array_unshift($args, $where);
       
   745             $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
       
   746         }
       
   747 
       
   748         return $this->add('where', $where, true);
       
   749     }
       
   750 
       
   751     /**
       
   752      * Specifies a grouping over the results of the query.
       
   753      * Replaces any previously specified groupings, if any.
       
   754      *
       
   755      * <code>
       
   756      *     $qb = $conn->createQueryBuilder()
       
   757      *         ->select('u.name')
       
   758      *         ->from('users', 'u')
       
   759      *         ->groupBy('u.id');
       
   760      * </code>
       
   761      *
       
   762      * @param mixed $groupBy The grouping expression.
       
   763      * @return QueryBuilder This QueryBuilder instance.
       
   764      */
       
   765     public function groupBy($groupBy)
       
   766     {
       
   767         if (empty($groupBy)) {
       
   768             return $this;
       
   769         }
       
   770 
       
   771         $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
       
   772 
       
   773         return $this->add('groupBy', $groupBy, false);
       
   774     }
       
   775 
       
   776 
       
   777     /**
       
   778      * Adds a grouping expression to the query.
       
   779      *
       
   780      * <code>
       
   781      *     $qb = $conn->createQueryBuilder()
       
   782      *         ->select('u.name')
       
   783      *         ->from('users', 'u')
       
   784      *         ->groupBy('u.lastLogin');
       
   785      *         ->addGroupBy('u.createdAt')
       
   786      * </code>
       
   787      *
       
   788      * @param mixed $groupBy The grouping expression.
       
   789      * @return QueryBuilder This QueryBuilder instance.
       
   790      */
       
   791     public function addGroupBy($groupBy)
       
   792     {
       
   793         if (empty($groupBy)) {
       
   794             return $this;
       
   795         }
       
   796 
       
   797         $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
       
   798 
       
   799         return $this->add('groupBy', $groupBy, true);
       
   800     }
       
   801 
       
   802     /**
       
   803      * Specifies a restriction over the groups of the query.
       
   804      * Replaces any previous having restrictions, if any.
       
   805      *
       
   806      * @param mixed $having The restriction over the groups.
       
   807      * @return QueryBuilder This QueryBuilder instance.
       
   808      */
       
   809     public function having($having)
       
   810     {
       
   811         if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) {
       
   812             $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
       
   813         }
       
   814 
       
   815         return $this->add('having', $having);
       
   816     }
       
   817 
       
   818     /**
       
   819      * Adds a restriction over the groups of the query, forming a logical
       
   820      * conjunction with any existing having restrictions.
       
   821      *
       
   822      * @param mixed $having The restriction to append.
       
   823      * @return QueryBuilder This QueryBuilder instance.
       
   824      */
       
   825     public function andHaving($having)
       
   826     {
       
   827         $having = $this->getQueryPart('having');
       
   828         $args = func_get_args();
       
   829 
       
   830         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
       
   831             $having->addMultiple($args);
       
   832         } else {
       
   833             array_unshift($args, $having);
       
   834             $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
       
   835         }
       
   836 
       
   837         return $this->add('having', $having);
       
   838     }
       
   839 
       
   840     /**
       
   841      * Adds a restriction over the groups of the query, forming a logical
       
   842      * disjunction with any existing having restrictions.
       
   843      *
       
   844      * @param mixed $having The restriction to add.
       
   845      * @return QueryBuilder This QueryBuilder instance.
       
   846      */
       
   847     public function orHaving($having)
       
   848     {
       
   849         $having = $this->getQueryPart('having');
       
   850         $args = func_get_args();
       
   851 
       
   852         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
       
   853             $having->addMultiple($args);
       
   854         } else {
       
   855             array_unshift($args, $having);
       
   856             $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
       
   857         }
       
   858 
       
   859         return $this->add('having', $having);
       
   860     }
       
   861 
       
   862     /**
       
   863      * Specifies an ordering for the query results.
       
   864      * Replaces any previously specified orderings, if any.
       
   865      *
       
   866      * @param string $sort The ordering expression.
       
   867      * @param string $order The ordering direction.
       
   868      * @return QueryBuilder This QueryBuilder instance.
       
   869      */
       
   870     public function orderBy($sort, $order = null)
       
   871     {
       
   872         return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
       
   873     }
       
   874 
       
   875     /**
       
   876      * Adds an ordering to the query results.
       
   877      *
       
   878      * @param string $sort The ordering expression.
       
   879      * @param string $order The ordering direction.
       
   880      * @return QueryBuilder This QueryBuilder instance.
       
   881      */
       
   882     public function addOrderBy($sort, $order = null)
       
   883     {
       
   884         return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
       
   885     }
       
   886 
       
   887     /**
       
   888      * Get a query part by its name.
       
   889      *
       
   890      * @param string $queryPartName
       
   891      * @return mixed $queryPart
       
   892      */
       
   893     public function getQueryPart($queryPartName)
       
   894     {
       
   895         return $this->sqlParts[$queryPartName];
       
   896     }
       
   897 
       
   898     /**
       
   899      * Get all query parts.
       
   900      *
       
   901      * @return array $sqlParts
       
   902      */
       
   903     public function getQueryParts()
       
   904     {
       
   905         return $this->sqlParts;
       
   906     }
       
   907 
       
   908     /**
       
   909      * Reset SQL parts
       
   910      *
       
   911      * @param array $queryPartNames
       
   912      * @return QueryBuilder
       
   913      */
       
   914     public function resetQueryParts($queryPartNames = null)
       
   915     {
       
   916         if (is_null($queryPartNames)) {
       
   917             $queryPartNames = array_keys($this->sqlParts);
       
   918         }
       
   919 
       
   920         foreach ($queryPartNames as $queryPartName) {
       
   921             $this->resetQueryPart($queryPartName);
       
   922         }
       
   923 
       
   924         return $this;
       
   925     }
       
   926 
       
   927     /**
       
   928      * Reset single SQL part
       
   929      *
       
   930      * @param string $queryPartName
       
   931      * @return QueryBuilder
       
   932      */
       
   933     public function resetQueryPart($queryPartName)
       
   934     {
       
   935         $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName])
       
   936             ? array() : null;
       
   937 
       
   938         $this->state = self::STATE_DIRTY;
       
   939 
       
   940         return $this;
       
   941     }
       
   942     
       
   943     /**
       
   944      * Converts this instance into a SELECT string in SQL.
       
   945      * 
       
   946      * @return string
       
   947      */
       
   948     private function getSQLForSelect()
       
   949     {
       
   950         $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM ';
       
   951         
       
   952         $fromClauses = array();
       
   953         
       
   954         // Loop through all FROM clauses
       
   955         foreach ($this->sqlParts['from'] as $from) {
       
   956             $fromClause = $from['table'] . ' ' . $from['alias'];
       
   957             
       
   958             if (isset($this->sqlParts['join'][$from['alias']])) {
       
   959                 foreach ($this->sqlParts['join'][$from['alias']] as $join) {
       
   960                     $fromClause .= ' ' . strtoupper($join['joinType']) 
       
   961                                  . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] 
       
   962                                  . ' ON ' . ((string) $join['joinCondition']);
       
   963                 }
       
   964             }
       
   965             
       
   966             $fromClauses[] = $fromClause;
       
   967         }
       
   968         
       
   969         $query .= implode(', ', $fromClauses) 
       
   970                 . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
       
   971                 . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
       
   972                 . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
       
   973                 . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
       
   974         
       
   975         return ($this->maxResults === null && $this->firstResult == null) 
       
   976             ? $query
       
   977             : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult);
       
   978     }
       
   979     
       
   980     /**
       
   981      * Converts this instance into an UPDATE string in SQL.
       
   982      * 
       
   983      * @return string
       
   984      */
       
   985     private function getSQLForUpdate()
       
   986     {
       
   987         $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
       
   988         $query = 'UPDATE ' . $table 
       
   989                . ' SET ' . implode(", ", $this->sqlParts['set'])
       
   990                . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
       
   991         
       
   992         return $query;
       
   993     }
       
   994     
       
   995     /**
       
   996      * Converts this instance into a DELETE string in SQL.
       
   997      * 
       
   998      * @return string
       
   999      */
       
  1000     private function getSQLForDelete()
       
  1001     {
       
  1002         $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
       
  1003         $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
       
  1004         
       
  1005         return $query;
       
  1006     }
       
  1007 
       
  1008     /**
       
  1009      * Gets a string representation of this QueryBuilder which corresponds to
       
  1010      * the final SQL query being constructed.
       
  1011      *
       
  1012      * @return string The string representation of this QueryBuilder.
       
  1013      */
       
  1014     public function __toString()
       
  1015     {
       
  1016         return $this->getSQL();
       
  1017     }
       
  1018     
       
  1019     /**
       
  1020      * Create a new named parameter and bind the value $value to it.
       
  1021      *
       
  1022      * This method provides a shortcut for PDOStatement::bindValue
       
  1023      * when using prepared statements.
       
  1024      *
       
  1025      * The parameter $value specifies the value that you want to bind. If
       
  1026      * $placeholder is not provided bindValue() will automatically create a
       
  1027      * placeholder for you. An automatic placeholder will be of the name
       
  1028      * ':dcValue1', ':dcValue2' etc.
       
  1029      *
       
  1030      * For more information see {@link http://php.net/pdostatement-bindparam}
       
  1031      *
       
  1032      * Example:
       
  1033      * <code>
       
  1034      * $value = 2;
       
  1035      * $q->eq( 'id', $q->bindValue( $value ) );
       
  1036      * $stmt = $q->executeQuery(); // executed with 'id = 2'
       
  1037      * </code>
       
  1038      *
       
  1039      * @license New BSD License
       
  1040      * @link http://www.zetacomponents.org
       
  1041      * @param mixed $value
       
  1042      * @param mixed $type
       
  1043      * @param string $placeHolder the name to bind with. The string must start with a colon ':'.
       
  1044      * @return string the placeholder name used.
       
  1045      */
       
  1046     public function createNamedParameter( $value, $type = \PDO::PARAM_STR, $placeHolder = null )
       
  1047     {
       
  1048         if ( $placeHolder === null ) {
       
  1049             $this->boundCounter++;
       
  1050             $placeHolder = ":dcValue" . $this->boundCounter;
       
  1051         }
       
  1052         $this->setParameter(substr($placeHolder, 1), $value, $type);
       
  1053 
       
  1054         return $placeHolder;
       
  1055     }
       
  1056     
       
  1057     /**
       
  1058      * Create a new positional parameter and bind the given value to it.
       
  1059      * 
       
  1060      * Attention: If you are using positional parameters with the query builder you have
       
  1061      * to be very careful to bind all parameters in the order they appear in the SQL
       
  1062      * statement , otherwise they get bound in the wrong order which can lead to serious
       
  1063      * bugs in your code.
       
  1064      * 
       
  1065      * Example:
       
  1066      * <code>
       
  1067      *  $qb = $conn->createQueryBuilder();
       
  1068      *  $qb->select('u.*')
       
  1069      *     ->from('users', 'u')
       
  1070      *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR))
       
  1071      *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR))
       
  1072      * </code>
       
  1073      * 
       
  1074      * @param  mixed $value
       
  1075      * @param  mixed $type
       
  1076      * @return string
       
  1077      */
       
  1078     public function createPositionalParameter($value, $type = \PDO::PARAM_STR)
       
  1079     {
       
  1080         $this->boundCounter++;
       
  1081         $this->setParameter($this->boundCounter, $value, $type);
       
  1082         return "?";
       
  1083     }
       
  1084 }