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.8 Red Velvet API

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