source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/DBAL/Query/QueryBuilder.php @ 356

Last change on this file since 356 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 32.9 KB
RevLine 
[345]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
20namespace Doctrine\DBAL\Query;
21
22use 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 */
41class 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[$from['alias']] = $fromClause;
967        }
968
969        // loop through all JOIN clasues for validation purpose
970        foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
971            if ( ! isset($fromClauses[$fromAlias]) ) {
972                throw QueryException::unknownFromAlias($fromAlias, array_keys($fromClauses));
973            }
974        }
975
976        $query .= implode(', ', $fromClauses)
977                . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
978                . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
979                . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
980                . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
981
982        return ($this->maxResults === null && $this->firstResult == null)
983            ? $query
984            : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult);
985    }
986
987    /**
988     * Converts this instance into an UPDATE string in SQL.
989     *
990     * @return string
991     */
992    private function getSQLForUpdate()
993    {
994        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
995        $query = 'UPDATE ' . $table
996               . ' SET ' . implode(", ", $this->sqlParts['set'])
997               . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
998
999        return $query;
1000    }
1001
1002    /**
1003     * Converts this instance into a DELETE string in SQL.
1004     *
1005     * @return string
1006     */
1007    private function getSQLForDelete()
1008    {
1009        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1010        $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1011
1012        return $query;
1013    }
1014
1015    /**
1016     * Gets a string representation of this QueryBuilder which corresponds to
1017     * the final SQL query being constructed.
1018     *
1019     * @return string The string representation of this QueryBuilder.
1020     */
1021    public function __toString()
1022    {
1023        return $this->getSQL();
1024    }
1025
1026    /**
1027     * Create a new named parameter and bind the value $value to it.
1028     *
1029     * This method provides a shortcut for PDOStatement::bindValue
1030     * when using prepared statements.
1031     *
1032     * The parameter $value specifies the value that you want to bind. If
1033     * $placeholder is not provided bindValue() will automatically create a
1034     * placeholder for you. An automatic placeholder will be of the name
1035     * ':dcValue1', ':dcValue2' etc.
1036     *
1037     * For more information see {@link http://php.net/pdostatement-bindparam}
1038     *
1039     * Example:
1040     * <code>
1041     * $value = 2;
1042     * $q->eq( 'id', $q->bindValue( $value ) );
1043     * $stmt = $q->executeQuery(); // executed with 'id = 2'
1044     * </code>
1045     *
1046     * @license New BSD License
1047     * @link http://www.zetacomponents.org
1048     * @param mixed $value
1049     * @param mixed $type
1050     * @param string $placeHolder the name to bind with. The string must start with a colon ':'.
1051     * @return string the placeholder name used.
1052     */
1053    public function createNamedParameter( $value, $type = \PDO::PARAM_STR, $placeHolder = null )
1054    {
1055        if ( $placeHolder === null ) {
1056            $this->boundCounter++;
1057            $placeHolder = ":dcValue" . $this->boundCounter;
1058        }
1059        $this->setParameter(substr($placeHolder, 1), $value, $type);
1060
1061        return $placeHolder;
1062    }
1063
1064    /**
1065     * Create a new positional parameter and bind the given value to it.
1066     *
1067     * Attention: If you are using positional parameters with the query builder you have
1068     * to be very careful to bind all parameters in the order they appear in the SQL
1069     * statement , otherwise they get bound in the wrong order which can lead to serious
1070     * bugs in your code.
1071     *
1072     * Example:
1073     * <code>
1074     *  $qb = $conn->createQueryBuilder();
1075     *  $qb->select('u.*')
1076     *     ->from('users', 'u')
1077     *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR))
1078     *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR))
1079     * </code>
1080     *
1081     * @param  mixed $value
1082     * @param  mixed $type
1083     * @return string
1084     */
1085    public function createPositionalParameter($value, $type = \PDO::PARAM_STR)
1086    {
1087        $this->boundCounter++;
1088        $this->setParameter($this->boundCounter, $value, $type);
1089        return "?";
1090    }
1091}
Note: See TracBrowser for help on using the repository browser.