CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.7 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.7
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • BetweenExpression
  • CaseExpression
  • Comparison
  • FunctionExpression
  • IdentifierExpression
  • OrderByExpression
  • OrderClauseExpression
  • QueryExpression
  • TupleComparison
  • UnaryExpression
  • ValuesExpression

Interfaces

  • FieldInterface

Traits

  • FieldTrait
  1: <?php
  2: /**
  3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5:  *
  6:  * Licensed under The MIT License
  7:  * For full copyright and license information, please see the LICENSE.txt
  8:  * Redistributions of files must retain the above copyright notice.
  9:  *
 10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 11:  * @link          https://cakephp.org CakePHP(tm) Project
 12:  * @since         3.0.0
 13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
 14:  */
 15: namespace Cake\Database\Expression;
 16: 
 17: use BadMethodCallException;
 18: use Cake\Database\ExpressionInterface;
 19: use Cake\Database\Query;
 20: use Cake\Database\TypeMapTrait;
 21: use Cake\Database\ValueBinder;
 22: use Countable;
 23: 
 24: /**
 25:  * Represents a SQL Query expression. Internally it stores a tree of
 26:  * expressions that can be compiled by converting this object to string
 27:  * and will contain a correctly parenthesized and nested expression.
 28:  *
 29:  * @method $this and(callable|string|array|\Cake\Database\ExpressionInterface $conditions)
 30:  * @method $this or(callable|string|array|\Cake\Database\ExpressionInterface $conditions)
 31:  */
 32: class QueryExpression implements ExpressionInterface, Countable
 33: {
 34: 
 35:     use TypeMapTrait;
 36: 
 37:     /**
 38:      * String to be used for joining each of the internal expressions
 39:      * this object internally stores for example "AND", "OR", etc.
 40:      *
 41:      * @var string
 42:      */
 43:     protected $_conjunction;
 44: 
 45:     /**
 46:      * A list of strings or other expression objects that represent the "branches" of
 47:      * the expression tree. For example one key of the array might look like "sum > :value"
 48:      *
 49:      * @var array
 50:      */
 51:     protected $_conditions = [];
 52: 
 53:     /**
 54:      * Constructor. A new expression object can be created without any params and
 55:      * be built dynamically. Otherwise it is possible to pass an array of conditions
 56:      * containing either a tree-like array structure to be parsed and/or other
 57:      * expression objects. Optionally, you can set the conjunction keyword to be used
 58:      * for joining each part of this level of the expression tree.
 59:      *
 60:      * @param string|array|\Cake\Database\ExpressionInterface $conditions tree-like array structure containing all the conditions
 61:      * to be added or nested inside this expression object.
 62:      * @param array|\Cake\Database\TypeMap $types associative array of types to be associated with the values
 63:      * passed in $conditions.
 64:      * @param string $conjunction the glue that will join all the string conditions at this
 65:      * level of the expression tree. For example "AND", "OR", "XOR"...
 66:      * @see \Cake\Database\Expression\QueryExpression::add() for more details on $conditions and $types
 67:      */
 68:     public function __construct($conditions = [], $types = [], $conjunction = 'AND')
 69:     {
 70:         $this->setTypeMap($types);
 71:         $this->setConjunction(strtoupper($conjunction));
 72:         if (!empty($conditions)) {
 73:             $this->add($conditions, $this->getTypeMap()->getTypes());
 74:         }
 75:     }
 76: 
 77:     /**
 78:      * Changes the conjunction for the conditions at this level of the expression tree.
 79:      *
 80:      * @param string $conjunction Value to be used for joining conditions
 81:      * @return $this
 82:      */
 83:     public function setConjunction($conjunction)
 84:     {
 85:         $this->_conjunction = strtoupper($conjunction);
 86: 
 87:         return $this;
 88:     }
 89: 
 90:     /**
 91:      * Gets the currently configured conjunction for the conditions at this level of the expression tree.
 92:      *
 93:      * @return string
 94:      */
 95:     public function getConjunction()
 96:     {
 97:         return $this->_conjunction;
 98:     }
 99: 
100:     /**
101:      * Changes the conjunction for the conditions at this level of the expression tree.
102:      * If called with no arguments it will return the currently configured value.
103:      *
104:      * @deprecated 3.4.0 Use setConjunction()/getConjunction() instead.
105:      * @param string|null $conjunction value to be used for joining conditions. If null it
106:      * will not set any value, but return the currently stored one
107:      * @return string|$this
108:      */
109:     public function tieWith($conjunction = null)
110:     {
111:         deprecationWarning(
112:             'QueryExpression::tieWith() is deprecated. ' .
113:             'Use QueryExpression::setConjunction()/getConjunction() instead.'
114:         );
115:         if ($conjunction !== null) {
116:             return $this->setConjunction($conjunction);
117:         }
118: 
119:         return $this->getConjunction();
120:     }
121: 
122:     /**
123:      * Backwards compatible wrapper for tieWith()
124:      *
125:      * @param string|null $conjunction value to be used for joining conditions. If null it
126:      * will not set any value, but return the currently stored one
127:      * @return string|$this
128:      * @deprecated 3.2.0 Use setConjunction()/getConjunction() instead
129:      */
130:     public function type($conjunction = null)
131:     {
132:         deprecationWarning(
133:             'QueryExpression::type() is deprecated. ' .
134:             'Use QueryExpression::setConjunction()/getConjunction() instead.'
135:         );
136: 
137:         return $this->tieWith($conjunction);
138:     }
139: 
140:     /**
141:      * Adds one or more conditions to this expression object. Conditions can be
142:      * expressed in a one dimensional array, that will cause all conditions to
143:      * be added directly at this level of the tree or they can be nested arbitrarily
144:      * making it create more expression objects that will be nested inside and
145:      * configured to use the specified conjunction.
146:      *
147:      * If the type passed for any of the fields is expressed "type[]" (note braces)
148:      * then it will cause the placeholder to be re-written dynamically so if the
149:      * value is an array, it will create as many placeholders as values are in it.
150:      *
151:      * @param string|array|\Cake\Database\ExpressionInterface $conditions single or multiple conditions to
152:      * be added. When using an array and the key is 'OR' or 'AND' a new expression
153:      * object will be created with that conjunction and internal array value passed
154:      * as conditions.
155:      * @param array $types associative array of fields pointing to the type of the
156:      * values that are being passed. Used for correctly binding values to statements.
157:      * @see \Cake\Database\Query::where() for examples on conditions
158:      * @return $this
159:      */
160:     public function add($conditions, $types = [])
161:     {
162:         if (is_string($conditions)) {
163:             $this->_conditions[] = $conditions;
164: 
165:             return $this;
166:         }
167: 
168:         if ($conditions instanceof ExpressionInterface) {
169:             $this->_conditions[] = $conditions;
170: 
171:             return $this;
172:         }
173: 
174:         $this->_addConditions($conditions, $types);
175: 
176:         return $this;
177:     }
178: 
179:     /**
180:      * Adds a new condition to the expression object in the form "field = value".
181:      *
182:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
183:      * @param mixed $value The value to be bound to $field for comparison
184:      * @param string|null $type the type name for $value as configured using the Type map.
185:      * If it is suffixed with "[]" and the value is an array then multiple placeholders
186:      * will be created, one per each value in the array.
187:      * @return $this
188:      */
189:     public function eq($field, $value, $type = null)
190:     {
191:         if ($type === null) {
192:             $type = $this->_calculateType($field);
193:         }
194: 
195:         return $this->add(new Comparison($field, $value, $type, '='));
196:     }
197: 
198:     /**
199:      * Adds a new condition to the expression object in the form "field != value".
200:      *
201:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
202:      * @param mixed $value The value to be bound to $field for comparison
203:      * @param string|null $type the type name for $value as configured using the Type map.
204:      * If it is suffixed with "[]" and the value is an array then multiple placeholders
205:      * will be created, one per each value in the array.
206:      * @return $this
207:      */
208:     public function notEq($field, $value, $type = null)
209:     {
210:         if ($type === null) {
211:             $type = $this->_calculateType($field);
212:         }
213: 
214:         return $this->add(new Comparison($field, $value, $type, '!='));
215:     }
216: 
217:     /**
218:      * Adds a new condition to the expression object in the form "field > value".
219:      *
220:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
221:      * @param mixed $value The value to be bound to $field for comparison
222:      * @param string|null $type the type name for $value as configured using the Type map.
223:      * @return $this
224:      */
225:     public function gt($field, $value, $type = null)
226:     {
227:         if ($type === null) {
228:             $type = $this->_calculateType($field);
229:         }
230: 
231:         return $this->add(new Comparison($field, $value, $type, '>'));
232:     }
233: 
234:     /**
235:      * Adds a new condition to the expression object in the form "field < value".
236:      *
237:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
238:      * @param mixed $value The value to be bound to $field for comparison
239:      * @param string|null $type the type name for $value as configured using the Type map.
240:      * @return $this
241:      */
242:     public function lt($field, $value, $type = null)
243:     {
244:         if ($type === null) {
245:             $type = $this->_calculateType($field);
246:         }
247: 
248:         return $this->add(new Comparison($field, $value, $type, '<'));
249:     }
250: 
251:     /**
252:      * Adds a new condition to the expression object in the form "field >= value".
253:      *
254:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
255:      * @param mixed $value The value to be bound to $field for comparison
256:      * @param string|null $type the type name for $value as configured using the Type map.
257:      * @return $this
258:      */
259:     public function gte($field, $value, $type = null)
260:     {
261:         if ($type === null) {
262:             $type = $this->_calculateType($field);
263:         }
264: 
265:         return $this->add(new Comparison($field, $value, $type, '>='));
266:     }
267: 
268:     /**
269:      * Adds a new condition to the expression object in the form "field <= value".
270:      *
271:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
272:      * @param mixed $value The value to be bound to $field for comparison
273:      * @param string|null $type the type name for $value as configured using the Type map.
274:      * @return $this
275:      */
276:     public function lte($field, $value, $type = null)
277:     {
278:         if ($type === null) {
279:             $type = $this->_calculateType($field);
280:         }
281: 
282:         return $this->add(new Comparison($field, $value, $type, '<='));
283:     }
284: 
285:     /**
286:      * Adds a new condition to the expression object in the form "field IS NULL".
287:      *
288:      * @param string|\Cake\Database\ExpressionInterface $field database field to be
289:      * tested for null
290:      * @return $this
291:      */
292:     public function isNull($field)
293:     {
294:         if (!($field instanceof ExpressionInterface)) {
295:             $field = new IdentifierExpression($field);
296:         }
297: 
298:         return $this->add(new UnaryExpression('IS NULL', $field, UnaryExpression::POSTFIX));
299:     }
300: 
301:     /**
302:      * Adds a new condition to the expression object in the form "field IS NOT NULL".
303:      *
304:      * @param string|\Cake\Database\ExpressionInterface $field database field to be
305:      * tested for not null
306:      * @return $this
307:      */
308:     public function isNotNull($field)
309:     {
310:         if (!($field instanceof ExpressionInterface)) {
311:             $field = new IdentifierExpression($field);
312:         }
313: 
314:         return $this->add(new UnaryExpression('IS NOT NULL', $field, UnaryExpression::POSTFIX));
315:     }
316: 
317:     /**
318:      * Adds a new condition to the expression object in the form "field LIKE value".
319:      *
320:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
321:      * @param mixed $value The value to be bound to $field for comparison
322:      * @param string|null $type the type name for $value as configured using the Type map.
323:      * @return $this
324:      */
325:     public function like($field, $value, $type = null)
326:     {
327:         if ($type === null) {
328:             $type = $this->_calculateType($field);
329:         }
330: 
331:         return $this->add(new Comparison($field, $value, $type, 'LIKE'));
332:     }
333: 
334:     /**
335:      * Adds a new condition to the expression object in the form "field NOT LIKE value".
336:      *
337:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
338:      * @param mixed $value The value to be bound to $field for comparison
339:      * @param string|null $type the type name for $value as configured using the Type map.
340:      * @return $this
341:      */
342:     public function notLike($field, $value, $type = null)
343:     {
344:         if ($type === null) {
345:             $type = $this->_calculateType($field);
346:         }
347: 
348:         return $this->add(new Comparison($field, $value, $type, 'NOT LIKE'));
349:     }
350: 
351:     /**
352:      * Adds a new condition to the expression object in the form
353:      * "field IN (value1, value2)".
354:      *
355:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
356:      * @param string|array $values the value to be bound to $field for comparison
357:      * @param string|null $type the type name for $value as configured using the Type map.
358:      * @return $this
359:      */
360:     public function in($field, $values, $type = null)
361:     {
362:         if ($type === null) {
363:             $type = $this->_calculateType($field);
364:         }
365:         $type = $type ?: 'string';
366:         $type .= '[]';
367:         $values = $values instanceof ExpressionInterface ? $values : (array)$values;
368: 
369:         return $this->add(new Comparison($field, $values, $type, 'IN'));
370:     }
371: 
372:     /**
373:      * Adds a new case expression to the expression object
374:      *
375:      * @param array|\Cake\Database\ExpressionInterface $conditions The conditions to test. Must be a ExpressionInterface
376:      * instance, or an array of ExpressionInterface instances.
377:      * @param array|\Cake\Database\ExpressionInterface $values associative array of values to be associated with the conditions
378:      * passed in $conditions. If there are more $values than $conditions, the last $value is used as the `ELSE` value
379:      * @param array $types associative array of types to be associated with the values
380:      * passed in $values
381:      * @return $this
382:      */
383:     public function addCase($conditions, $values = [], $types = [])
384:     {
385:         return $this->add(new CaseExpression($conditions, $values, $types));
386:     }
387: 
388:     /**
389:      * Adds a new condition to the expression object in the form
390:      * "field NOT IN (value1, value2)".
391:      *
392:      * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value
393:      * @param array $values the value to be bound to $field for comparison
394:      * @param string|null $type the type name for $value as configured using the Type map.
395:      * @return $this
396:      */
397:     public function notIn($field, $values, $type = null)
398:     {
399:         if ($type === null) {
400:             $type = $this->_calculateType($field);
401:         }
402:         $type = $type ?: 'string';
403:         $type .= '[]';
404:         $values = $values instanceof ExpressionInterface ? $values : (array)$values;
405: 
406:         return $this->add(new Comparison($field, $values, $type, 'NOT IN'));
407:     }
408: 
409:     /**
410:      * Adds a new condition to the expression object in the form "EXISTS (...)".
411:      *
412:      * @param \Cake\Database\ExpressionInterface $query the inner query
413:      * @return $this
414:      */
415:     public function exists(ExpressionInterface $query)
416:     {
417:         return $this->add(new UnaryExpression('EXISTS', $query, UnaryExpression::PREFIX));
418:     }
419: 
420:     /**
421:      * Adds a new condition to the expression object in the form "NOT EXISTS (...)".
422:      *
423:      * @param \Cake\Database\ExpressionInterface $query the inner query
424:      * @return $this
425:      */
426:     public function notExists(ExpressionInterface $query)
427:     {
428:         return $this->add(new UnaryExpression('NOT EXISTS', $query, UnaryExpression::PREFIX));
429:     }
430: 
431:     /**
432:      * Adds a new condition to the expression object in the form
433:      * "field BETWEEN from AND to".
434:      *
435:      * @param string|\Cake\Database\ExpressionInterface $field The field name to compare for values in between the range.
436:      * @param mixed $from The initial value of the range.
437:      * @param mixed $to The ending value in the comparison range.
438:      * @param string|null $type the type name for $value as configured using the Type map.
439:      * @return $this
440:      */
441:     public function between($field, $from, $to, $type = null)
442:     {
443:         if ($type === null) {
444:             $type = $this->_calculateType($field);
445:         }
446: 
447:         return $this->add(new BetweenExpression($field, $from, $to, $type));
448:     }
449: 
450: // @codingStandardsIgnoreStart
451:     /**
452:      * Returns a new QueryExpression object containing all the conditions passed
453:      * and set up the conjunction to be "AND"
454:      *
455:      * @param callable|string|array|\Cake\Database\ExpressionInterface $conditions to be joined with AND
456:      * @param array $types associative array of fields pointing to the type of the
457:      * values that are being passed. Used for correctly binding values to statements.
458:      * @return \Cake\Database\Expression\QueryExpression
459:      */
460:     public function and_($conditions, $types = [])
461:     {
462:         if ($this->isCallable($conditions)) {
463:             return $conditions(new static([], $this->getTypeMap()->setTypes($types)));
464:         }
465: 
466:         return new static($conditions, $this->getTypeMap()->setTypes($types));
467:     }
468: 
469:     /**
470:      * Returns a new QueryExpression object containing all the conditions passed
471:      * and set up the conjunction to be "OR"
472:      *
473:      * @param callable|string|array|\Cake\Database\ExpressionInterface $conditions to be joined with OR
474:      * @param array $types associative array of fields pointing to the type of the
475:      * values that are being passed. Used for correctly binding values to statements.
476:      * @return \Cake\Database\Expression\QueryExpression
477:      */
478:     public function or_($conditions, $types = [])
479:     {
480:         if ($this->isCallable($conditions)) {
481:             return $conditions(new static([], $this->getTypeMap()->setTypes($types), 'OR'));
482:         }
483: 
484:         return new static($conditions, $this->getTypeMap()->setTypes($types), 'OR');
485:     }
486: // @codingStandardsIgnoreEnd
487: 
488:     /**
489:      * Adds a new set of conditions to this level of the tree and negates
490:      * the final result by prepending a NOT, it will look like
491:      * "NOT ( (condition1) AND (conditions2) )" conjunction depends on the one
492:      * currently configured for this object.
493:      *
494:      * @param string|array|\Cake\Database\ExpressionInterface $conditions to be added and negated
495:      * @param array $types associative array of fields pointing to the type of the
496:      * values that are being passed. Used for correctly binding values to statements.
497:      * @return $this
498:      */
499:     public function not($conditions, $types = [])
500:     {
501:         return $this->add(['NOT' => $conditions], $types);
502:     }
503: 
504:     /**
505:      * Returns the number of internal conditions that are stored in this expression.
506:      * Useful to determine if this expression object is void or it will generate
507:      * a non-empty string when compiled
508:      *
509:      * @return int
510:      */
511:     public function count()
512:     {
513:         return count($this->_conditions);
514:     }
515: 
516:     /**
517:      * Builds equal condition or assignment with identifier wrapping.
518:      *
519:      * @param string $left Left join condition field name.
520:      * @param string $right Right join condition field name.
521:      * @return $this
522:      */
523:     public function equalFields($left, $right)
524:     {
525:         $wrapIdentifier = function ($field) {
526:             if ($field instanceof ExpressionInterface) {
527:                 return $field;
528:             }
529: 
530:             return new IdentifierExpression($field);
531:         };
532: 
533:         return $this->eq($wrapIdentifier($left), $wrapIdentifier($right));
534:     }
535: 
536:     /**
537:      * Returns the string representation of this object so that it can be used in a
538:      * SQL query. Note that values condition values are not included in the string,
539:      * in their place placeholders are put and can be replaced by the quoted values
540:      * accordingly.
541:      *
542:      * @param \Cake\Database\ValueBinder $generator Placeholder generator object
543:      * @return string
544:      */
545:     public function sql(ValueBinder $generator)
546:     {
547:         $len = $this->count();
548:         if ($len === 0) {
549:             return '';
550:         }
551:         $conjunction = $this->_conjunction;
552:         $template = ($len === 1) ? '%s' : '(%s)';
553:         $parts = [];
554:         foreach ($this->_conditions as $part) {
555:             if ($part instanceof Query) {
556:                 $part = '(' . $part->sql($generator) . ')';
557:             } elseif ($part instanceof ExpressionInterface) {
558:                 $part = $part->sql($generator);
559:             }
560:             if (strlen($part)) {
561:                 $parts[] = $part;
562:             }
563:         }
564: 
565:         return sprintf($template, implode(" $conjunction ", $parts));
566:     }
567: 
568:     /**
569:      * Traverses the tree structure of this query expression by executing a callback
570:      * function for each of the conditions that are included in this object.
571:      * Useful for compiling the final expression, or doing
572:      * introspection in the structure.
573:      *
574:      * Callback function receives as only argument an instance of ExpressionInterface
575:      *
576:      * @param callable $callable The callable to apply to all sub-expressions.
577:      * @return void
578:      */
579:     public function traverse(callable $callable)
580:     {
581:         foreach ($this->_conditions as $c) {
582:             if ($c instanceof ExpressionInterface) {
583:                 $callable($c);
584:                 $c->traverse($callable);
585:             }
586:         }
587:     }
588: 
589:     /**
590:      * Executes a callable function for each of the parts that form this expression.
591:      *
592:      * The callable function is required to return a value with which the currently
593:      * visited part will be replaced. If the callable function returns null then
594:      * the part will be discarded completely from this expression.
595:      *
596:      * The callback function will receive each of the conditions as first param and
597:      * the key as second param. It is possible to declare the second parameter as
598:      * passed by reference, this will enable you to change the key under which the
599:      * modified part is stored.
600:      *
601:      * @param callable $callable The callable to apply to each part.
602:      * @return $this
603:      */
604:     public function iterateParts(callable $callable)
605:     {
606:         $parts = [];
607:         foreach ($this->_conditions as $k => $c) {
608:             $key =& $k;
609:             $part = $callable($c, $key);
610:             if ($part !== null) {
611:                 $parts[$key] = $part;
612:             }
613:         }
614:         $this->_conditions = $parts;
615: 
616:         return $this;
617:     }
618: 
619:     /**
620:      * Helps calling the `and()` and `or()` methods transparently.
621:      *
622:      * @param string $method The method name.
623:      * @param array $args The arguments to pass to the method.
624:      * @return \Cake\Database\Expression\QueryExpression
625:      * @throws \BadMethodCallException
626:      */
627:     public function __call($method, $args)
628:     {
629:         if (in_array($method, ['and', 'or'])) {
630:             return call_user_func_array([$this, $method . '_'], $args);
631:         }
632:         throw new BadMethodCallException(sprintf('Method %s does not exist', $method));
633:     }
634: 
635:     /**
636:      * Check whether or not a callable is acceptable.
637:      *
638:      * We don't accept ['class', 'method'] style callbacks,
639:      * as they often contain user input and arrays of strings
640:      * are easy to sneak in.
641:      *
642:      * @param callable $c The callable to check.
643:      * @return bool Valid callable.
644:      */
645:     public function isCallable($c)
646:     {
647:         if (is_string($c)) {
648:             return false;
649:         }
650:         if (is_object($c) && is_callable($c)) {
651:             return true;
652:         }
653: 
654:         return is_array($c) && isset($c[0]) && is_object($c[0]) && is_callable($c);
655:     }
656: 
657:     /**
658:      * Returns true if this expression contains any other nested
659:      * ExpressionInterface objects
660:      *
661:      * @return bool
662:      */
663:     public function hasNestedExpression()
664:     {
665:         foreach ($this->_conditions as $c) {
666:             if ($c instanceof ExpressionInterface) {
667:                 return true;
668:             }
669:         }
670: 
671:         return false;
672:     }
673: 
674:     /**
675:      * Auxiliary function used for decomposing a nested array of conditions and build
676:      * a tree structure inside this object to represent the full SQL expression.
677:      * String conditions are stored directly in the conditions, while any other
678:      * representation is wrapped around an adequate instance or of this class.
679:      *
680:      * @param array $conditions list of conditions to be stored in this object
681:      * @param array $types list of types associated on fields referenced in $conditions
682:      * @return void
683:      */
684:     protected function _addConditions(array $conditions, array $types)
685:     {
686:         $operators = ['and', 'or', 'xor'];
687: 
688:         $typeMap = $this->getTypeMap()->setTypes($types);
689: 
690:         foreach ($conditions as $k => $c) {
691:             $numericKey = is_numeric($k);
692: 
693:             if ($this->isCallable($c)) {
694:                 $expr = new static([], $typeMap);
695:                 $c = $c($expr, $this);
696:             }
697: 
698:             if ($numericKey && empty($c)) {
699:                 continue;
700:             }
701: 
702:             $isArray = is_array($c);
703:             $isOperator = in_array(strtolower($k), $operators);
704:             $isNot = strtolower($k) === 'not';
705: 
706:             if (($isOperator || $isNot) && ($isArray || $c instanceof Countable) && count($c) === 0) {
707:                 continue;
708:             }
709: 
710:             if ($numericKey && $c instanceof ExpressionInterface) {
711:                 $this->_conditions[] = $c;
712:                 continue;
713:             }
714: 
715:             if ($numericKey && is_string($c)) {
716:                 $this->_conditions[] = $c;
717:                 continue;
718:             }
719: 
720:             if ($numericKey && $isArray || $isOperator) {
721:                 $this->_conditions[] = new static($c, $typeMap, $numericKey ? 'AND' : $k);
722:                 continue;
723:             }
724: 
725:             if ($isNot) {
726:                 $this->_conditions[] = new UnaryExpression('NOT', new static($c, $typeMap));
727:                 continue;
728:             }
729: 
730:             if (!$numericKey) {
731:                 $this->_conditions[] = $this->_parseCondition($k, $c);
732:             }
733:         }
734:     }
735: 
736:     /**
737:      * Parses a string conditions by trying to extract the operator inside it if any
738:      * and finally returning either an adequate QueryExpression object or a plain
739:      * string representation of the condition. This function is responsible for
740:      * generating the placeholders and replacing the values by them, while storing
741:      * the value elsewhere for future binding.
742:      *
743:      * @param string $field The value from with the actual field and operator will
744:      * be extracted.
745:      * @param mixed $value The value to be bound to a placeholder for the field
746:      * @return string|\Cake\Database\ExpressionInterface
747:      */
748:     protected function _parseCondition($field, $value)
749:     {
750:         $operator = '=';
751:         $expression = $field;
752:         $parts = explode(' ', trim($field), 2);
753: 
754:         if (count($parts) > 1) {
755:             list($expression, $operator) = $parts;
756:         }
757: 
758:         $type = $this->getTypeMap()->type($expression);
759:         $operator = strtolower(trim($operator));
760: 
761:         $typeMultiple = strpos($type, '[]') !== false;
762:         if (in_array($operator, ['in', 'not in']) || $typeMultiple) {
763:             $type = $type ?: 'string';
764:             $type .= $typeMultiple ? null : '[]';
765:             $operator = $operator === '=' ? 'IN' : $operator;
766:             $operator = $operator === '!=' ? 'NOT IN' : $operator;
767:             $typeMultiple = true;
768:         }
769: 
770:         if ($typeMultiple) {
771:             $value = $value instanceof ExpressionInterface ? $value : (array)$value;
772:         }
773: 
774:         if ($operator === 'is' && $value === null) {
775:             return new UnaryExpression(
776:                 'IS NULL',
777:                 new IdentifierExpression($expression),
778:                 UnaryExpression::POSTFIX
779:             );
780:         }
781: 
782:         if ($operator === 'is not' && $value === null) {
783:             return new UnaryExpression(
784:                 'IS NOT NULL',
785:                 new IdentifierExpression($expression),
786:                 UnaryExpression::POSTFIX
787:             );
788:         }
789: 
790:         if ($operator === 'is' && $value !== null) {
791:             $operator = '=';
792:         }
793: 
794:         if ($operator === 'is not' && $value !== null) {
795:             $operator = '!=';
796:         }
797: 
798:         return new Comparison($expression, $value, $type, $operator);
799:     }
800: 
801:     /**
802:      * Returns the type name for the passed field if it was stored in the typeMap
803:      *
804:      * @param string|\Cake\Database\Expression\IdentifierExpression $field The field name to get a type for.
805:      * @return string|null The computed type or null, if the type is unknown.
806:      */
807:     protected function _calculateType($field)
808:     {
809:         $field = $field instanceof IdentifierExpression ? $field->getIdentifier() : $field;
810:         if (is_string($field)) {
811:             return $this->getTypeMap()->type($field);
812:         }
813: 
814:         return null;
815:     }
816: 
817:     /**
818:      * Clone this object and its subtree of expressions.
819:      *
820:      * @return void
821:      */
822:     public function __clone()
823:     {
824:         foreach ($this->_conditions as $i => $condition) {
825:             if ($condition instanceof ExpressionInterface) {
826:                 $this->_conditions[$i] = clone $condition;
827:             }
828:         }
829:     }
830: }
831: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs