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

  • Association
  • AssociationCollection
  • Behavior
  • BehaviorRegistry
  • EagerLoader
  • Entity
  • Marshaller
  • Query
  • ResultSet
  • RulesChecker
  • SaveOptionsBuilder
  • Table
  • TableRegistry

Interfaces

  • PropertyMarshalInterface

Traits

  • AssociationsNormalizerTrait
  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\ORM;
 16: 
 17: use Cake\Collection\Collection;
 18: use Cake\Collection\CollectionTrait;
 19: use Cake\Database\Exception;
 20: use Cake\Database\Type;
 21: use Cake\Datasource\EntityInterface;
 22: use Cake\Datasource\ResultSetInterface;
 23: use SplFixedArray;
 24: 
 25: /**
 26:  * Represents the results obtained after executing a query for a specific table
 27:  * This object is responsible for correctly nesting result keys reported from
 28:  * the query, casting each field to the correct type and executing the extra
 29:  * queries required for eager loading external associations.
 30:  */
 31: class ResultSet implements ResultSetInterface
 32: {
 33:     use CollectionTrait;
 34: 
 35:     /**
 36:      * Original query from where results were generated
 37:      *
 38:      * @var \Cake\ORM\Query
 39:      * @deprecated 3.1.6 Due to a memory leak, this property cannot be used anymore
 40:      */
 41:     protected $_query;
 42: 
 43:     /**
 44:      * Database statement holding the results
 45:      *
 46:      * @var \Cake\Database\StatementInterface
 47:      */
 48:     protected $_statement;
 49: 
 50:     /**
 51:      * Points to the next record number that should be fetched
 52:      *
 53:      * @var int
 54:      */
 55:     protected $_index = 0;
 56: 
 57:     /**
 58:      * Last record fetched from the statement
 59:      *
 60:      * @var array
 61:      */
 62:     protected $_current;
 63: 
 64:     /**
 65:      * Default table instance
 66:      *
 67:      * @var \Cake\ORM\Table|\Cake\Datasource\RepositoryInterface
 68:      */
 69:     protected $_defaultTable;
 70: 
 71:     /**
 72:      * The default table alias
 73:      *
 74:      * @var string
 75:      */
 76:     protected $_defaultAlias;
 77: 
 78:     /**
 79:      * List of associations that should be placed under the `_matchingData`
 80:      * result key.
 81:      *
 82:      * @var array
 83:      */
 84:     protected $_matchingMap = [];
 85: 
 86:     /**
 87:      * List of associations that should be eager loaded.
 88:      *
 89:      * @var array
 90:      */
 91:     protected $_containMap = [];
 92: 
 93:     /**
 94:      * Map of fields that are fetched from the statement with
 95:      * their type and the table they belong to
 96:      *
 97:      * @var array
 98:      */
 99:     protected $_map = [];
100: 
101:     /**
102:      * List of matching associations and the column keys to expect
103:      * from each of them.
104:      *
105:      * @var array
106:      */
107:     protected $_matchingMapColumns = [];
108: 
109:     /**
110:      * Results that have been fetched or hydrated into the results.
111:      *
112:      * @var array|\ArrayAccess
113:      */
114:     protected $_results = [];
115: 
116:     /**
117:      * Whether to hydrate results into objects or not
118:      *
119:      * @var bool
120:      */
121:     protected $_hydrate = true;
122: 
123:     /**
124:      * Tracks value of $_autoFields property of $query passed to constructor.
125:      *
126:      * @var bool
127:      */
128:     protected $_autoFields;
129: 
130:     /**
131:      * The fully namespaced name of the class to use for hydrating results
132:      *
133:      * @var string
134:      */
135:     protected $_entityClass;
136: 
137:     /**
138:      * Whether or not to buffer results fetched from the statement
139:      *
140:      * @var bool
141:      */
142:     protected $_useBuffering = true;
143: 
144:     /**
145:      * Holds the count of records in this result set
146:      *
147:      * @var int
148:      */
149:     protected $_count;
150: 
151:     /**
152:      * Type cache for type converters.
153:      *
154:      * Converters are indexed by alias and column name.
155:      *
156:      * @var array
157:      * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level
158:      */
159:     protected $_types = [];
160: 
161:     /**
162:      * The Database driver object.
163:      *
164:      * Cached in a property to avoid multiple calls to the same function.
165:      *
166:      * @var \Cake\Database\Driver
167:      */
168:     protected $_driver;
169: 
170:     /**
171:      * Constructor
172:      *
173:      * @param \Cake\ORM\Query $query Query from where results come
174:      * @param \Cake\Database\StatementInterface $statement The statement to fetch from
175:      */
176:     public function __construct($query, $statement)
177:     {
178:         /** @var \Cake\ORM\Table $repository */
179:         $repository = $query->getRepository();
180:         $this->_statement = $statement;
181:         $this->_driver = $query->getConnection()->getDriver();
182:         $this->_defaultTable = $query->getRepository();
183:         $this->_calculateAssociationMap($query);
184:         $this->_hydrate = $query->isHydrationEnabled();
185:         $this->_entityClass = $repository->getEntityClass();
186:         $this->_useBuffering = $query->isBufferedResultsEnabled();
187:         $this->_defaultAlias = $this->_defaultTable->getAlias();
188:         $this->_calculateColumnMap($query);
189:         $this->_autoFields = $query->isAutoFieldsEnabled();
190: 
191:         if ($this->_useBuffering) {
192:             $count = $this->count();
193:             $this->_results = new SplFixedArray($count);
194:         }
195:     }
196: 
197:     /**
198:      * Returns the current record in the result iterator
199:      *
200:      * Part of Iterator interface.
201:      *
202:      * @return array|object
203:      */
204:     public function current()
205:     {
206:         return $this->_current;
207:     }
208: 
209:     /**
210:      * Returns the key of the current record in the iterator
211:      *
212:      * Part of Iterator interface.
213:      *
214:      * @return int
215:      */
216:     public function key()
217:     {
218:         return $this->_index;
219:     }
220: 
221:     /**
222:      * Advances the iterator pointer to the next record
223:      *
224:      * Part of Iterator interface.
225:      *
226:      * @return void
227:      */
228:     public function next()
229:     {
230:         $this->_index++;
231:     }
232: 
233:     /**
234:      * Rewinds a ResultSet.
235:      *
236:      * Part of Iterator interface.
237:      *
238:      * @throws \Cake\Database\Exception
239:      * @return void
240:      */
241:     public function rewind()
242:     {
243:         if ($this->_index == 0) {
244:             return;
245:         }
246: 
247:         if (!$this->_useBuffering) {
248:             $msg = 'You cannot rewind an un-buffered ResultSet. Use Query::bufferResults() to get a buffered ResultSet.';
249:             throw new Exception($msg);
250:         }
251: 
252:         $this->_index = 0;
253:     }
254: 
255:     /**
256:      * Whether there are more results to be fetched from the iterator
257:      *
258:      * Part of Iterator interface.
259:      *
260:      * @return bool
261:      */
262:     public function valid()
263:     {
264:         if ($this->_useBuffering) {
265:             $valid = $this->_index < $this->_count;
266:             if ($valid && $this->_results[$this->_index] !== null) {
267:                 $this->_current = $this->_results[$this->_index];
268: 
269:                 return true;
270:             }
271:             if (!$valid) {
272:                 return $valid;
273:             }
274:         }
275: 
276:         $this->_current = $this->_fetchResult();
277:         $valid = $this->_current !== false;
278: 
279:         if ($valid && $this->_useBuffering) {
280:             $this->_results[$this->_index] = $this->_current;
281:         }
282:         if (!$valid && $this->_statement !== null) {
283:             $this->_statement->closeCursor();
284:         }
285: 
286:         return $valid;
287:     }
288: 
289:     /**
290:      * Get the first record from a result set.
291:      *
292:      * This method will also close the underlying statement cursor.
293:      *
294:      * @return array|object
295:      */
296:     public function first()
297:     {
298:         foreach ($this as $result) {
299:             if ($this->_statement && !$this->_useBuffering) {
300:                 $this->_statement->closeCursor();
301:             }
302: 
303:             return $result;
304:         }
305:     }
306: 
307:     /**
308:      * Serializes a resultset.
309:      *
310:      * Part of Serializable interface.
311:      *
312:      * @return string Serialized object
313:      */
314:     public function serialize()
315:     {
316:         if (!$this->_useBuffering) {
317:             $msg = 'You cannot serialize an un-buffered ResultSet. Use Query::bufferResults() to get a buffered ResultSet.';
318:             throw new Exception($msg);
319:         }
320: 
321:         while ($this->valid()) {
322:             $this->next();
323:         }
324: 
325:         if ($this->_results instanceof SplFixedArray) {
326:             return serialize($this->_results->toArray());
327:         }
328: 
329:         return serialize($this->_results);
330:     }
331: 
332:     /**
333:      * Unserializes a resultset.
334:      *
335:      * Part of Serializable interface.
336:      *
337:      * @param string $serialized Serialized object
338:      * @return void
339:      */
340:     public function unserialize($serialized)
341:     {
342:         $results = (array)(unserialize($serialized) ?: []);
343:         $this->_results = SplFixedArray::fromArray($results);
344:         $this->_useBuffering = true;
345:         $this->_count = $this->_results->count();
346:     }
347: 
348:     /**
349:      * Gives the number of rows in the result set.
350:      *
351:      * Part of the Countable interface.
352:      *
353:      * @return int
354:      */
355:     public function count()
356:     {
357:         if ($this->_count !== null) {
358:             return $this->_count;
359:         }
360:         if ($this->_statement) {
361:             return $this->_count = $this->_statement->rowCount();
362:         }
363: 
364:         if ($this->_results instanceof SplFixedArray) {
365:             $this->_count = $this->_results->count();
366:         } else {
367:             $this->_count = count($this->_results);
368:         }
369: 
370:         return $this->_count;
371:     }
372: 
373:     /**
374:      * Calculates the list of associations that should get eager loaded
375:      * when fetching each record
376:      *
377:      * @param \Cake\ORM\Query $query The query from where to derive the associations
378:      * @return void
379:      */
380:     protected function _calculateAssociationMap($query)
381:     {
382:         $map = $query->getEagerLoader()->associationsMap($this->_defaultTable);
383:         $this->_matchingMap = (new Collection($map))
384:             ->match(['matching' => true])
385:             ->indexBy('alias')
386:             ->toArray();
387: 
388:         $this->_containMap = (new Collection(array_reverse($map)))
389:             ->match(['matching' => false])
390:             ->indexBy('nestKey')
391:             ->toArray();
392:     }
393: 
394:     /**
395:      * Creates a map of row keys out of the query select clause that can be
396:      * used to hydrate nested result sets more quickly.
397:      *
398:      * @param \Cake\ORM\Query $query The query from where to derive the column map
399:      * @return void
400:      */
401:     protected function _calculateColumnMap($query)
402:     {
403:         $map = [];
404:         foreach ($query->clause('select') as $key => $field) {
405:             $key = trim($key, '"`[]');
406: 
407:             if (strpos($key, '__') <= 0) {
408:                 $map[$this->_defaultAlias][$key] = $key;
409:                 continue;
410:             }
411: 
412:             $parts = explode('__', $key, 2);
413:             $map[$parts[0]][$key] = $parts[1];
414:         }
415: 
416:         foreach ($this->_matchingMap as $alias => $assoc) {
417:             if (!isset($map[$alias])) {
418:                 continue;
419:             }
420:             $this->_matchingMapColumns[$alias] = $map[$alias];
421:             unset($map[$alias]);
422:         }
423: 
424:         $this->_map = $map;
425:     }
426: 
427:     /**
428:      * Creates a map of Type converter classes for each of the columns that should
429:      * be fetched by this object.
430:      *
431:      * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level
432:      * @return void
433:      */
434:     protected function _calculateTypeMap()
435:     {
436:         deprecationWarning('ResultSet::_calculateTypeMap() is deprecated, and will be removed in 4.0.0.');
437:     }
438: 
439:     /**
440:      * Returns the Type classes for each of the passed fields belonging to the
441:      * table.
442:      *
443:      * @param \Cake\ORM\Table $table The table from which to get the schema
444:      * @param array $fields The fields whitelist to use for fields in the schema.
445:      * @return array
446:      * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level
447:      */
448:     protected function _getTypes($table, $fields)
449:     {
450:         $types = [];
451:         $schema = $table->getSchema();
452:         $map = array_keys((array)Type::getMap() + ['string' => 1, 'text' => 1, 'boolean' => 1]);
453:         $typeMap = array_combine(
454:             $map,
455:             array_map(['Cake\Database\Type', 'build'], $map)
456:         );
457: 
458:         foreach (['string', 'text'] as $t) {
459:             if (get_class($typeMap[$t]) === 'Cake\Database\Type') {
460:                 unset($typeMap[$t]);
461:             }
462:         }
463: 
464:         foreach (array_intersect($fields, $schema->columns()) as $col) {
465:             $typeName = $schema->getColumnType($col);
466:             if (isset($typeMap[$typeName])) {
467:                 $types[$col] = $typeMap[$typeName];
468:             }
469:         }
470: 
471:         return $types;
472:     }
473: 
474:     /**
475:      * Helper function to fetch the next result from the statement or
476:      * seeded results.
477:      *
478:      * @return mixed
479:      */
480:     protected function _fetchResult()
481:     {
482:         if (!$this->_statement) {
483:             return false;
484:         }
485: 
486:         $row = $this->_statement->fetch('assoc');
487:         if ($row === false) {
488:             return $row;
489:         }
490: 
491:         return $this->_groupResult($row);
492:     }
493: 
494:     /**
495:      * Correctly nests results keys including those coming from associations
496:      *
497:      * @param array $row Array containing columns and values or false if there is no results
498:      * @return array Results
499:      */
500:     protected function _groupResult($row)
501:     {
502:         $defaultAlias = $this->_defaultAlias;
503:         $results = $presentAliases = [];
504:         $options = [
505:             'useSetters' => false,
506:             'markClean' => true,
507:             'markNew' => false,
508:             'guard' => false
509:         ];
510: 
511:         foreach ($this->_matchingMapColumns as $alias => $keys) {
512:             $matching = $this->_matchingMap[$alias];
513:             $results['_matchingData'][$alias] = array_combine(
514:                 $keys,
515:                 array_intersect_key($row, $keys)
516:             );
517:             if ($this->_hydrate) {
518:                 /* @var \Cake\ORM\Table $table */
519:                 $table = $matching['instance'];
520:                 $options['source'] = $table->getRegistryAlias();
521:                 /* @var \Cake\Datasource\EntityInterface $entity */
522:                 $entity = new $matching['entityClass']($results['_matchingData'][$alias], $options);
523:                 $results['_matchingData'][$alias] = $entity;
524:             }
525:         }
526: 
527:         foreach ($this->_map as $table => $keys) {
528:             $results[$table] = array_combine($keys, array_intersect_key($row, $keys));
529:             $presentAliases[$table] = true;
530:         }
531: 
532:         // If the default table is not in the results, set
533:         // it to an empty array so that any contained
534:         // associations hydrate correctly.
535:         if (!isset($results[$defaultAlias])) {
536:             $results[$defaultAlias] = [];
537:         }
538: 
539:         unset($presentAliases[$defaultAlias]);
540: 
541:         foreach ($this->_containMap as $assoc) {
542:             $alias = $assoc['nestKey'];
543: 
544:             if ($assoc['canBeJoined'] && empty($this->_map[$alias])) {
545:                 continue;
546:             }
547: 
548:             /* @var \Cake\ORM\Association $instance */
549:             $instance = $assoc['instance'];
550: 
551:             if (!$assoc['canBeJoined'] && !isset($row[$alias])) {
552:                 $results = $instance->defaultRowValue($results, $assoc['canBeJoined']);
553:                 continue;
554:             }
555: 
556:             if (!$assoc['canBeJoined']) {
557:                 $results[$alias] = $row[$alias];
558:             }
559: 
560:             $target = $instance->getTarget();
561:             $options['source'] = $target->getRegistryAlias();
562:             unset($presentAliases[$alias]);
563: 
564:             if ($assoc['canBeJoined'] && $this->_autoFields !== false) {
565:                 $hasData = false;
566:                 foreach ($results[$alias] as $v) {
567:                     if ($v !== null && $v !== []) {
568:                         $hasData = true;
569:                         break;
570:                     }
571:                 }
572: 
573:                 if (!$hasData) {
574:                     $results[$alias] = null;
575:                 }
576:             }
577: 
578:             if ($this->_hydrate && $results[$alias] !== null && $assoc['canBeJoined']) {
579:                 $entity = new $assoc['entityClass']($results[$alias], $options);
580:                 $results[$alias] = $entity;
581:             }
582: 
583:             $results = $instance->transformRow($results, $alias, $assoc['canBeJoined'], $assoc['targetProperty']);
584:         }
585: 
586:         foreach ($presentAliases as $alias => $present) {
587:             if (!isset($results[$alias])) {
588:                 continue;
589:             }
590:             $results[$defaultAlias][$alias] = $results[$alias];
591:         }
592: 
593:         if (isset($results['_matchingData'])) {
594:             $results[$defaultAlias]['_matchingData'] = $results['_matchingData'];
595:         }
596: 
597:         $options['source'] = $this->_defaultTable->getRegistryAlias();
598:         if (isset($results[$defaultAlias])) {
599:             $results = $results[$defaultAlias];
600:         }
601:         if ($this->_hydrate && !($results instanceof EntityInterface)) {
602:             $results = new $this->_entityClass($results, $options);
603:         }
604: 
605:         return $results;
606:     }
607: 
608:     /**
609:      * Casts all values from a row brought from a table to the correct
610:      * PHP type.
611:      *
612:      * @param string $alias The table object alias
613:      * @param array $values The values to cast
614:      * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level
615:      * @return array
616:      */
617:     protected function _castValues($alias, $values)
618:     {
619:         deprecationWarning('ResultSet::_castValues() is deprecated, and will be removed in 4.0.0.');
620: 
621:         return $values;
622:     }
623: 
624:     /**
625:      * Returns an array that can be used to describe the internal state of this
626:      * object.
627:      *
628:      * @return array
629:      */
630:     public function __debugInfo()
631:     {
632:         return [
633:             'items' => $this->toArray(),
634:         ];
635:     }
636: }
637: 
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