Abstract.php 29 KB
Newer Older
romanb's avatar
romanb committed
1
<?php
2

romanb's avatar
romanb committed
3
/*
4
 *  $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $
romanb's avatar
romanb committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
20
 * <http://www.phpdoctrine.org>.
romanb's avatar
romanb committed
21
 */
22

romanb's avatar
romanb committed
23 24 25 26
/**
 * Doctrine_Query_Abstract
 *
 * @package     Doctrine
27
 * @subpackage  Query
romanb's avatar
romanb committed
28
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
29
 * @link        www.phpdoctrine.com
romanb's avatar
romanb committed
30 31
 * @since       1.0
 * @version     $Revision: 1393 $
32
 * @author      Guilherme Blanco <guilhermeblanco@hotmail.com>
romanb's avatar
romanb committed
33
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
34
 * @todo        See {@link Doctrine_Query}
romanb's avatar
romanb committed
35
 */
36
abstract class Doctrine_Query_Abstract
romanb's avatar
romanb committed
37
{
38 39 40 41 42
    /**
     * QUERY TYPE CONSTANTS
     */

    /**
43
     * Constant for SELECT queries.
44 45 46 47
     */
    const SELECT = 0;

    /**
48
     * Constant for DELETE queries.
49 50 51 52
     */
    const DELETE = 1;

    /**
53
     * Constant for UPDATE queries.
54 55 56 57
     */
    const UPDATE = 2;

    /**
58 59 60 61 62
     * @todo [TODO] Remove these ones (INSERT and CREATE)?
     */

    /**
     * Constant for INSERT queries.
63
     */
64
    //const INSERT = 3;
65 66

    /**
67
     * Constant for CREATE queries.
68
     */
69 70 71
    //const CREATE = 4;


72 73 74
    /**
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
     */
75
    const STATE_CLEAN  = 1;
76

77
    /**
78 79 80
     * A query object is in state DIRTY when it has DQL parts that have not yet been
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
     * is called.
81
     */
82
    const STATE_DIRTY  = 2;
83

84
    /**
85
     * @todo [TODO] Remove these ones (DIRECT and LOCKED)?
86
     */
87

88
    /**
89
     * A query is in DIRECT state when ... ?
90
     */
91 92
    //const STATE_DIRECT = 3;

93
    /**
94
     * A query object is on LOCKED state when ... ?
95
     */
96 97 98
    //const STATE_LOCKED = 4;


99
    /**
100 101 102
     * @var integer $type Query type.
     *
     * @see Doctrine_Query::* constants
103
     */
104 105
    protected $_type = self::SELECT;

106 107 108
    /**
     * @var integer $_state   The current state of this query.
     */
109
    protected $_state = self::STATE_CLEAN;
110 111

    /**
112 113
     * @var array $params Parameters of this query.
     * @see Doctrine_Query::free that initializes this property
114
     */
115 116
    protected $_params = array();

117
    /**
118 119
     * @var array $_enumParams Array containing the keys of the parameters that should be enumerated.
     * @see Doctrine_Query::free that initializes this property
120
     */
121 122 123 124 125
    protected $_enumParams = array();

    /**
     * @var array $_dqlParts An array containing all DQL query parts.
     * @see Doctrine_Query::free that initializes this property
126
     */
127 128
    protected $_dqlParts = array();

129
    /**
130
     * @var string $_dql Cached DQL query.
131
     */
132 133 134
    protected $_dql = null;


135
    /**
136 137 138 139 140
     * Frees the resources used by the query object. It especially breaks a
     * cyclic reference between the query object and it's parsers. This enables
     * PHP's current GC to reclaim the memory.
     * This method can therefore be used to reduce memory usage when creating a lot
     * of query objects during a request.
141
     */
142 143 144 145 146 147
    public function free()
    {
        /**
         * @todo [TODO] What about "forUpdate" support? Remove it?
         */
        $this->_dqlParts = array(
148 149 150 151 152
            'select'    => array(),
            'distinct'  => false,
            'forUpdate' => false,
            'from'      => array(),
            'join'      => array(),
153 154 155 156 157 158 159
            'set'       => array(),
            'where'     => array(),
            'groupby'   => array(),
            'having'    => array(),
            'orderby'   => array(),
            'limit'     => array(),
            'offset'    => array(),
160
        );
161

162 163 164 165 166 167
        $this->_params = array(
            'join' => array(),
            'set' => array(),
            'where' => array(),
            'having' => array()
        );
168

169
        $this->_enumParams = array();
170

171 172
        $this->_dql = null;
        $this->_state = self::STATE_CLEAN;
173
    }
174 175


176
    /**
177
     * Defines a complete DQL
178
     *
179
     * @param string $dqlQuery DQL Query
180
     */
181
    public function setDql($dqlQuery)
182
    {
183
        $this->free();
184

185 186
        if ($dqlQuery !== null) {
            $this->_dql = $dqlQuery;
187

188
            $this->_state = self::STATE_DIRTY;
189
        }
190
    }
191 192


193
    /**
194
     * Returns the DQL query that is represented by this query object.
195
     *
196
     * @return string DQL query
197
     */
198
    public function getDql()
199
    {
200 201
        if ($this->_dql !== null) {
            return $this->_dql;
202 203
        }

204
        $dql = '';
205

206 207 208 209
        switch ($this->_type) {
            case self::DELETE:
                $dql = $this->_getDqlForDelete();
            break;
210

211 212 213
            case self::UPDATE:
                $dql = $this->_getDqlForUpdate();
            break;
214

215 216 217 218 219 220
            /**
             * @todo [TODO] Remove these ones (INSERT and CREATE)?
             */
            /*
            case self::INSERT:
            break;
221

222 223 224
            case self::CREATE:
            break;
            */
225

226 227 228 229 230 231 232
            case self::SELECT:
            default:
                $dql = $this->_getDqlForSelect();
            break;
        }

        return $dql;
233
    }
234 235 236 237


    /**
     * Builds the DQL of DELETE
238
     */
239
    protected function _getDqlForDelete()
240
    {
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
        /*
         * BNF:
         *
         * DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
         * DeleteClause    = "DELETE" "FROM" RangeVariableDeclaration
         * WhereClause     = "WHERE" ConditionalExpression
         * OrderByClause   = "ORDER" "BY" OrderByItem {"," OrderByItem}
         * LimitClause     = "LIMIT" integer
         * OffsetClause    = "OFFSET" integer
         *
         */
         return 'DELETE'
              . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', '))
              . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
258
    }
259

260

romanb's avatar
romanb committed
261
    /**
262
     * Builds the DQL of UPDATE
romanb's avatar
romanb committed
263
     */
264
    protected function _getDqlForUpdate()
romanb's avatar
romanb committed
265
    {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
        /*
         * BNF:
         *
         * UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
         * UpdateClause    = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem}
         * WhereClause     = "WHERE" ConditionalExpression
         * OrderByClause   = "ORDER" "BY" OrderByItem {"," OrderByItem}
         * LimitClause     = "LIMIT" integer
         * OffsetClause    = "OFFSET" integer
         *
         */
         return 'UPDATE'
              . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('where', array('pre' => ' SET ', 'separator' => ', '))
              . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', '))
              . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
romanb's avatar
romanb committed
284
    }
285

286

romanb's avatar
romanb committed
287
    /**
288
     * Builds the DQL of SELECT
romanb's avatar
romanb committed
289
     */
290
    protected function _getDqlForSelect()
romanb's avatar
romanb committed
291
    {
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
        /*
         * BNF:
         *
         * SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause]
         * SelectClause    = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}
         * FromClause      = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}
         * WhereClause     = "WHERE" ConditionalExpression
         * GroupByClause   = "GROUP" "BY" GroupByItem {"," GroupByItem}
         * HavingClause    = "HAVING" ConditionalExpression
         * OrderByClause   = "ORDER" "BY" OrderByItem {"," OrderByItem}
         * LimitClause     = "LIMIT" integer
         * OffsetClause    = "OFFSET" integer
         *
         */
         /**
          * @todo [TODO] What about "ALL" support?
          */
         return 'SELECT'
              . (($this->getDqlQueryPart('distinct') === true) ? ' DISTINCT' : '')
              . $this->_getReducedDqlQueryPart('select', array('pre' => ' ', 'separator' => ', ', 'empty' => ' *'))
              . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('groupby', array('pre' => ' GROUP BY ', 'separator' => ', '))
              . $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', '))
              . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
              . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
romanb's avatar
romanb committed
319
    }
320

321

zYne's avatar
zYne committed
322
    /**
323
     * @nodoc
zYne's avatar
zYne committed
324
     */
325
    protected function _getReducedDqlQueryPart($queryPartName, $options = array())
zYne's avatar
zYne committed
326
    {
327 328
        if (empty($this->_dqlParts[$queryPartName])) {
            return (isset($options['empty']) ? $options['empty'] : '');
zYne's avatar
zYne committed
329
        }
zYne's avatar
zYne committed
330

331 332 333
        $str  = (isset($options['pre']) ? $options['pre'] : '');
        $str .= implode($options['separator'], $this->getDqlQueryPart($queryPartName));
        $str .= (isset($options['post']) ? $options['post'] : '');
zYne's avatar
zYne committed
334

335
        return $str;
zYne's avatar
zYne committed
336
    }
337

meus's avatar
meus committed
338
    /**
339 340 341 342
     * Returns the type of this query object
     * By default the type is Doctrine_Query_Abstract::SELECT but if update() or delete()
     * are being called the type is Doctrine_Query_Abstract::UPDATE and Doctrine_Query_Abstract::DELETE,
     * respectively.
meus's avatar
meus committed
343
     *
344 345 346
     * @see Doctrine_Query_Abstract::SELECT
     * @see Doctrine_Query_Abstract::UPDATE
     * @see Doctrine_Query_Abstract::DELETE
romanb's avatar
romanb committed
347
     *
348
     * @return integer Return the query type
romanb's avatar
romanb committed
349
     */
350
    public function getType()
romanb's avatar
romanb committed
351
    {
352
        return $this->_type;
romanb's avatar
romanb committed
353
    }
354 355


romanb's avatar
romanb committed
356
    /**
357 358 359
     * Returns the state of this query object
     * By default the type is Doctrine_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
     * part, it is switched to Doctrine_Query_Abstract::STATE_DIRTY.
romanb's avatar
romanb committed
360
     *
361 362 363 364
     * @see Doctrine_Query_Abstract::STATE_CLEAN
     * @see Doctrine_Query_Abstract::STATE_DIRTY
     *
     * @return integer Return the query state
romanb's avatar
romanb committed
365
     */
366
    public function getState()
romanb's avatar
romanb committed
367
    {
368
        return $this->_state;
romanb's avatar
romanb committed
369
    }
370

371

romanb's avatar
romanb committed
372
    /**
373
     * Adds fields to the SELECT part of the query
romanb's avatar
romanb committed
374
     *
375
     * @param string $select Query SELECT part
romanb's avatar
romanb committed
376 377
     * @return Doctrine_Query
     */
378
    public function select($select = '', $override = false)
romanb's avatar
romanb committed
379
    {
380 381 382 383 384
        if ($select === '') {
            return $this;
        }

        return $this->_addDqlQueryPart('select', $select, ! $override);
romanb's avatar
romanb committed
385
    }
386

387

romanb's avatar
romanb committed
388 389 390
    /**
     * Makes the query SELECT DISTINCT.
     *
391
     * @param bool $flag Whether or not the SELECT is DISTINCT (default true).
romanb's avatar
romanb committed
392 393 394
     * @return Doctrine_Query
     */
    public function distinct($flag = true)
395 396
    {
        $this->_dqlParts['distinct'] = (bool) $flag;
romanb's avatar
romanb committed
397 398 399
        return $this;
    }

400

romanb's avatar
romanb committed
401 402 403
    /**
     * Makes the query SELECT FOR UPDATE.
     *
404
     * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true).
romanb's avatar
romanb committed
405
     * @return Doctrine_Query
406 407
     *
     * @todo [TODO] What about "forUpdate" support? Remove it?
romanb's avatar
romanb committed
408 409 410
     */
    public function forUpdate($flag = true)
    {
411
        return $this->_addDqlQueryPart('forUpdate', (bool) $flag);
romanb's avatar
romanb committed
412
    }
413

414

romanb's avatar
romanb committed
415
    /**
416
     * Sets the query type to DELETE
romanb's avatar
romanb committed
417 418 419 420 421
     *
     * @return Doctrine_Query
     */
    public function delete()
    {
422
        $this->_type = self::DELETE;
romanb's avatar
romanb committed
423 424
        return $this;
    }
425

426

romanb's avatar
romanb committed
427
    /**
428
     * Sets the UPDATE part of the query
romanb's avatar
romanb committed
429
     *
430
     * @param string $update Query UPDATE part
romanb's avatar
romanb committed
431 432 433 434
     * @return Doctrine_Query
     */
    public function update($update)
    {
435 436
        $this->_type = self::UPDATE;
        return $this->_addDqlQueryPart('from', $update);
romanb's avatar
romanb committed
437
    }
438

439

romanb's avatar
romanb committed
440
    /**
441
     * Sets the SET part of the query
romanb's avatar
romanb committed
442
     *
443 444 445
     * @param mixed $key UPDATE keys. Accepts either a string (requiring then $value or $params to be defined)
     *                   or an array of $key => $value pairs.
     * @param string $value UPDATE key value. Optional argument, but required if $key is a string.
romanb's avatar
romanb committed
446 447
     * @return Doctrine_Query
     */
448
    public function set($key, $value = null, $params = null)
romanb's avatar
romanb committed
449
    {
450
        if (is_array($key)) {
zYne's avatar
zYne committed
451
            foreach ($key as $k => $v) {
452
                $this->set($k, '?', array($v));
zYne's avatar
zYne committed
453
            }
454

455
            return $this;
456
        } else {
zYne's avatar
zYne committed
457 458
            if ($params !== null) {
                if (is_array($params)) {
459
                    $this->_params['set'] = array_merge($this->_params['set'], $params);
zYne's avatar
zYne committed
460
                } else {
461
                    $this->_params['set'][] = $params;
zYne's avatar
zYne committed
462
                }
romanb's avatar
romanb committed
463
            }
464 465 466 467 468

            if ($value === null) {
                throw new Doctrine_Query_Exception( 'Cannot try to set \''.$key.'\' without a value.' );
            }

469
            return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
romanb's avatar
romanb committed
470 471
        }
    }
472

romanb's avatar
romanb committed
473
    /**
474
     * Adds fields to the FROM part of the query
romanb's avatar
romanb committed
475
     *
476
     * @param string $from Query FROM part
romanb's avatar
romanb committed
477 478
     * @return Doctrine_Query
     */
479
    public function from($from, $override = false)
romanb's avatar
romanb committed
480
    {
481
        return $this->_addDqlQueryPart('from', $from, ! $override);
romanb's avatar
romanb committed
482
    }
483

484

romanb's avatar
romanb committed
485
    /**
486
     * Appends an INNER JOIN to the FROM part of the query
romanb's avatar
romanb committed
487
     *
488 489
     * @param string $join Query INNER JOIN
     * @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
romanb's avatar
romanb committed
490 491
     * @return Doctrine_Query
     */
492
    public function innerJoin($join, $params = array())
romanb's avatar
romanb committed
493
    {
494 495 496 497 498 499
        if (is_array($params)) {
            $this->_params['join'] = array_merge($this->_params['join'], $params);
        } else {
            $this->_params['join'][] = $params;
        }

500
        return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
romanb's avatar
romanb committed
501
    }
502

503 504 505 506 507 508 509 510 511 512 513 514 515 516

    /**
     * Appends an INNER JOIN to the FROM part of the query
     *
     * @param string $join Query INNER JOIN
     * @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
     * @return Doctrine_Query
     */
    public function join($join, $params = array())
    {
        return $this->innerJoin($join, $params);
    }


romanb's avatar
romanb committed
517
    /**
518
     * Appends a LEFT JOIN to the FROM part of the query
romanb's avatar
romanb committed
519
     *
520 521
     * @param string $join Query LEFT JOIN
     * @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
romanb's avatar
romanb committed
522 523
     * @return Doctrine_Query
     */
524
    public function leftJoin($join, $params = array())
romanb's avatar
romanb committed
525
    {
526 527 528 529 530 531
        if (is_array($params)) {
            $this->_params['join'] = array_merge($this->_params['join'], $params);
        } else {
            $this->_params['join'][] = $params;
        }

532
        return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
romanb's avatar
romanb committed
533
    }
534 535


romanb's avatar
romanb committed
536
    /**
537
     * Adds conditions to the WHERE part of the query
romanb's avatar
romanb committed
538
     *
539 540
     * @param string $where Query WHERE part
     * @param mixed $params An array of parameters or a simple scalar
romanb's avatar
romanb committed
541 542
     * @return Doctrine_Query
     */
543
    public function where($where, $params = array(), $override = false)
romanb's avatar
romanb committed
544
    {
545 546 547 548
        if ($override) {
            $this->_params['where'] = array();
        }

romanb's avatar
romanb committed
549
        if (is_array($params)) {
550
            $this->_params['where'] = array_merge($this->_params['where'], $params);
romanb's avatar
romanb committed
551
        } else {
552
            $this->_params['where'][] = $params;
romanb's avatar
romanb committed
553 554
        }

555
        return $this->_addDqlQueryPart('where', $where, ! $override);
romanb's avatar
romanb committed
556
    }
557

558

romanb's avatar
romanb committed
559
    /**
560
     * Adds conditions to the WHERE part of the query
romanb's avatar
romanb committed
561
     *
562 563
     * @param string $where Query WHERE part
     * @param mixed $params An array of parameters or a simple scalar
romanb's avatar
romanb committed
564 565
     * @return Doctrine_Query
     */
566
    public function andWhere($where, $params = array(), $override = false)
romanb's avatar
romanb committed
567
    {
568 569
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'AND', true);
romanb's avatar
romanb committed
570
        }
571 572

        return $this->where($where, $params, $override);
romanb's avatar
romanb committed
573
    }
574

575

romanb's avatar
romanb committed
576
    /**
577
     * Adds conditions to the WHERE part of the query
romanb's avatar
romanb committed
578
     *
579 580
     * @param string $where Query WHERE part
     * @param mixed $params An array of parameters or a simple scalar
romanb's avatar
romanb committed
581 582
     * @return Doctrine_Query
     */
583
    public function orWhere($where, $params = array(), $override = false)
romanb's avatar
romanb committed
584
    {
585 586 587 588 589
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'OR', true);
        }

        return $this->where($where, $params, $override);
romanb's avatar
romanb committed
590
    }
591

592

romanb's avatar
romanb committed
593
    /**
594
     * Adds IN condition to the query WHERE part
romanb's avatar
romanb committed
595
     *
596 597 598
     * @param string $expr The operand of the IN
     * @param mixed $params An array of parameters or a simple scalar
     * @param boolean $not Whether or not to use NOT in front of IN
romanb's avatar
romanb committed
599 600
     * @return Doctrine_Query
     */
601
    public function whereIn($expr, $params = array(), $override = false, $not = false)
romanb's avatar
romanb committed
602
    {
603 604 605 606 607 608 609 610 611 612 613 614
        $params = (array) $params;

        // Must have at least one param, otherwise we'll get an empty IN () => invalid SQL
        if ( ! count($params)) {
            return $this;
        }

        list($sqlPart, $params) = $this->_processWhereInParams($params);

        $where = $expr . ($not === true ? ' NOT' : '') . ' IN (' . $sqlPart . ')';

        return $this->_returnWhereIn($where, $params, $override);
romanb's avatar
romanb committed
615
    }
616

617

romanb's avatar
romanb committed
618
    /**
619
     * Adds NOT IN condition to the query WHERE part
romanb's avatar
romanb committed
620
     *
621 622
     * @param string $expr The operand of the NOT IN
     * @param mixed $params An array of parameters or a simple scalar
romanb's avatar
romanb committed
623 624
     * @return Doctrine_Query
     */
625
    public function whereNotIn($expr, $params = array(), $override = false)
romanb's avatar
romanb committed
626
    {
627
        return $this->whereIn($expr, $params, $override, true);
romanb's avatar
romanb committed
628
    }
629 630


631
    /**
632
     * Adds IN condition to the query WHERE part
633
     *
634 635 636 637
     * @param string $expr The operand of the IN
     * @param mixed $params An array of parameters or a simple scalar
     * @param boolean $not Whether or not to use NOT in front of IN
     * @return Doctrine_Query
638
     */
639
    public function andWhereIn($expr, $params = array(), $override = false)
640
    {
641 642 643 644 645
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'AND', true);
        }

        return $this->whereIn($expr, $params, $override);
646
    }
647 648


649
    /**
650
     * Adds NOT IN condition to the query WHERE part
651
     *
652 653 654
     * @param string $expr The operand of the NOT IN
     * @param mixed $params An array of parameters or a simple scalar
     * @return Doctrine_Query
655
     */
656
    public function andWhereNotIn($expr, $params = array(), $override = false)
657
    {
658 659 660 661 662
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'AND', true);
        }

        return $this->whereIn($expr, $params, $override, true);
663
    }
664 665


666
    /**
667 668 669 670 671 672
     * Adds IN condition to the query WHERE part
     *
     * @param string $expr The operand of the IN
     * @param mixed $params An array of parameters or a simple scalar
     * @param boolean $not Whether or not to use NOT in front of IN
     * @return Doctrine_Query
673
     */
674
    public function orWhereIn($expr, $params = array(), $override = false)
675
    {
676 677 678 679 680
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'OR', true);
        }

        return $this->whereIn($expr, $params, $override);
681
    }
682 683


684
    /**
685
     * Adds NOT IN condition to the query WHERE part
686
     *
687 688 689
     * @param string $expr The operand of the NOT IN
     * @param mixed $params An array of parameters or a simple scalar
     * @return Doctrine_Query
690
     */
691
    public function orWhereNotIn($expr, $params = array(), $override = false)
692
    {
693 694 695 696 697
        if (count($this->getDqlQueryPart('where')) > 0) {
            $this->_addDqlQueryPart('where', 'OR', true);
        }

        return $this->whereIn($expr, $params, $override, true);
698
    }
699 700


701
    /**
702
     * Adds fields to the GROUP BY part of the query
703
     *
704 705
     * @param string $groupby Query GROUP BY part
     * @return Doctrine_Query
706
     */
707
    public function groupBy($groupby, $override = false)
708
    {
709
        return $this->_addDqlQueryPart('groupby', $groupby, ! $override);
710
    }
pookey's avatar
pookey committed
711

712

713
    /**
714
     * Adds conditions to the HAVING part of the query
715
     *
716 717 718
     * @param string $having Query HAVING part
     * @param mixed $params An array of parameters or a simple scalar
     * @return Doctrine_Query
719
     */
720
    public function having($having, $params = array(), $override = false)
721
    {
722 723 724 725 726 727 728 729 730 731 732
        if ($override) {
            $this->_params['having'] = array();
        }

        if (is_array($params)) {
            $this->_params['having'] = array_merge($this->_params['having'], $params);
        } else {
            $this->_params['having'][] = $params;
        }

        return $this->_addDqlQueryPart('having', $having, true);
733
    }
734 735


736
    /**
737
     * Adds conditions to the HAVING part of the query
738
     *
739 740 741
     * @param string $having Query HAVING part
     * @param mixed $params An array of parameters or a simple scalar
     * @return Doctrine_Query
742
     */
743
    public function andHaving($having, $params = array(), $override = false)
744
    {
745 746 747 748 749
        if (count($this->getDqlQueryPart('having')) > 0) {
            $this->_addDqlQueryPart('having', 'AND', true);
        }

        return $this->having($having, $params, $override);
750
    }
751 752


753
    /**
754
     * Adds conditions to the HAVING part of the query
755
     *
756 757 758
     * @param string $having Query HAVING part
     * @param mixed $params An array of parameters or a simple scalar
     * @return Doctrine_Query
759
     */
760
    public function orHaving($having, $params = array(), $override = false)
761
    {
762 763
        if (count($this->getDqlQueryPart('having')) > 0) {
            $this->_addDqlQueryPart('having', 'OR', true);
764 765
        }

766
        return $this->having($having, $params, $override);
767
    }
768 769


770
    /**
771
     * Adds fields to the ORDER BY part of the query
772
     *
773 774
     * @param string $orderby Query ORDER BY part
     * @return Doctrine_Query
775
     */
776
    public function orderBy($orderby, $override = false)
777
    {
778
        return $this->_addDqlQueryPart('orderby', $orderby, ! $override);
779
    }
780 781


782
    /**
783
     * Sets the Query query limit
784
     *
785 786
     * @param integer $limit Limit to be used for limiting the query results
     * @return Doctrine_Query
787
     */
788
    public function limit($limit)
789
    {
790
        return $this->_addDqlQueryPart('limit', $limit);
791
    }
792 793


794
    /**
795
     * Sets the Query query offset
796
     *
797 798
     * @param integer $offset Offset to be used for paginating the query
     * @return Doctrine_Query
799
     */
800
    public function offset($offset)
801
    {
802
        return $this->_addDqlQueryPart('offset', $offset);
803
    }
804 805


806
    /**
807
     * Set enumerated parameters
808
     *
809
     * @param array $enumParams Enum parameters.
810
     */
811
    protected function _setEnumParams($enumParams = array())
812
    {
813
        $this->_enumParams = $enumParams;
814
    }
815 816


817
    /**
818
     * Get all enumerated parameters
819
     *
820
     * @return array All enumerated parameters
821
     */
822
    public function getEnumParams()
823
    {
824
        return $this->_enumParams;
825
    }
826 827


828
    /**
829
     * Convert ENUM parameters to their integer equivalents
830
     *
831 832
     * @param $params Parameters to be converted
     * @return array Converted parameters array
833
     */
834
    public function convertEnums($params)
835
    {
836 837 838 839
        foreach ($this->_enumParams as $key => $values) {
            if (isset($params[$key]) && ! empty($values)) {
                $params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
            }
840
        }
841

842
        return $params;
843
    }
844 845


846
    /**
847
     * Get all defined parameters
848
     *
849
     * @return array Defined parameters
850
     */
851
    public function getParams($params = array())
852
    {
853 854 855 856 857 858 859
        return array_merge(
            $this->_params['join'],
            $this->_params['set'],
            $this->_params['where'],
            $this->_params['having'],
            $params
        );
860
    }
861 862


863
    /**
864
     * setParams
865
     *
866
     * @param array $params
867
     */
868 869
    public function setParams(array $params = array()) {
        $this->_params = $params;
870
    }
871 872


873
    /**
874
     * Method to check if a arbitrary piece of DQL exists
875
     *
876 877
     * @param string $dql Arbitrary piece of DQL to check for
     * @return boolean
878
     */
879
    public function contains($dql)
880
    {
881
      return stripos($this->getDql(), $dql) === false ? false : true;
882
    }
883 884


885
    /**
886
     * Retrieve a DQL part for internal purposes
887
     *
888 889
     * @param string $queryPartName  The name of the query part.
     * @return mixed Array related to query part or simple scalar
890
     */
891
    public function getDqlQueryPart($queryPartName)
892
    {
893 894
        if ( ! isset($this->_dqlParts[$queryPartName])) {
            throw new Doctrine_Query_Exception('Unknown DQL query part \'' . $queryPartName . '\'');
895
        }
896 897

        return $this->_dqlParts[$queryPartName];
898
    }
899 900


901
    /**
902
     * Adds a DQL part to the internal parts collection.
903
     *
904 905 906 907 908
     * @param string $queryPartName  The name of the query part.
     * @param string $queryPart      The actual query part to add.
     * @param boolean $append        Whether to append $queryPart to already existing
     *                               parts under the same $queryPartName. Defaults to FALSE
     *                               (previously added parts with the same name get overridden).
909
     * @return Doctrine_Query
910 911 912 913 914 915 916 917
     */
    protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
    {
        if ($append) {
            $this->_dqlParts[$queryPartName][] = $queryPart;
        } else {
            $this->_dqlParts[$queryPartName] = array($queryPart);
        }
918

919
        $this->_state = Doctrine_Query::STATE_DIRTY;
920
        return $this;
921
    }
922 923


romanb's avatar
romanb committed
924
    /**
925 926 927
     * Processes the WHERE IN () parameters and return an indexed array containing
     * the sqlPart to be placed in SQL statement and the new parameters (that will be
     * bound in SQL execution)
romanb's avatar
romanb committed
928
     *
929 930
     * @param array $params Parameters to be processed
     * @return array
romanb's avatar
romanb committed
931
     */
932
    protected function _processWhereInParams($params = array())
933
    {
934 935 936 937 938 939
        return array(
            // [0] => sqlPart
            implode(', ', array_map(array(&$this, '_processWhereInSqlPart'), $params)),
            // [1] => params
            array_filter($params, array(&$this, '_processWhereInParamItem')),
        );
940
    }
941 942


943
    /**
944
     * @nodoc
945
     */
946
    protected function _processWhereInSqlPart($value)
947
    {
948 949 950
        // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser)
        return  ($value instanceof Doctrine_Expression) ? $value->getSql() : '?';
    }
951 952


953
    /**
954
     * @nodoc
955
     */
956 957 958 959 960 961 962
    protected function _processWhereInParamItem($value)
    {
        // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser)
        return ( ! ($value instanceof Doctrine_Expression));
    }


963
    /**
964
     * Processes a WHERE IN () and build defined stuff to add in DQL
965
     *
966 967 968 969 970
     * @param string $where The WHERE clause to be added
     * @param array $params WHERE clause parameters
     * @param mixed $appender Where this clause may be not be appended, or appended 
     *                        (two possible values: AND or OR)
     * @return Doctrine_Query
971
     */
972
    protected function _returnWhereIn($where, $params = array(), $override = false)
973
    {
974 975 976 977 978
        // Parameters inclusion
        $this->_params['where'] = $override ? $params : array_merge($this->_params['where'], $params);

        // WHERE clause definition
        return $this->_addDqlQueryPart('where', $where, ! $override);
979
    }
980 981


982
    /**
983 984 985 986 987
     * Gets the SQL query that corresponds to this query object.
     * The returned SQL syntax depends on the connection driver that is used
     * by this query object at the time of this method call.
     *
     * @return string SQL query
988
     */
989
    abstract public function getSql();
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
    
    /**
     * Sets a query parameter.
     *
     * @param string|integer $key
     * @param mixed $value
     */
    public function setParameter($key, $value)
    {
        $this->_params[$key] = $value;
    }
    
    /**
     * Sets a collection of query parameters.
     *
     * @param array $params
     */
    public function setParameters(array $params)
    {
        foreach ($params as $key => $value) {
            $this->setParameter($key, $value);
        }
    }
1013

zYne's avatar
zYne committed
1014
}