Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Query / QueryBuilder.php
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 MIT license. 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\Expression\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      * @param array $types  The query parameters types to set.
275      * @return QueryBuilder This QueryBuilder instance.
276      */
277     public function setParameters(array $params, array $types = array())
278     {
279         $this->paramTypes = $types;
280         $this->params = $params;
281
282         return $this;
283     }
284
285     /**
286      * Gets all defined query parameters for the query being constructed.
287      *
288      * @return array The currently defined query parameters.
289      */
290     public function getParameters()
291     {
292         return $this->params;
293     }
294
295     /**
296      * Gets a (previously set) query parameter of the query being constructed.
297      *
298      * @param mixed $key The key (index or name) of the bound parameter.
299      * @return mixed The value of the bound parameter.
300      */
301     public function getParameter($key)
302     {
303         return isset($this->params[$key]) ? $this->params[$key] : null;
304     }
305
306     /**
307      * Sets the position of the first result to retrieve (the "offset").
308      *
309      * @param integer $firstResult The first result to return.
310      * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
311      */
312     public function setFirstResult($firstResult)
313     {
314         $this->state = self::STATE_DIRTY;
315         $this->firstResult = $firstResult;
316         return $this;
317     }
318
319     /**
320      * Gets the position of the first result the query object was set to retrieve (the "offset").
321      * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
322      *
323      * @return integer The position of the first result.
324      */
325     public function getFirstResult()
326     {
327         return $this->firstResult;
328     }
329
330     /**
331      * Sets the maximum number of results to retrieve (the "limit").
332      *
333      * @param integer $maxResults The maximum number of results to retrieve.
334      * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
335      */
336     public function setMaxResults($maxResults)
337     {
338         $this->state = self::STATE_DIRTY;
339         $this->maxResults = $maxResults;
340         return $this;
341     }
342
343     /**
344      * Gets the maximum number of results the query object was set to retrieve (the "limit").
345      * Returns NULL if {@link setMaxResults} was not applied to this query builder.
346      *
347      * @return integer Maximum number of results.
348      */
349     public function getMaxResults()
350     {
351         return $this->maxResults;
352     }
353
354     /**
355      * Either appends to or replaces a single, generic query part.
356      *
357      * The available parts are: 'select', 'from', 'set', 'where',
358      * 'groupBy', 'having' and 'orderBy'.
359      *
360      * @param string  $sqlPartName
361      * @param string  $sqlPart
362      * @param boolean $append
363      * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance.
364      */
365     public function add($sqlPartName, $sqlPart, $append = false)
366     {
367         $isArray = is_array($sqlPart);
368         $isMultiple = is_array($this->sqlParts[$sqlPartName]);
369
370         if ($isMultiple && !$isArray) {
371             $sqlPart = array($sqlPart);
372         }
373
374         $this->state = self::STATE_DIRTY;
375
376         if ($append) {
377             if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") {
378                 foreach ($sqlPart as $part) {
379                     $this->sqlParts[$sqlPartName][] = $part;
380                 }
381             } else if ($isArray && is_array($sqlPart[key($sqlPart)])) {
382                 $key = key($sqlPart);
383                 $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
384             } else if ($isMultiple) {
385                 $this->sqlParts[$sqlPartName][] = $sqlPart;
386             } else {
387                 $this->sqlParts[$sqlPartName] = $sqlPart;
388             }
389
390             return $this;
391         }
392
393         $this->sqlParts[$sqlPartName] = $sqlPart;
394
395         return $this;
396     }
397
398     /**
399      * Specifies an item that is to be returned in the query result.
400      * Replaces any previously specified selections, if any.
401      *
402      * <code>
403      *     $qb = $conn->createQueryBuilder()
404      *         ->select('u.id', 'p.id')
405      *         ->from('users', 'u')
406      *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
407      * </code>
408      *
409      * @param mixed $select The selection expressions.
410      * @return QueryBuilder This QueryBuilder instance.
411      */
412     public function select($select = null)
413     {
414         $this->type = self::SELECT;
415
416         if (empty($select)) {
417             return $this;
418         }
419
420         $selects = is_array($select) ? $select : func_get_args();
421
422         return $this->add('select', $selects, false);
423     }
424
425     /**
426      * Adds an item that is to be returned in the query result.
427      *
428      * <code>
429      *     $qb = $conn->createQueryBuilder()
430      *         ->select('u.id')
431      *         ->addSelect('p.id')
432      *         ->from('users', 'u')
433      *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
434      * </code>
435      *
436      * @param mixed $select The selection expression.
437      * @return QueryBuilder This QueryBuilder instance.
438      */
439     public function addSelect($select = null)
440     {
441         $this->type = self::SELECT;
442
443         if (empty($select)) {
444             return $this;
445         }
446
447         $selects = is_array($select) ? $select : func_get_args();
448
449         return $this->add('select', $selects, true);
450     }
451
452     /**
453      * Turns the query being built into a bulk delete query that ranges over
454      * a certain table.
455      *
456      * <code>
457      *     $qb = $conn->createQueryBuilder()
458      *         ->delete('users', 'u')
459      *         ->where('u.id = :user_id');
460      *         ->setParameter(':user_id', 1);
461      * </code>
462      *
463      * @param string $delete The table whose rows are subject to the deletion.
464      * @param string $alias The table alias used in the constructed query.
465      * @return QueryBuilder This QueryBuilder instance.
466      */
467     public function delete($delete = null, $alias = null)
468     {
469         $this->type = self::DELETE;
470
471         if ( ! $delete) {
472             return $this;
473         }
474
475         return $this->add('from', array(
476             'table' => $delete,
477             'alias' => $alias
478         ));
479     }
480
481     /**
482      * Turns the query being built into a bulk update query that ranges over
483      * a certain table
484      *
485      * <code>
486      *     $qb = $conn->createQueryBuilder()
487      *         ->update('users', 'u')
488      *         ->set('u.password', md5('password'))
489      *         ->where('u.id = ?');
490      * </code>
491      *
492      * @param string $update The table whose rows are subject to the update.
493      * @param string $alias The table alias used in the constructed query.
494      * @return QueryBuilder This QueryBuilder instance.
495      */
496     public function update($update = null, $alias = null)
497     {
498         $this->type = self::UPDATE;
499
500         if ( ! $update) {
501             return $this;
502         }
503
504         return $this->add('from', array(
505             'table' => $update,
506             'alias' => $alias
507         ));
508     }
509
510     /**
511      * Create and add a query root corresponding to the table identified by the
512      * given alias, forming a cartesian product with any existing query roots.
513      *
514      * <code>
515      *     $qb = $conn->createQueryBuilder()
516      *         ->select('u.id')
517      *         ->from('users', 'u')
518      * </code>
519      *
520      * @param string $from   The table
521      * @param string $alias  The alias of the table
522      * @return QueryBuilder This QueryBuilder instance.
523      */
524     public function from($from, $alias)
525     {
526         return $this->add('from', array(
527             'table' => $from,
528             'alias' => $alias
529         ), true);
530     }
531
532     /**
533      * Creates and adds a join to the query.
534      *
535      * <code>
536      *     $qb = $conn->createQueryBuilder()
537      *         ->select('u.name')
538      *         ->from('users', 'u')
539      *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
540      * </code>
541      *
542      * @param string $fromAlias The alias that points to a from clause
543      * @param string $join The table name to join
544      * @param string $alias The alias of the join table
545      * @param string $condition The condition for the join
546      * @return QueryBuilder This QueryBuilder instance.
547      */
548     public function join($fromAlias, $join, $alias, $condition = null)
549     {
550         return $this->innerJoin($fromAlias, $join, $alias, $condition);
551     }
552
553     /**
554      * Creates and adds a join to the query.
555      *
556      * <code>
557      *     $qb = $conn->createQueryBuilder()
558      *         ->select('u.name')
559      *         ->from('users', 'u')
560      *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
561      * </code>
562      *
563      * @param string $fromAlias The alias that points to a from clause
564      * @param string $join The table name to join
565      * @param string $alias The alias of the join table
566      * @param string $condition The condition for the join
567      * @return QueryBuilder This QueryBuilder instance.
568      */
569     public function innerJoin($fromAlias, $join, $alias, $condition = null)
570     {
571         return $this->add('join', array(
572             $fromAlias => array(
573                 'joinType'      => 'inner',
574                 'joinTable'     => $join,
575                 'joinAlias'     => $alias,
576                 'joinCondition' => $condition
577             )
578         ), true);
579     }
580
581     /**
582      * Creates and adds a left join to the query.
583      *
584      * <code>
585      *     $qb = $conn->createQueryBuilder()
586      *         ->select('u.name')
587      *         ->from('users', 'u')
588      *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
589      * </code>
590      *
591      * @param string $fromAlias The alias that points to a from clause
592      * @param string $join The table name to join
593      * @param string $alias The alias of the join table
594      * @param string $condition The condition for the join
595      * @return QueryBuilder This QueryBuilder instance.
596      */
597     public function leftJoin($fromAlias, $join, $alias, $condition = null)
598     {
599         return $this->add('join', array(
600             $fromAlias => array(
601                 'joinType'      => 'left',
602                 'joinTable'     => $join,
603                 'joinAlias'     => $alias,
604                 'joinCondition' => $condition
605             )
606         ), true);
607     }
608
609     /**
610      * Creates and adds a right join to the query.
611      *
612      * <code>
613      *     $qb = $conn->createQueryBuilder()
614      *         ->select('u.name')
615      *         ->from('users', 'u')
616      *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
617      * </code>
618      *
619      * @param string $fromAlias The alias that points to a from clause
620      * @param string $join The table name to join
621      * @param string $alias The alias of the join table
622      * @param string $condition The condition for the join
623      * @return QueryBuilder This QueryBuilder instance.
624      */
625     public function rightJoin($fromAlias, $join, $alias, $condition = null)
626     {
627         return $this->add('join', array(
628             $fromAlias => array(
629                 'joinType'      => 'right',
630                 'joinTable'     => $join,
631                 'joinAlias'     => $alias,
632                 'joinCondition' => $condition
633             )
634         ), true);
635     }
636
637     /**
638      * Sets a new value for a column in a bulk update query.
639      *
640      * <code>
641      *     $qb = $conn->createQueryBuilder()
642      *         ->update('users', 'u')
643      *         ->set('u.password', md5('password'))
644      *         ->where('u.id = ?');
645      * </code>
646      *
647      * @param string $key The column to set.
648      * @param string $value The value, expression, placeholder, etc.
649      * @return QueryBuilder This QueryBuilder instance.
650      */
651     public function set($key, $value)
652     {
653         return $this->add('set', $key .' = ' . $value, true);
654     }
655
656     /**
657      * Specifies one or more restrictions to the query result.
658      * Replaces any previously specified restrictions, if any.
659      *
660      * <code>
661      *     $qb = $conn->createQueryBuilder()
662      *         ->select('u.name')
663      *         ->from('users', 'u')
664      *         ->where('u.id = ?');
665      *
666      *     // You can optionally programatically build and/or expressions
667      *     $qb = $conn->createQueryBuilder();
668      *
669      *     $or = $qb->expr()->orx();
670      *     $or->add($qb->expr()->eq('u.id', 1));
671      *     $or->add($qb->expr()->eq('u.id', 2));
672      *
673      *     $qb->update('users', 'u')
674      *         ->set('u.password', md5('password'))
675      *         ->where($or);
676      * </code>
677      *
678      * @param mixed $predicates The restriction predicates.
679      * @return QueryBuilder This QueryBuilder instance.
680      */
681     public function where($predicates)
682     {
683         if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression) ) {
684             $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
685         }
686
687         return $this->add('where', $predicates);
688     }
689
690     /**
691      * Adds one or more restrictions to the query results, forming a logical
692      * conjunction with any previously specified restrictions.
693      *
694      * <code>
695      *     $qb = $conn->createQueryBuilder()
696      *         ->select('u')
697      *         ->from('users', 'u')
698      *         ->where('u.username LIKE ?')
699      *         ->andWhere('u.is_active = 1');
700      * </code>
701      *
702      * @param mixed $where The query restrictions.
703      * @return QueryBuilder This QueryBuilder instance.
704      * @see where()
705      */
706     public function andWhere($where)
707     {
708         $where = $this->getQueryPart('where');
709         $args = func_get_args();
710
711         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
712             $where->addMultiple($args);
713         } else {
714             array_unshift($args, $where);
715             $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
716         }
717
718         return $this->add('where', $where, true);
719     }
720
721     /**
722      * Adds one or more restrictions to the query results, forming a logical
723      * disjunction with any previously specified restrictions.
724      *
725      * <code>
726      *     $qb = $em->createQueryBuilder()
727      *         ->select('u.name')
728      *         ->from('users', 'u')
729      *         ->where('u.id = 1')
730      *         ->orWhere('u.id = 2');
731      * </code>
732      *
733      * @param mixed $where The WHERE statement
734      * @return QueryBuilder $qb
735      * @see where()
736      */
737     public function orWhere($where)
738     {
739         $where = $this->getQueryPart('where');
740         $args = func_get_args();
741
742         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
743             $where->addMultiple($args);
744         } else {
745             array_unshift($args, $where);
746             $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
747         }
748
749         return $this->add('where', $where, true);
750     }
751
752     /**
753      * Specifies a grouping over the results of the query.
754      * Replaces any previously specified groupings, if any.
755      *
756      * <code>
757      *     $qb = $conn->createQueryBuilder()
758      *         ->select('u.name')
759      *         ->from('users', 'u')
760      *         ->groupBy('u.id');
761      * </code>
762      *
763      * @param mixed $groupBy The grouping expression.
764      * @return QueryBuilder This QueryBuilder instance.
765      */
766     public function groupBy($groupBy)
767     {
768         if (empty($groupBy)) {
769             return $this;
770         }
771
772         $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
773
774         return $this->add('groupBy', $groupBy, false);
775     }
776
777
778     /**
779      * Adds a grouping expression to the query.
780      *
781      * <code>
782      *     $qb = $conn->createQueryBuilder()
783      *         ->select('u.name')
784      *         ->from('users', 'u')
785      *         ->groupBy('u.lastLogin');
786      *         ->addGroupBy('u.createdAt')
787      * </code>
788      *
789      * @param mixed $groupBy The grouping expression.
790      * @return QueryBuilder This QueryBuilder instance.
791      */
792     public function addGroupBy($groupBy)
793     {
794         if (empty($groupBy)) {
795             return $this;
796         }
797
798         $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
799
800         return $this->add('groupBy', $groupBy, true);
801     }
802
803     /**
804      * Specifies a restriction over the groups of the query.
805      * Replaces any previous having restrictions, if any.
806      *
807      * @param mixed $having The restriction over the groups.
808      * @return QueryBuilder This QueryBuilder instance.
809      */
810     public function having($having)
811     {
812         if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) {
813             $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
814         }
815
816         return $this->add('having', $having);
817     }
818
819     /**
820      * Adds a restriction over the groups of the query, forming a logical
821      * conjunction with any existing having restrictions.
822      *
823      * @param mixed $having The restriction to append.
824      * @return QueryBuilder This QueryBuilder instance.
825      */
826     public function andHaving($having)
827     {
828         $having = $this->getQueryPart('having');
829         $args = func_get_args();
830
831         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
832             $having->addMultiple($args);
833         } else {
834             array_unshift($args, $having);
835             $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
836         }
837
838         return $this->add('having', $having);
839     }
840
841     /**
842      * Adds a restriction over the groups of the query, forming a logical
843      * disjunction with any existing having restrictions.
844      *
845      * @param mixed $having The restriction to add.
846      * @return QueryBuilder This QueryBuilder instance.
847      */
848     public function orHaving($having)
849     {
850         $having = $this->getQueryPart('having');
851         $args = func_get_args();
852
853         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
854             $having->addMultiple($args);
855         } else {
856             array_unshift($args, $having);
857             $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
858         }
859
860         return $this->add('having', $having);
861     }
862
863     /**
864      * Specifies an ordering for the query results.
865      * Replaces any previously specified orderings, if any.
866      *
867      * @param string $sort The ordering expression.
868      * @param string $order The ordering direction.
869      * @return QueryBuilder This QueryBuilder instance.
870      */
871     public function orderBy($sort, $order = null)
872     {
873         return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
874     }
875
876     /**
877      * Adds an ordering to the query results.
878      *
879      * @param string $sort The ordering expression.
880      * @param string $order The ordering direction.
881      * @return QueryBuilder This QueryBuilder instance.
882      */
883     public function addOrderBy($sort, $order = null)
884     {
885         return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
886     }
887
888     /**
889      * Get a query part by its name.
890      *
891      * @param string $queryPartName
892      * @return mixed $queryPart
893      */
894     public function getQueryPart($queryPartName)
895     {
896         return $this->sqlParts[$queryPartName];
897     }
898
899     /**
900      * Get all query parts.
901      *
902      * @return array $sqlParts
903      */
904     public function getQueryParts()
905     {
906         return $this->sqlParts;
907     }
908
909     /**
910      * Reset SQL parts
911      *
912      * @param array $queryPartNames
913      * @return QueryBuilder
914      */
915     public function resetQueryParts($queryPartNames = null)
916     {
917         if (is_null($queryPartNames)) {
918             $queryPartNames = array_keys($this->sqlParts);
919         }
920
921         foreach ($queryPartNames as $queryPartName) {
922             $this->resetQueryPart($queryPartName);
923         }
924
925         return $this;
926     }
927
928     /**
929      * Reset single SQL part
930      *
931      * @param string $queryPartName
932      * @return QueryBuilder
933      */
934     public function resetQueryPart($queryPartName)
935     {
936         $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName])
937             ? array() : null;
938
939         $this->state = self::STATE_DIRTY;
940
941         return $this;
942     }
943
944     private function getSQLForSelect()
945     {
946         $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM ';
947
948         $fromClauses = array();
949
950         // Loop through all FROM clauses
951         foreach ($this->sqlParts['from'] as $from) {
952             $fromClause = $from['table'] . ' ' . $from['alias'];
953
954             if (isset($this->sqlParts['join'][$from['alias']])) {
955                 foreach ($this->sqlParts['join'][$from['alias']] as $join) {
956                     $fromClause .= ' ' . strtoupper($join['joinType'])
957                                  . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']
958                                  . ' ON ' . ((string) $join['joinCondition']);
959                 }
960             }
961
962             $fromClauses[$from['alias']] = $fromClause;
963         }
964
965         // loop through all JOIN clasues for validation purpose
966         foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
967             if ( ! isset($fromClauses[$fromAlias]) ) {
968                 throw QueryException::unknownFromAlias($fromAlias, array_keys($fromClauses));
969             }
970         }
971
972         $query .= implode(', ', $fromClauses)
973                 . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
974                 . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
975                 . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
976                 . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
977
978         return ($this->maxResults === null && $this->firstResult == null)
979             ? $query
980             : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult);
981     }
982
983     /**
984      * Converts this instance into an UPDATE string in SQL.
985      *
986      * @return string
987      */
988     private function getSQLForUpdate()
989     {
990         $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
991         $query = 'UPDATE ' . $table
992                . ' SET ' . implode(", ", $this->sqlParts['set'])
993                . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
994
995         return $query;
996     }
997
998     /**
999      * Converts this instance into a DELETE string in SQL.
1000      *
1001      * @return string
1002      */
1003     private function getSQLForDelete()
1004     {
1005         $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1006         $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1007
1008         return $query;
1009     }
1010
1011     /**
1012      * Gets a string representation of this QueryBuilder which corresponds to
1013      * the final SQL query being constructed.
1014      *
1015      * @return string The string representation of this QueryBuilder.
1016      */
1017     public function __toString()
1018     {
1019         return $this->getSQL();
1020     }
1021
1022     /**
1023      * Create a new named parameter and bind the value $value to it.
1024      *
1025      * This method provides a shortcut for PDOStatement::bindValue
1026      * when using prepared statements.
1027      *
1028      * The parameter $value specifies the value that you want to bind. If
1029      * $placeholder is not provided bindValue() will automatically create a
1030      * placeholder for you. An automatic placeholder will be of the name
1031      * ':dcValue1', ':dcValue2' etc.
1032      *
1033      * For more information see {@link http://php.net/pdostatement-bindparam}
1034      *
1035      * Example:
1036      * <code>
1037      * $value = 2;
1038      * $q->eq( 'id', $q->bindValue( $value ) );
1039      * $stmt = $q->executeQuery(); // executed with 'id = 2'
1040      * </code>
1041      *
1042      * @license New BSD License
1043      * @link http://www.zetacomponents.org
1044      * @param mixed $value
1045      * @param mixed $type
1046      * @param string $placeHolder the name to bind with. The string must start with a colon ':'.
1047      * @return string the placeholder name used.
1048      */
1049     public function createNamedParameter( $value, $type = \PDO::PARAM_STR, $placeHolder = null )
1050     {
1051         if ( $placeHolder === null ) {
1052             $this->boundCounter++;
1053             $placeHolder = ":dcValue" . $this->boundCounter;
1054         }
1055         $this->setParameter(substr($placeHolder, 1), $value, $type);
1056
1057         return $placeHolder;
1058     }
1059
1060     /**
1061      * Create a new positional parameter and bind the given value to it.
1062      *
1063      * Attention: If you are using positional parameters with the query builder you have
1064      * to be very careful to bind all parameters in the order they appear in the SQL
1065      * statement , otherwise they get bound in the wrong order which can lead to serious
1066      * bugs in your code.
1067      *
1068      * Example:
1069      * <code>
1070      *  $qb = $conn->createQueryBuilder();
1071      *  $qb->select('u.*')
1072      *     ->from('users', 'u')
1073      *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR))
1074      *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR))
1075      * </code>
1076      *
1077      * @param  mixed $value
1078      * @param  mixed $type
1079      * @return string
1080      */
1081     public function createPositionalParameter($value, $type = \PDO::PARAM_STR)
1082     {
1083         $this->boundCounter++;
1084         $this->setParameter($this->boundCounter, $value, $type);
1085         return "?";
1086     }
1087 }