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

  • 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 ArrayObject;
  18: use BadMethodCallException;
  19: use Cake\Core\App;
  20: use Cake\Database\Schema\TableSchema;
  21: use Cake\Database\Type;
  22: use Cake\Datasource\ConnectionInterface;
  23: use Cake\Datasource\EntityInterface;
  24: use Cake\Datasource\Exception\InvalidPrimaryKeyException;
  25: use Cake\Datasource\RepositoryInterface;
  26: use Cake\Datasource\RulesAwareTrait;
  27: use Cake\Event\EventDispatcherInterface;
  28: use Cake\Event\EventDispatcherTrait;
  29: use Cake\Event\EventListenerInterface;
  30: use Cake\Event\EventManager;
  31: use Cake\ORM\Association\BelongsTo;
  32: use Cake\ORM\Association\BelongsToMany;
  33: use Cake\ORM\Association\HasMany;
  34: use Cake\ORM\Association\HasOne;
  35: use Cake\ORM\Exception\MissingEntityException;
  36: use Cake\ORM\Exception\PersistenceFailedException;
  37: use Cake\ORM\Exception\RolledbackTransactionException;
  38: use Cake\ORM\Rule\IsUnique;
  39: use Cake\Utility\Inflector;
  40: use Cake\Validation\ValidatorAwareInterface;
  41: use Cake\Validation\ValidatorAwareTrait;
  42: use InvalidArgumentException;
  43: use RuntimeException;
  44: 
  45: /**
  46:  * Represents a single database table.
  47:  *
  48:  * Exposes methods for retrieving data out of it, and manages the associations
  49:  * this table has to other tables. Multiple instances of this class can be created
  50:  * for the same database table with different aliases, this allows you to address
  51:  * your database structure in a richer and more expressive way.
  52:  *
  53:  * ### Retrieving data
  54:  *
  55:  * The primary way to retrieve data is using Table::find(). See that method
  56:  * for more information.
  57:  *
  58:  * ### Dynamic finders
  59:  *
  60:  * In addition to the standard find($type) finder methods, CakePHP provides dynamic
  61:  * finder methods. These methods allow you to easily set basic conditions up. For example
  62:  * to filter users by username you would call
  63:  *
  64:  * ```
  65:  * $query = $users->findByUsername('mark');
  66:  * ```
  67:  *
  68:  * You can also combine conditions on multiple fields using either `Or` or `And`:
  69:  *
  70:  * ```
  71:  * $query = $users->findByUsernameOrEmail('mark', 'mark@example.org');
  72:  * ```
  73:  *
  74:  * ### Bulk updates/deletes
  75:  *
  76:  * You can use Table::updateAll() and Table::deleteAll() to do bulk updates/deletes.
  77:  * You should be aware that events will *not* be fired for bulk updates/deletes.
  78:  *
  79:  * ### Callbacks/events
  80:  *
  81:  * Table objects provide a few callbacks/events you can hook into to augment/replace
  82:  * find operations. Each event uses the standard event subsystem in CakePHP
  83:  *
  84:  * - `beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)`
  85:  *   Fired before each find operation. By stopping the event and supplying a
  86:  *   return value you can bypass the find operation entirely. Any changes done
  87:  *   to the $query instance will be retained for the rest of the find. The
  88:  *   $primary parameter indicates whether or not this is the root query,
  89:  *   or an associated query.
  90:  *
  91:  * - `buildValidator(Event $event, Validator $validator, string $name)`
  92:  *   Allows listeners to modify validation rules for the provided named validator.
  93:  *
  94:  * - `buildRules(Event $event, RulesChecker $rules)`
  95:  *   Allows listeners to modify the rules checker by adding more rules.
  96:  *
  97:  * - `beforeRules(Event $event, EntityInterface $entity, ArrayObject $options, string $operation)`
  98:  *   Fired before an entity is validated using the rules checker. By stopping this event,
  99:  *   you can return the final value of the rules checking operation.
 100:  *
 101:  * - `afterRules(Event $event, EntityInterface $entity, ArrayObject $options, bool $result, string $operation)`
 102:  *   Fired after the rules have been checked on the entity. By stopping this event,
 103:  *   you can return the final value of the rules checking operation.
 104:  *
 105:  * - `beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)`
 106:  *   Fired before each entity is saved. Stopping this event will abort the save
 107:  *   operation. When the event is stopped the result of the event will be returned.
 108:  *
 109:  * - `afterSave(Event $event, EntityInterface $entity, ArrayObject $options)`
 110:  *   Fired after an entity is saved.
 111:  *
 112:  * - `afterSaveCommit(Event $event, EntityInterface $entity, ArrayObject $options)`
 113:  *   Fired after the transaction in which the save operation is wrapped has been committed.
 114:  *   It’s also triggered for non atomic saves where database operations are implicitly committed.
 115:  *   The event is triggered only for the primary table on which save() is directly called.
 116:  *   The event is not triggered if a transaction is started before calling save.
 117:  *
 118:  * - `beforeDelete(Event $event, EntityInterface $entity, ArrayObject $options)`
 119:  *   Fired before an entity is deleted. By stopping this event you will abort
 120:  *   the delete operation.
 121:  *
 122:  * - `afterDelete(Event $event, EntityInterface $entity, ArrayObject $options)`
 123:  *   Fired after an entity has been deleted.
 124:  *
 125:  * @see \Cake\Event\EventManager for reference on the events system.
 126:  */
 127: class Table implements RepositoryInterface, EventListenerInterface, EventDispatcherInterface, ValidatorAwareInterface
 128: {
 129: 
 130:     use EventDispatcherTrait;
 131:     use RulesAwareTrait;
 132:     use ValidatorAwareTrait;
 133: 
 134:     /**
 135:      * The alias this object is assigned to validators as.
 136:      *
 137:      * @var string
 138:      */
 139:     const VALIDATOR_PROVIDER_NAME = 'table';
 140: 
 141:     /**
 142:      * The name of the event dispatched when a validator has been built.
 143:      *
 144:      * @var string
 145:      */
 146:     const BUILD_VALIDATOR_EVENT = 'Model.buildValidator';
 147: 
 148:     /**
 149:      * The rules class name that is used.
 150:      *
 151:      * @var string
 152:      */
 153:     const RULES_CLASS = RulesChecker::class;
 154: 
 155:     /**
 156:      * The IsUnique class name that is used.
 157:      *
 158:      * @var string
 159:      */
 160:     const IS_UNIQUE_CLASS = IsUnique::class;
 161: 
 162:     /**
 163:      * Name of the table as it can be found in the database
 164:      *
 165:      * @var string
 166:      */
 167:     protected $_table;
 168: 
 169:     /**
 170:      * Human name giving to this particular instance. Multiple objects representing
 171:      * the same database table can exist by using different aliases.
 172:      *
 173:      * @var string
 174:      */
 175:     protected $_alias;
 176: 
 177:     /**
 178:      * Connection instance
 179:      *
 180:      * @var \Cake\Database\Connection
 181:      */
 182:     protected $_connection;
 183: 
 184:     /**
 185:      * The schema object containing a description of this table fields
 186:      *
 187:      * @var \Cake\Database\Schema\TableSchema
 188:      */
 189:     protected $_schema;
 190: 
 191:     /**
 192:      * The name of the field that represents the primary key in the table
 193:      *
 194:      * @var string|array
 195:      */
 196:     protected $_primaryKey;
 197: 
 198:     /**
 199:      * The name of the field that represents a human readable representation of a row
 200:      *
 201:      * @var string
 202:      */
 203:     protected $_displayField;
 204: 
 205:     /**
 206:      * The associations container for this Table.
 207:      *
 208:      * @var \Cake\ORM\AssociationCollection
 209:      */
 210:     protected $_associations;
 211: 
 212:     /**
 213:      * BehaviorRegistry for this table
 214:      *
 215:      * @var \Cake\ORM\BehaviorRegistry
 216:      */
 217:     protected $_behaviors;
 218: 
 219:     /**
 220:      * The name of the class that represent a single row for this table
 221:      *
 222:      * @var string
 223:      */
 224:     protected $_entityClass;
 225: 
 226:     /**
 227:      * Registry key used to create this table object
 228:      *
 229:      * @var string
 230:      */
 231:     protected $_registryAlias;
 232: 
 233:     /**
 234:      * Initializes a new instance
 235:      *
 236:      * The $config array understands the following keys:
 237:      *
 238:      * - table: Name of the database table to represent
 239:      * - alias: Alias to be assigned to this table (default to table name)
 240:      * - connection: The connection instance to use
 241:      * - entityClass: The fully namespaced class name of the entity class that will
 242:      *   represent rows in this table.
 243:      * - schema: A \Cake\Database\Schema\TableSchema object or an array that can be
 244:      *   passed to it.
 245:      * - eventManager: An instance of an event manager to use for internal events
 246:      * - behaviors: A BehaviorRegistry. Generally not used outside of tests.
 247:      * - associations: An AssociationCollection instance.
 248:      * - validator: A Validator instance which is assigned as the "default"
 249:      *   validation set, or an associative array, where key is the name of the
 250:      *   validation set and value the Validator instance.
 251:      *
 252:      * @param array $config List of options for this table
 253:      */
 254:     public function __construct(array $config = [])
 255:     {
 256:         if (!empty($config['registryAlias'])) {
 257:             $this->setRegistryAlias($config['registryAlias']);
 258:         }
 259:         if (!empty($config['table'])) {
 260:             $this->setTable($config['table']);
 261:         }
 262:         if (!empty($config['alias'])) {
 263:             $this->setAlias($config['alias']);
 264:         }
 265:         if (!empty($config['connection'])) {
 266:             $this->setConnection($config['connection']);
 267:         }
 268:         if (!empty($config['schema'])) {
 269:             $this->setSchema($config['schema']);
 270:         }
 271:         if (!empty($config['entityClass'])) {
 272:             $this->setEntityClass($config['entityClass']);
 273:         }
 274:         $eventManager = $behaviors = $associations = null;
 275:         if (!empty($config['eventManager'])) {
 276:             $eventManager = $config['eventManager'];
 277:         }
 278:         if (!empty($config['behaviors'])) {
 279:             $behaviors = $config['behaviors'];
 280:         }
 281:         if (!empty($config['associations'])) {
 282:             $associations = $config['associations'];
 283:         }
 284:         if (!empty($config['validator'])) {
 285:             if (!is_array($config['validator'])) {
 286:                 $this->setValidator(static::DEFAULT_VALIDATOR, $config['validator']);
 287:             } else {
 288:                 foreach ($config['validator'] as $name => $validator) {
 289:                     $this->setValidator($name, $validator);
 290:                 }
 291:             }
 292:         }
 293:         $this->_eventManager = $eventManager ?: new EventManager();
 294:         $this->_behaviors = $behaviors ?: new BehaviorRegistry();
 295:         $this->_behaviors->setTable($this);
 296:         $this->_associations = $associations ?: new AssociationCollection();
 297: 
 298:         $this->initialize($config);
 299:         $this->_eventManager->on($this);
 300:         $this->dispatchEvent('Model.initialize');
 301:     }
 302: 
 303:     /**
 304:      * Get the default connection name.
 305:      *
 306:      * This method is used to get the fallback connection name if an
 307:      * instance is created through the TableLocator without a connection.
 308:      *
 309:      * @return string
 310:      * @see \Cake\ORM\Locator\TableLocator::get()
 311:      */
 312:     public static function defaultConnectionName()
 313:     {
 314:         return 'default';
 315:     }
 316: 
 317:     /**
 318:      * Initialize a table instance. Called after the constructor.
 319:      *
 320:      * You can use this method to define associations, attach behaviors
 321:      * define validation and do any other initialization logic you need.
 322:      *
 323:      * ```
 324:      *  public function initialize(array $config)
 325:      *  {
 326:      *      $this->belongsTo('Users');
 327:      *      $this->belongsToMany('Tagging.Tags');
 328:      *      $this->setPrimaryKey('something_else');
 329:      *  }
 330:      * ```
 331:      *
 332:      * @param array $config Configuration options passed to the constructor
 333:      * @return void
 334:      */
 335:     public function initialize(array $config)
 336:     {
 337:     }
 338: 
 339:     /**
 340:      * Sets the database table name.
 341:      *
 342:      * @param string $table Table name.
 343:      * @return $this
 344:      */
 345:     public function setTable($table)
 346:     {
 347:         $this->_table = $table;
 348: 
 349:         return $this;
 350:     }
 351: 
 352:     /**
 353:      * Returns the database table name.
 354:      *
 355:      * @return string
 356:      */
 357:     public function getTable()
 358:     {
 359:         if ($this->_table === null) {
 360:             $table = namespaceSplit(get_class($this));
 361:             $table = substr(end($table), 0, -5);
 362:             if (!$table) {
 363:                 $table = $this->getAlias();
 364:             }
 365:             $this->_table = Inflector::underscore($table);
 366:         }
 367: 
 368:         return $this->_table;
 369:     }
 370: 
 371:     /**
 372:      * Returns the database table name or sets a new one.
 373:      *
 374:      * @deprecated 3.4.0 Use setTable()/getTable() instead.
 375:      * @param string|null $table the new table name
 376:      * @return string
 377:      */
 378:     public function table($table = null)
 379:     {
 380:         deprecationWarning(
 381:             get_called_class() . '::table() is deprecated. ' .
 382:             'Use setTable()/getTable() instead.'
 383:         );
 384:         if ($table !== null) {
 385:             $this->setTable($table);
 386:         }
 387: 
 388:         return $this->getTable();
 389:     }
 390: 
 391:     /**
 392:      * Sets the table alias.
 393:      *
 394:      * @param string $alias Table alias
 395:      * @return $this
 396:      */
 397:     public function setAlias($alias)
 398:     {
 399:         $this->_alias = $alias;
 400: 
 401:         return $this;
 402:     }
 403: 
 404:     /**
 405:      * Returns the table alias.
 406:      *
 407:      * @return string
 408:      */
 409:     public function getAlias()
 410:     {
 411:         if ($this->_alias === null) {
 412:             $alias = namespaceSplit(get_class($this));
 413:             $alias = substr(end($alias), 0, -5) ?: $this->_table;
 414:             $this->_alias = $alias;
 415:         }
 416: 
 417:         return $this->_alias;
 418:     }
 419: 
 420:     /**
 421:      * {@inheritDoc}
 422:      * @deprecated 3.4.0 Use setAlias()/getAlias() instead.
 423:      */
 424:     public function alias($alias = null)
 425:     {
 426:         deprecationWarning(
 427:             get_called_class() . '::alias() is deprecated. ' .
 428:             'Use setAlias()/getAlias() instead.'
 429:         );
 430:         if ($alias !== null) {
 431:             $this->setAlias($alias);
 432:         }
 433: 
 434:         return $this->getAlias();
 435:     }
 436: 
 437:     /**
 438:      * Alias a field with the table's current alias.
 439:      *
 440:      * If field is already aliased it will result in no-op.
 441:      *
 442:      * @param string $field The field to alias.
 443:      * @return string The field prefixed with the table alias.
 444:      */
 445:     public function aliasField($field)
 446:     {
 447:         if (strpos($field, '.') !== false) {
 448:             return $field;
 449:         }
 450: 
 451:         return $this->getAlias() . '.' . $field;
 452:     }
 453: 
 454:     /**
 455:      * Sets the table registry key used to create this table instance.
 456:      *
 457:      * @param string $registryAlias The key used to access this object.
 458:      * @return $this
 459:      */
 460:     public function setRegistryAlias($registryAlias)
 461:     {
 462:         $this->_registryAlias = $registryAlias;
 463: 
 464:         return $this;
 465:     }
 466: 
 467:     /**
 468:      * Returns the table registry key used to create this table instance.
 469:      *
 470:      * @return string
 471:      */
 472:     public function getRegistryAlias()
 473:     {
 474:         if ($this->_registryAlias === null) {
 475:             $this->_registryAlias = $this->getAlias();
 476:         }
 477: 
 478:         return $this->_registryAlias;
 479:     }
 480: 
 481:     /**
 482:      * Returns the table registry key used to create this table instance or sets one.
 483:      *
 484:      * @deprecated 3.4.0 Use setRegistryAlias()/getRegistryAlias() instead.
 485:      * @param string|null $registryAlias the key used to access this object
 486:      * @return string
 487:      */
 488:     public function registryAlias($registryAlias = null)
 489:     {
 490:         deprecationWarning(
 491:             get_called_class() . '::registryAlias() is deprecated. ' .
 492:             'Use setRegistryAlias()/getRegistryAlias() instead.'
 493:         );
 494:         if ($registryAlias !== null) {
 495:             $this->setRegistryAlias($registryAlias);
 496:         }
 497: 
 498:         return $this->getRegistryAlias();
 499:     }
 500: 
 501:     /**
 502:      * Sets the connection instance.
 503:      *
 504:      * @param \Cake\Database\Connection $connection The connection instance
 505:      * @return $this
 506:      */
 507:     public function setConnection(ConnectionInterface $connection)
 508:     {
 509:         $this->_connection = $connection;
 510: 
 511:         return $this;
 512:     }
 513: 
 514:     /**
 515:      * Returns the connection instance.
 516:      *
 517:      * @return \Cake\Database\Connection
 518:      */
 519:     public function getConnection()
 520:     {
 521:         return $this->_connection;
 522:     }
 523: 
 524:     /**
 525:      * Returns the connection instance or sets a new one
 526:      *
 527:      * @deprecated 3.4.0 Use setConnection()/getConnection() instead.
 528:      * @param \Cake\Datasource\ConnectionInterface|null $connection The new connection instance
 529:      * @return \Cake\Datasource\ConnectionInterface
 530:      */
 531:     public function connection(ConnectionInterface $connection = null)
 532:     {
 533:         deprecationWarning(
 534:             get_called_class() . '::connection() is deprecated. ' .
 535:             'Use setConnection()/getConnection() instead.'
 536:         );
 537:         if ($connection !== null) {
 538:             $this->setConnection($connection);
 539:         }
 540: 
 541:         return $this->getConnection();
 542:     }
 543: 
 544:     /**
 545:      * Returns the schema table object describing this table's properties.
 546:      *
 547:      * @return \Cake\Database\Schema\TableSchema
 548:      */
 549:     public function getSchema()
 550:     {
 551:         if ($this->_schema === null) {
 552:             $this->_schema = $this->_initializeSchema(
 553:                 $this->getConnection()
 554:                     ->getSchemaCollection()
 555:                     ->describe($this->getTable())
 556:             );
 557:         }
 558: 
 559:         return $this->_schema;
 560:     }
 561: 
 562:     /**
 563:      * Sets the schema table object describing this table's properties.
 564:      *
 565:      * If an array is passed, a new TableSchema will be constructed
 566:      * out of it and used as the schema for this table.
 567:      *
 568:      * @param array|\Cake\Database\Schema\TableSchema $schema Schema to be used for this table
 569:      * @return $this
 570:      */
 571:     public function setSchema($schema)
 572:     {
 573:         if (is_array($schema)) {
 574:             $constraints = [];
 575: 
 576:             if (isset($schema['_constraints'])) {
 577:                 $constraints = $schema['_constraints'];
 578:                 unset($schema['_constraints']);
 579:             }
 580: 
 581:             $schema = new TableSchema($this->getTable(), $schema);
 582: 
 583:             foreach ($constraints as $name => $value) {
 584:                 $schema->addConstraint($name, $value);
 585:             }
 586:         }
 587: 
 588:         $this->_schema = $schema;
 589: 
 590:         return $this;
 591:     }
 592: 
 593:     /**
 594:      * Returns the schema table object describing this table's properties.
 595:      *
 596:      * If a TableSchema is passed, it will be used for this table
 597:      * instead of the default one.
 598:      *
 599:      * If an array is passed, a new TableSchema will be constructed
 600:      * out of it and used as the schema for this table.
 601:      *
 602:      * @deprecated 3.4.0 Use setSchema()/getSchema() instead.
 603:      * @param array|\Cake\Database\Schema\TableSchema|null $schema New schema to be used for this table
 604:      * @return \Cake\Database\Schema\TableSchema
 605:      */
 606:     public function schema($schema = null)
 607:     {
 608:         deprecationWarning(
 609:             get_called_class() . '::schema() is deprecated. ' .
 610:             'Use setSchema()/getSchema() instead.'
 611:         );
 612:         if ($schema !== null) {
 613:             $this->setSchema($schema);
 614:         }
 615: 
 616:         return $this->getSchema();
 617:     }
 618: 
 619:     /**
 620:      * Override this function in order to alter the schema used by this table.
 621:      * This function is only called after fetching the schema out of the database.
 622:      * If you wish to provide your own schema to this table without touching the
 623:      * database, you can override schema() or inject the definitions though that
 624:      * method.
 625:      *
 626:      * ### Example:
 627:      *
 628:      * ```
 629:      * protected function _initializeSchema(\Cake\Database\Schema\TableSchema $schema) {
 630:      *  $schema->setColumnType('preferences', 'json');
 631:      *  return $schema;
 632:      * }
 633:      * ```
 634:      *
 635:      * @param \Cake\Database\Schema\TableSchema $schema The table definition fetched from database.
 636:      * @return \Cake\Database\Schema\TableSchema the altered schema
 637:      */
 638:     protected function _initializeSchema(TableSchema $schema)
 639:     {
 640:         return $schema;
 641:     }
 642: 
 643:     /**
 644:      * Test to see if a Table has a specific field/column.
 645:      *
 646:      * Delegates to the schema object and checks for column presence
 647:      * using the Schema\Table instance.
 648:      *
 649:      * @param string $field The field to check for.
 650:      * @return bool True if the field exists, false if it does not.
 651:      */
 652:     public function hasField($field)
 653:     {
 654:         $schema = $this->getSchema();
 655: 
 656:         return $schema->getColumn($field) !== null;
 657:     }
 658: 
 659:     /**
 660:      * Sets the primary key field name.
 661:      *
 662:      * @param string|array $key Sets a new name to be used as primary key
 663:      * @return $this
 664:      */
 665:     public function setPrimaryKey($key)
 666:     {
 667:         $this->_primaryKey = $key;
 668: 
 669:         return $this;
 670:     }
 671: 
 672:     /**
 673:      * Returns the primary key field name.
 674:      *
 675:      * @return string|array
 676:      */
 677:     public function getPrimaryKey()
 678:     {
 679:         if ($this->_primaryKey === null) {
 680:             $key = (array)$this->getSchema()->primaryKey();
 681:             if (count($key) === 1) {
 682:                 $key = $key[0];
 683:             }
 684:             $this->_primaryKey = $key;
 685:         }
 686: 
 687:         return $this->_primaryKey;
 688:     }
 689: 
 690:     /**
 691:      * Returns the primary key field name or sets a new one
 692:      *
 693:      * @deprecated 3.4.0 Use setPrimaryKey()/getPrimaryKey() instead.
 694:      * @param string|array|null $key Sets a new name to be used as primary key
 695:      * @return string|array
 696:      */
 697:     public function primaryKey($key = null)
 698:     {
 699:         deprecationWarning(
 700:             get_called_class() . '::primaryKey() is deprecated. ' .
 701:             'Use setPrimaryKey()/getPrimaryKey() instead.'
 702:         );
 703:         if ($key !== null) {
 704:             $this->setPrimaryKey($key);
 705:         }
 706: 
 707:         return $this->getPrimaryKey();
 708:     }
 709: 
 710:     /**
 711:      * Sets the display field.
 712:      *
 713:      * @param string $key Name to be used as display field.
 714:      * @return $this
 715:      */
 716:     public function setDisplayField($key)
 717:     {
 718:         $this->_displayField = $key;
 719: 
 720:         return $this;
 721:     }
 722: 
 723:     /**
 724:      * Returns the display field.
 725:      *
 726:      * @return string
 727:      */
 728:     public function getDisplayField()
 729:     {
 730:         if ($this->_displayField === null) {
 731:             $schema = $this->getSchema();
 732:             $primary = (array)$this->getPrimaryKey();
 733:             $this->_displayField = array_shift($primary);
 734:             if ($schema->getColumn('title')) {
 735:                 $this->_displayField = 'title';
 736:             }
 737:             if ($schema->getColumn('name')) {
 738:                 $this->_displayField = 'name';
 739:             }
 740:         }
 741: 
 742:         return $this->_displayField;
 743:     }
 744: 
 745:     /**
 746:      * Returns the display field or sets a new one
 747:      *
 748:      * @deprecated 3.4.0 Use setDisplayField()/getDisplayField() instead.
 749:      * @param string|null $key sets a new name to be used as display field
 750:      * @return string
 751:      */
 752:     public function displayField($key = null)
 753:     {
 754:         deprecationWarning(
 755:             get_called_class() . '::displayField() is deprecated. ' .
 756:             'Use setDisplayField()/getDisplayField() instead.'
 757:         );
 758:         if ($key !== null) {
 759:             $this->setDisplayField($key);
 760: 
 761:             return $key;
 762:         }
 763: 
 764:         return $this->getDisplayField();
 765:     }
 766: 
 767:     /**
 768:      * Returns the class used to hydrate rows for this table.
 769:      *
 770:      * @return string
 771:      */
 772:     public function getEntityClass()
 773:     {
 774:         if (!$this->_entityClass) {
 775:             $default = Entity::class;
 776:             $self = get_called_class();
 777:             $parts = explode('\\', $self);
 778: 
 779:             if ($self === __CLASS__ || count($parts) < 3) {
 780:                 return $this->_entityClass = $default;
 781:             }
 782: 
 783:             $alias = Inflector::classify(Inflector::underscore(substr(array_pop($parts), 0, -5)));
 784:             $name = implode('\\', array_slice($parts, 0, -1)) . '\\Entity\\' . $alias;
 785:             if (!class_exists($name)) {
 786:                 return $this->_entityClass = $default;
 787:             }
 788: 
 789:             $class = App::className($name, 'Model/Entity');
 790:             if (!$class) {
 791:                 throw new MissingEntityException([$name]);
 792:             }
 793: 
 794:             $this->_entityClass = $class;
 795:         }
 796: 
 797:         return $this->_entityClass;
 798:     }
 799: 
 800:     /**
 801:      * Sets the class used to hydrate rows for this table.
 802:      *
 803:      * @param string $name The name of the class to use
 804:      * @throws \Cake\ORM\Exception\MissingEntityException when the entity class cannot be found
 805:      * @return $this
 806:      */
 807:     public function setEntityClass($name)
 808:     {
 809:         $class = App::className($name, 'Model/Entity');
 810:         if (!$class) {
 811:             throw new MissingEntityException([$name]);
 812:         }
 813: 
 814:         $this->_entityClass = $class;
 815: 
 816:         return $this;
 817:     }
 818: 
 819:     /**
 820:      * Returns the class used to hydrate rows for this table or sets
 821:      * a new one
 822:      *
 823:      * @deprecated 3.4.0 Use setEntityClass()/getEntityClass() instead.
 824:      * @param string|null $name The name of the class to use
 825:      * @throws \Cake\ORM\Exception\MissingEntityException when the entity class cannot be found
 826:      * @return string
 827:      */
 828:     public function entityClass($name = null)
 829:     {
 830:         deprecationWarning(
 831:             get_called_class() . '::entityClass() is deprecated. ' .
 832:             'Use setEntityClass()/getEntityClass() instead.'
 833:         );
 834:         if ($name !== null) {
 835:             $this->setEntityClass($name);
 836:         }
 837: 
 838:         return $this->getEntityClass();
 839:     }
 840: 
 841:     /**
 842:      * Add a behavior.
 843:      *
 844:      * Adds a behavior to this table's behavior collection. Behaviors
 845:      * provide an easy way to create horizontally re-usable features
 846:      * that can provide trait like functionality, and allow for events
 847:      * to be listened to.
 848:      *
 849:      * Example:
 850:      *
 851:      * Load a behavior, with some settings.
 852:      *
 853:      * ```
 854:      * $this->addBehavior('Tree', ['parent' => 'parentId']);
 855:      * ```
 856:      *
 857:      * Behaviors are generally loaded during Table::initialize().
 858:      *
 859:      * @param string $name The name of the behavior. Can be a short class reference.
 860:      * @param array $options The options for the behavior to use.
 861:      * @return $this
 862:      * @throws \RuntimeException If a behavior is being reloaded.
 863:      * @see \Cake\ORM\Behavior
 864:      */
 865:     public function addBehavior($name, array $options = [])
 866:     {
 867:         $this->_behaviors->load($name, $options);
 868: 
 869:         return $this;
 870:     }
 871: 
 872:     /**
 873:      * Adds an array of behaviors to the table's behavior collection.
 874:      *
 875:      * Example:
 876:      *
 877:      * ```
 878:      * $this->addBehaviors([
 879:      *      'Timestamp',
 880:      *      'Tree' => ['level' => 'level'],
 881:      * ]);
 882:      * ```
 883:      *
 884:      * @param array $behaviors All of the behaviors to load.
 885:      * @return $this
 886:      * @throws \RuntimeException If a behavior is being reloaded.
 887:      */
 888:     public function addBehaviors(array $behaviors)
 889:     {
 890:         foreach ($behaviors as $name => $options) {
 891:             if (is_int($name)) {
 892:                 $name = $options;
 893:                 $options = [];
 894:             }
 895: 
 896:             $this->addBehavior($name, $options);
 897:         }
 898: 
 899:         return $this;
 900:     }
 901: 
 902:     /**
 903:      * Removes a behavior from this table's behavior registry.
 904:      *
 905:      * Example:
 906:      *
 907:      * Remove a behavior from this table.
 908:      *
 909:      * ```
 910:      * $this->removeBehavior('Tree');
 911:      * ```
 912:      *
 913:      * @param string $name The alias that the behavior was added with.
 914:      * @return $this
 915:      * @see \Cake\ORM\Behavior
 916:      */
 917:     public function removeBehavior($name)
 918:     {
 919:         $this->_behaviors->unload($name);
 920: 
 921:         return $this;
 922:     }
 923: 
 924:     /**
 925:      * Returns the behavior registry for this table.
 926:      *
 927:      * @return \Cake\ORM\BehaviorRegistry The BehaviorRegistry instance.
 928:      */
 929:     public function behaviors()
 930:     {
 931:         return $this->_behaviors;
 932:     }
 933: 
 934:     /**
 935:      * Get a behavior from the registry.
 936:      *
 937:      * @param string $name The behavior alias to get from the registry.
 938:      * @return \Cake\ORM\Behavior
 939:      * @throws \InvalidArgumentException If the behavior does not exist.
 940:      */
 941:     public function getBehavior($name)
 942:     {
 943:         /** @var \Cake\ORM\Behavior $behavior */
 944:         $behavior = $this->_behaviors->get($name);
 945:         if ($behavior === null) {
 946:             throw new InvalidArgumentException(sprintf(
 947:                 'The %s behavior is not defined on %s.',
 948:                 $name,
 949:                 get_class($this)
 950:             ));
 951:         }
 952: 
 953:         return $behavior;
 954:     }
 955: 
 956:     /**
 957:      * Check if a behavior with the given alias has been loaded.
 958:      *
 959:      * @param string $name The behavior alias to check.
 960:      * @return bool Whether or not the behavior exists.
 961:      */
 962:     public function hasBehavior($name)
 963:     {
 964:         return $this->_behaviors->has($name);
 965:     }
 966: 
 967:     /**
 968:      * Returns an association object configured for the specified alias if any.
 969:      *
 970:      * @deprecated 3.6.0 Use getAssociation() and Table::hasAssociation() instead.
 971:      * @param string $name the alias used for the association.
 972:      * @return \Cake\ORM\Association|null Either the association or null.
 973:      */
 974:     public function association($name)
 975:     {
 976:         deprecationWarning('Use Table::getAssociation() and Table::hasAssociation() instead.');
 977: 
 978:         return $this->findAssociation($name);
 979:     }
 980: 
 981:     /**
 982:      * Returns an association object configured for the specified alias.
 983:      *
 984:      * The name argument also supports dot syntax to access deeper associations.
 985:      *
 986:      * ```
 987:      * $users = $this->getAssociation('Articles.Comments.Users');
 988:      * ```
 989:      *
 990:      * Note that this method requires the association to be present or otherwise
 991:      * throws an exception.
 992:      * If you are not sure, use hasAssociation() before calling this method.
 993:      *
 994:      * @param string $name The alias used for the association.
 995:      * @return \Cake\ORM\Association The association.
 996:      * @throws \InvalidArgumentException
 997:      */
 998:     public function getAssociation($name)
 999:     {
1000:         $association = $this->findAssociation($name);
1001:         if (!$association) {
1002:             throw new InvalidArgumentException("The {$name} association is not defined on {$this->getAlias()}.");
1003:         }
1004: 
1005:         return $association;
1006:     }
1007: 
1008:     /**
1009:      * Checks whether a specific association exists on this Table instance.
1010:      *
1011:      * The name argument also supports dot syntax to access deeper associations.
1012:      *
1013:      * ```
1014:      * $hasUsers = $this->hasAssociation('Articles.Comments.Users');
1015:      * ```
1016:      *
1017:      * @param string $name The alias used for the association.
1018:      * @return bool
1019:      */
1020:     public function hasAssociation($name)
1021:     {
1022:         return $this->findAssociation($name) !== null;
1023:     }
1024: 
1025:     /**
1026:      * Returns an association object configured for the specified alias if any.
1027:      *
1028:      * The name argument also supports dot syntax to access deeper associations.
1029:      *
1030:      * ```
1031:      * $users = $this->getAssociation('Articles.Comments.Users');
1032:      * ```
1033:      *
1034:      * @param string $name The alias used for the association.
1035:      * @return \Cake\ORM\Association|null Either the association or null.
1036:      */
1037:     protected function findAssociation($name)
1038:     {
1039:         if (strpos($name, '.') === false) {
1040:             return $this->_associations->get($name);
1041:         }
1042: 
1043:         list($name, $next) = array_pad(explode('.', $name, 2), 2, null);
1044:         $result = $this->_associations->get($name);
1045: 
1046:         if ($result !== null && $next !== null) {
1047:             $result = $result->getTarget()->getAssociation($next);
1048:         }
1049: 
1050:         return $result;
1051:     }
1052: 
1053:     /**
1054:      * Get the associations collection for this table.
1055:      *
1056:      * @return \Cake\ORM\AssociationCollection|\Cake\ORM\Association[] The collection of association objects.
1057:      */
1058:     public function associations()
1059:     {
1060:         return $this->_associations;
1061:     }
1062: 
1063:     /**
1064:      * Setup multiple associations.
1065:      *
1066:      * It takes an array containing set of table names indexed by association type
1067:      * as argument:
1068:      *
1069:      * ```
1070:      * $this->Posts->addAssociations([
1071:      *   'belongsTo' => [
1072:      *     'Users' => ['className' => 'App\Model\Table\UsersTable']
1073:      *   ],
1074:      *   'hasMany' => ['Comments'],
1075:      *   'belongsToMany' => ['Tags']
1076:      * ]);
1077:      * ```
1078:      *
1079:      * Each association type accepts multiple associations where the keys
1080:      * are the aliases, and the values are association config data. If numeric
1081:      * keys are used the values will be treated as association aliases.
1082:      *
1083:      * @param array $params Set of associations to bind (indexed by association type)
1084:      * @return $this
1085:      * @see \Cake\ORM\Table::belongsTo()
1086:      * @see \Cake\ORM\Table::hasOne()
1087:      * @see \Cake\ORM\Table::hasMany()
1088:      * @see \Cake\ORM\Table::belongsToMany()
1089:      */
1090:     public function addAssociations(array $params)
1091:     {
1092:         foreach ($params as $assocType => $tables) {
1093:             foreach ($tables as $associated => $options) {
1094:                 if (is_numeric($associated)) {
1095:                     $associated = $options;
1096:                     $options = [];
1097:                 }
1098:                 $this->{$assocType}($associated, $options);
1099:             }
1100:         }
1101: 
1102:         return $this;
1103:     }
1104: 
1105:     /**
1106:      * Creates a new BelongsTo association between this table and a target
1107:      * table. A "belongs to" association is a N-1 relationship where this table
1108:      * is the N side, and where there is a single associated record in the target
1109:      * table for each one in this table.
1110:      *
1111:      * Target table can be inferred by its name, which is provided in the
1112:      * first argument, or you can either pass the to be instantiated or
1113:      * an instance of it directly.
1114:      *
1115:      * The options array accept the following keys:
1116:      *
1117:      * - className: The class name of the target table object
1118:      * - targetTable: An instance of a table object to be used as the target table
1119:      * - foreignKey: The name of the field to use as foreign key, if false none
1120:      *   will be used
1121:      * - conditions: array with a list of conditions to filter the join with
1122:      * - joinType: The type of join to be used (e.g. INNER)
1123:      * - strategy: The loading strategy to use. 'join' and 'select' are supported.
1124:      * - finder: The finder method to use when loading records from this association.
1125:      *   Defaults to 'all'. When the strategy is 'join', only the fields, containments,
1126:      *   and where conditions will be used from the finder.
1127:      *
1128:      * This method will return the association object that was built.
1129:      *
1130:      * @param string $associated the alias for the target table. This is used to
1131:      * uniquely identify the association
1132:      * @param array $options list of options to configure the association definition
1133:      * @return \Cake\ORM\Association\BelongsTo
1134:      */
1135:     public function belongsTo($associated, array $options = [])
1136:     {
1137:         $options += ['sourceTable' => $this];
1138: 
1139:         /** @var \Cake\ORM\Association\BelongsTo $association */
1140:         $association = $this->_associations->load(BelongsTo::class, $associated, $options);
1141: 
1142:         return $association;
1143:     }
1144: 
1145:     /**
1146:      * Creates a new HasOne association between this table and a target
1147:      * table. A "has one" association is a 1-1 relationship.
1148:      *
1149:      * Target table can be inferred by its name, which is provided in the
1150:      * first argument, or you can either pass the class name to be instantiated or
1151:      * an instance of it directly.
1152:      *
1153:      * The options array accept the following keys:
1154:      *
1155:      * - className: The class name of the target table object
1156:      * - targetTable: An instance of a table object to be used as the target table
1157:      * - foreignKey: The name of the field to use as foreign key, if false none
1158:      *   will be used
1159:      * - dependent: Set to true if you want CakePHP to cascade deletes to the
1160:      *   associated table when an entity is removed on this table. The delete operation
1161:      *   on the associated table will not cascade further. To get recursive cascades enable
1162:      *   `cascadeCallbacks` as well. Set to false if you don't want CakePHP to remove
1163:      *   associated data, or when you are using database constraints.
1164:      * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
1165:      *   cascaded deletes. If false the ORM will use deleteAll() to remove data.
1166:      *   When true records will be loaded and then deleted.
1167:      * - conditions: array with a list of conditions to filter the join with
1168:      * - joinType: The type of join to be used (e.g. LEFT)
1169:      * - strategy: The loading strategy to use. 'join' and 'select' are supported.
1170:      * - finder: The finder method to use when loading records from this association.
1171:      *   Defaults to 'all'. When the strategy is 'join', only the fields, containments,
1172:      *   and where conditions will be used from the finder.
1173:      *
1174:      * This method will return the association object that was built.
1175:      *
1176:      * @param string $associated the alias for the target table. This is used to
1177:      * uniquely identify the association
1178:      * @param array $options list of options to configure the association definition
1179:      * @return \Cake\ORM\Association\HasOne
1180:      */
1181:     public function hasOne($associated, array $options = [])
1182:     {
1183:         $options += ['sourceTable' => $this];
1184: 
1185:         /** @var \Cake\ORM\Association\HasOne $association */
1186:         $association = $this->_associations->load(HasOne::class, $associated, $options);
1187: 
1188:         return $association;
1189:     }
1190: 
1191:     /**
1192:      * Creates a new HasMany association between this table and a target
1193:      * table. A "has many" association is a 1-N relationship.
1194:      *
1195:      * Target table can be inferred by its name, which is provided in the
1196:      * first argument, or you can either pass the class name to be instantiated or
1197:      * an instance of it directly.
1198:      *
1199:      * The options array accept the following keys:
1200:      *
1201:      * - className: The class name of the target table object
1202:      * - targetTable: An instance of a table object to be used as the target table
1203:      * - foreignKey: The name of the field to use as foreign key, if false none
1204:      *   will be used
1205:      * - dependent: Set to true if you want CakePHP to cascade deletes to the
1206:      *   associated table when an entity is removed on this table. The delete operation
1207:      *   on the associated table will not cascade further. To get recursive cascades enable
1208:      *   `cascadeCallbacks` as well. Set to false if you don't want CakePHP to remove
1209:      *   associated data, or when you are using database constraints.
1210:      * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
1211:      *   cascaded deletes. If false the ORM will use deleteAll() to remove data.
1212:      *   When true records will be loaded and then deleted.
1213:      * - conditions: array with a list of conditions to filter the join with
1214:      * - sort: The order in which results for this association should be returned
1215:      * - saveStrategy: Either 'append' or 'replace'. When 'append' the current records
1216:      *   are appended to any records in the database. When 'replace' associated records
1217:      *   not in the current set will be removed. If the foreign key is a null able column
1218:      *   or if `dependent` is true records will be orphaned.
1219:      * - strategy: The strategy to be used for selecting results Either 'select'
1220:      *   or 'subquery'. If subquery is selected the query used to return results
1221:      *   in the source table will be used as conditions for getting rows in the
1222:      *   target table.
1223:      * - finder: The finder method to use when loading records from this association.
1224:      *   Defaults to 'all'.
1225:      *
1226:      * This method will return the association object that was built.
1227:      *
1228:      * @param string $associated the alias for the target table. This is used to
1229:      * uniquely identify the association
1230:      * @param array $options list of options to configure the association definition
1231:      * @return \Cake\ORM\Association\HasMany
1232:      */
1233:     public function hasMany($associated, array $options = [])
1234:     {
1235:         $options += ['sourceTable' => $this];
1236: 
1237:         /** @var \Cake\ORM\Association\HasMany $association */
1238:         $association = $this->_associations->load(HasMany::class, $associated, $options);
1239: 
1240:         return $association;
1241:     }
1242: 
1243:     /**
1244:      * Creates a new BelongsToMany association between this table and a target
1245:      * table. A "belongs to many" association is a M-N relationship.
1246:      *
1247:      * Target table can be inferred by its name, which is provided in the
1248:      * first argument, or you can either pass the class name to be instantiated or
1249:      * an instance of it directly.
1250:      *
1251:      * The options array accept the following keys:
1252:      *
1253:      * - className: The class name of the target table object.
1254:      * - targetTable: An instance of a table object to be used as the target table.
1255:      * - foreignKey: The name of the field to use as foreign key.
1256:      * - targetForeignKey: The name of the field to use as the target foreign key.
1257:      * - joinTable: The name of the table representing the link between the two
1258:      * - through: If you choose to use an already instantiated link table, set this
1259:      *   key to a configured Table instance containing associations to both the source
1260:      *   and target tables in this association.
1261:      * - dependent: Set to false, if you do not want junction table records removed
1262:      *   when an owning record is removed.
1263:      * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
1264:      *   cascaded deletes. If false the ORM will use deleteAll() to remove data.
1265:      *   When true join/junction table records will be loaded and then deleted.
1266:      * - conditions: array with a list of conditions to filter the join with.
1267:      * - sort: The order in which results for this association should be returned.
1268:      * - strategy: The strategy to be used for selecting results Either 'select'
1269:      *   or 'subquery'. If subquery is selected the query used to return results
1270:      *   in the source table will be used as conditions for getting rows in the
1271:      *   target table.
1272:      * - saveStrategy: Either 'append' or 'replace'. Indicates the mode to be used
1273:      *   for saving associated entities. The former will only create new links
1274:      *   between both side of the relation and the latter will do a wipe and
1275:      *   replace to create the links between the passed entities when saving.
1276:      * - strategy: The loading strategy to use. 'select' and 'subquery' are supported.
1277:      * - finder: The finder method to use when loading records from this association.
1278:      *   Defaults to 'all'.
1279:      *
1280:      * This method will return the association object that was built.
1281:      *
1282:      * @param string $associated the alias for the target table. This is used to
1283:      * uniquely identify the association
1284:      * @param array $options list of options to configure the association definition
1285:      * @return \Cake\ORM\Association\BelongsToMany
1286:      */
1287:     public function belongsToMany($associated, array $options = [])
1288:     {
1289:         $options += ['sourceTable' => $this];
1290: 
1291:         /** @var \Cake\ORM\Association\BelongsToMany $association */
1292:         $association = $this->_associations->load(BelongsToMany::class, $associated, $options);
1293: 
1294:         return $association;
1295:     }
1296: 
1297:     /**
1298:      * Creates a new Query for this repository and applies some defaults based on the
1299:      * type of search that was selected.
1300:      *
1301:      * ### Model.beforeFind event
1302:      *
1303:      * Each find() will trigger a `Model.beforeFind` event for all attached
1304:      * listeners. Any listener can set a valid result set using $query
1305:      *
1306:      * By default, `$options` will recognize the following keys:
1307:      *
1308:      * - fields
1309:      * - conditions
1310:      * - order
1311:      * - limit
1312:      * - offset
1313:      * - page
1314:      * - group
1315:      * - having
1316:      * - contain
1317:      * - join
1318:      *
1319:      * ### Usage
1320:      *
1321:      * Using the options array:
1322:      *
1323:      * ```
1324:      * $query = $articles->find('all', [
1325:      *   'conditions' => ['published' => 1],
1326:      *   'limit' => 10,
1327:      *   'contain' => ['Users', 'Comments']
1328:      * ]);
1329:      * ```
1330:      *
1331:      * Using the builder interface:
1332:      *
1333:      * ```
1334:      * $query = $articles->find()
1335:      *   ->where(['published' => 1])
1336:      *   ->limit(10)
1337:      *   ->contain(['Users', 'Comments']);
1338:      * ```
1339:      *
1340:      * ### Calling finders
1341:      *
1342:      * The find() method is the entry point for custom finder methods.
1343:      * You can invoke a finder by specifying the type:
1344:      *
1345:      * ```
1346:      * $query = $articles->find('published');
1347:      * ```
1348:      *
1349:      * Would invoke the `findPublished` method.
1350:      *
1351:      * @param string $type the type of query to perform
1352:      * @param array|\ArrayAccess $options An array that will be passed to Query::applyOptions()
1353:      * @return \Cake\ORM\Query The query builder
1354:      */
1355:     public function find($type = 'all', $options = [])
1356:     {
1357:         $query = $this->query();
1358:         $query->select();
1359: 
1360:         return $this->callFinder($type, $query, $options);
1361:     }
1362: 
1363:     /**
1364:      * Returns the query as passed.
1365:      *
1366:      * By default findAll() applies no conditions, you
1367:      * can override this method in subclasses to modify how `find('all')` works.
1368:      *
1369:      * @param \Cake\ORM\Query $query The query to find with
1370:      * @param array $options The options to use for the find
1371:      * @return \Cake\ORM\Query The query builder
1372:      */
1373:     public function findAll(Query $query, array $options)
1374:     {
1375:         return $query;
1376:     }
1377: 
1378:     /**
1379:      * Sets up a query object so results appear as an indexed array, useful for any
1380:      * place where you would want a list such as for populating input select boxes.
1381:      *
1382:      * When calling this finder, the fields passed are used to determine what should
1383:      * be used as the array key, value and optionally what to group the results by.
1384:      * By default the primary key for the model is used for the key, and the display
1385:      * field as value.
1386:      *
1387:      * The results of this finder will be in the following form:
1388:      *
1389:      * ```
1390:      * [
1391:      *  1 => 'value for id 1',
1392:      *  2 => 'value for id 2',
1393:      *  4 => 'value for id 4'
1394:      * ]
1395:      * ```
1396:      *
1397:      * You can specify which property will be used as the key and which as value
1398:      * by using the `$options` array, when not specified, it will use the results
1399:      * of calling `primaryKey` and `displayField` respectively in this table:
1400:      *
1401:      * ```
1402:      * $table->find('list', [
1403:      *  'keyField' => 'name',
1404:      *  'valueField' => 'age'
1405:      * ]);
1406:      * ```
1407:      *
1408:      * Results can be put together in bigger groups when they share a property, you
1409:      * can customize the property to use for grouping by setting `groupField`:
1410:      *
1411:      * ```
1412:      * $table->find('list', [
1413:      *  'groupField' => 'category_id',
1414:      * ]);
1415:      * ```
1416:      *
1417:      * When using a `groupField` results will be returned in this format:
1418:      *
1419:      * ```
1420:      * [
1421:      *  'group_1' => [
1422:      *      1 => 'value for id 1',
1423:      *      2 => 'value for id 2',
1424:      *  ]
1425:      *  'group_2' => [
1426:      *      4 => 'value for id 4'
1427:      *  ]
1428:      * ]
1429:      * ```
1430:      *
1431:      * @param \Cake\ORM\Query $query The query to find with
1432:      * @param array $options The options for the find
1433:      * @return \Cake\ORM\Query The query builder
1434:      */
1435:     public function findList(Query $query, array $options)
1436:     {
1437:         $options += [
1438:             'keyField' => $this->getPrimaryKey(),
1439:             'valueField' => $this->getDisplayField(),
1440:             'groupField' => null
1441:         ];
1442: 
1443:         if (isset($options['idField'])) {
1444:             $options['keyField'] = $options['idField'];
1445:             unset($options['idField']);
1446:             deprecationWarning('Option "idField" is deprecated, use "keyField" instead.');
1447:         }
1448: 
1449:         if (!$query->clause('select') &&
1450:             !is_object($options['keyField']) &&
1451:             !is_object($options['valueField']) &&
1452:             !is_object($options['groupField'])
1453:         ) {
1454:             $fields = array_merge(
1455:                 (array)$options['keyField'],
1456:                 (array)$options['valueField'],
1457:                 (array)$options['groupField']
1458:             );
1459:             $columns = $this->getSchema()->columns();
1460:             if (count($fields) === count(array_intersect($fields, $columns))) {
1461:                 $query->select($fields);
1462:             }
1463:         }
1464: 
1465:         $options = $this->_setFieldMatchers(
1466:             $options,
1467:             ['keyField', 'valueField', 'groupField']
1468:         );
1469: 
1470:         return $query->formatResults(function ($results) use ($options) {
1471:             /** @var \Cake\Collection\CollectionInterface $results */
1472:             return $results->combine(
1473:                 $options['keyField'],
1474:                 $options['valueField'],
1475:                 $options['groupField']
1476:             );
1477:         });
1478:     }
1479: 
1480:     /**
1481:      * Results for this finder will be a nested array, and is appropriate if you want
1482:      * to use the parent_id field of your model data to build nested results.
1483:      *
1484:      * Values belonging to a parent row based on their parent_id value will be
1485:      * recursively nested inside the parent row values using the `children` property
1486:      *
1487:      * You can customize what fields are used for nesting results, by default the
1488:      * primary key and the `parent_id` fields are used. If you wish to change
1489:      * these defaults you need to provide the keys `keyField`, `parentField` or `nestingKey` in
1490:      * `$options`:
1491:      *
1492:      * ```
1493:      * $table->find('threaded', [
1494:      *  'keyField' => 'id',
1495:      *  'parentField' => 'ancestor_id'
1496:      *  'nestingKey' => 'children'
1497:      * ]);
1498:      * ```
1499:      *
1500:      * @param \Cake\ORM\Query $query The query to find with
1501:      * @param array $options The options to find with
1502:      * @return \Cake\ORM\Query The query builder
1503:      */
1504:     public function findThreaded(Query $query, array $options)
1505:     {
1506:         $options += [
1507:             'keyField' => $this->getPrimaryKey(),
1508:             'parentField' => 'parent_id',
1509:             'nestingKey' => 'children'
1510:         ];
1511: 
1512:         if (isset($options['idField'])) {
1513:             $options['keyField'] = $options['idField'];
1514:             unset($options['idField']);
1515:             deprecationWarning('Option "idField" is deprecated, use "keyField" instead.');
1516:         }
1517: 
1518:         $options = $this->_setFieldMatchers($options, ['keyField', 'parentField']);
1519: 
1520:         return $query->formatResults(function ($results) use ($options) {
1521:             /** @var \Cake\Collection\CollectionInterface $results */
1522:             return $results->nest($options['keyField'], $options['parentField'], $options['nestingKey']);
1523:         });
1524:     }
1525: 
1526:     /**
1527:      * Out of an options array, check if the keys described in `$keys` are arrays
1528:      * and change the values for closures that will concatenate the each of the
1529:      * properties in the value array when passed a row.
1530:      *
1531:      * This is an auxiliary function used for result formatters that can accept
1532:      * composite keys when comparing values.
1533:      *
1534:      * @param array $options the original options passed to a finder
1535:      * @param array $keys the keys to check in $options to build matchers from
1536:      * the associated value
1537:      * @return array
1538:      */
1539:     protected function _setFieldMatchers($options, $keys)
1540:     {
1541:         foreach ($keys as $field) {
1542:             if (!is_array($options[$field])) {
1543:                 continue;
1544:             }
1545: 
1546:             if (count($options[$field]) === 1) {
1547:                 $options[$field] = current($options[$field]);
1548:                 continue;
1549:             }
1550: 
1551:             $fields = $options[$field];
1552:             $options[$field] = function ($row) use ($fields) {
1553:                 $matches = [];
1554:                 foreach ($fields as $field) {
1555:                     $matches[] = $row[$field];
1556:                 }
1557: 
1558:                 return implode(';', $matches);
1559:             };
1560:         }
1561: 
1562:         return $options;
1563:     }
1564: 
1565:     /**
1566:      * {@inheritDoc}
1567:      *
1568:      * ### Usage
1569:      *
1570:      * Get an article and some relationships:
1571:      *
1572:      * ```
1573:      * $article = $articles->get(1, ['contain' => ['Users', 'Comments']]);
1574:      * ```
1575:      *
1576:      * @throws \Cake\Datasource\Exception\InvalidPrimaryKeyException When $primaryKey has an
1577:      *      incorrect number of elements.
1578:      */
1579:     public function get($primaryKey, $options = [])
1580:     {
1581:         $key = (array)$this->getPrimaryKey();
1582:         $alias = $this->getAlias();
1583:         foreach ($key as $index => $keyname) {
1584:             $key[$index] = $alias . '.' . $keyname;
1585:         }
1586:         $primaryKey = (array)$primaryKey;
1587:         if (count($key) !== count($primaryKey)) {
1588:             $primaryKey = $primaryKey ?: [null];
1589:             $primaryKey = array_map(function ($key) {
1590:                 return var_export($key, true);
1591:             }, $primaryKey);
1592: 
1593:             throw new InvalidPrimaryKeyException(sprintf(
1594:                 'Record not found in table "%s" with primary key [%s]',
1595:                 $this->getTable(),
1596:                 implode(', ', $primaryKey)
1597:             ));
1598:         }
1599:         $conditions = array_combine($key, $primaryKey);
1600: 
1601:         $cacheConfig = isset($options['cache']) ? $options['cache'] : false;
1602:         $cacheKey = isset($options['key']) ? $options['key'] : false;
1603:         $finder = isset($options['finder']) ? $options['finder'] : 'all';
1604:         unset($options['key'], $options['cache'], $options['finder']);
1605: 
1606:         $query = $this->find($finder, $options)->where($conditions);
1607: 
1608:         if ($cacheConfig) {
1609:             if (!$cacheKey) {
1610:                 $cacheKey = sprintf(
1611:                     'get:%s.%s%s',
1612:                     $this->getConnection()->configName(),
1613:                     $this->getTable(),
1614:                     json_encode($primaryKey)
1615:                 );
1616:             }
1617:             $query->cache($cacheKey, $cacheConfig);
1618:         }
1619: 
1620:         return $query->firstOrFail();
1621:     }
1622: 
1623:     /**
1624:      * Handles the logic executing of a worker inside a transaction.
1625:      *
1626:      * @param callable $worker The worker that will run inside the transaction.
1627:      * @param bool $atomic Whether to execute the worker inside a database transaction.
1628:      * @return mixed
1629:      */
1630:     protected function _executeTransaction(callable $worker, $atomic = true)
1631:     {
1632:         if ($atomic) {
1633:             return $this->getConnection()->transactional(function () use ($worker) {
1634:                 return $worker();
1635:             });
1636:         }
1637: 
1638:         return $worker();
1639:     }
1640: 
1641:     /**
1642:      * Checks if the caller would have executed a commit on a transaction.
1643:      *
1644:      * @param bool $atomic True if an atomic transaction was used.
1645:      * @param bool $primary True if a primary was used.
1646:      * @return bool Returns true if a transaction was committed.
1647:      */
1648:     protected function _transactionCommitted($atomic, $primary)
1649:     {
1650:         return !$this->getConnection()->inTransaction() && ($atomic || (!$atomic && $primary));
1651:     }
1652: 
1653:     /**
1654:      * Finds an existing record or creates a new one.
1655:      *
1656:      * A find() will be done to locate an existing record using the attributes
1657:      * defined in $search. If records matches the conditions, the first record
1658:      * will be returned.
1659:      *
1660:      * If no record can be found, a new entity will be created
1661:      * with the $search properties. If a callback is provided, it will be
1662:      * called allowing you to define additional default values. The new
1663:      * entity will be saved and returned.
1664:      *
1665:      * If your find conditions require custom order, associations or conditions, then the $search
1666:      * parameter can be a callable that takes the Query as the argument, or a \Cake\ORM\Query object passed
1667:      * as the $search parameter. Allowing you to customize the find results.
1668:      *
1669:      * ### Options
1670:      *
1671:      * The options array is passed to the save method with exception to the following keys:
1672:      *
1673:      * - atomic: Whether to execute the methods for find, save and callbacks inside a database
1674:      *   transaction (default: true)
1675:      * - defaults: Whether to use the search criteria as default values for the new entity (default: true)
1676:      *
1677:      * @param array|callable|\Cake\ORM\Query $search The criteria to find existing
1678:      *   records by. Note that when you pass a query object you'll have to use
1679:      *   the 2nd arg of the method to modify the entity data before saving.
1680:      * @param callable|null $callback A callback that will be invoked for newly
1681:      *   created entities. This callback will be called *before* the entity
1682:      *   is persisted.
1683:      * @param array $options The options to use when saving.
1684:      * @return \Cake\Datasource\EntityInterface An entity.
1685:      */
1686:     public function findOrCreate($search, callable $callback = null, $options = [])
1687:     {
1688:         $options = new ArrayObject($options + [
1689:             'atomic' => true,
1690:             'defaults' => true,
1691:         ]);
1692: 
1693:         $entity = $this->_executeTransaction(function () use ($search, $callback, $options) {
1694:             return $this->_processFindOrCreate($search, $callback, $options->getArrayCopy());
1695:         }, $options['atomic']);
1696: 
1697:         if ($entity && $this->_transactionCommitted($options['atomic'], true)) {
1698:             $this->dispatchEvent('Model.afterSaveCommit', compact('entity', 'options'));
1699:         }
1700: 
1701:         return $entity;
1702:     }
1703: 
1704:     /**
1705:      * Performs the actual find and/or create of an entity based on the passed options.
1706:      *
1707:      * @param array|callable|\Cake\ORM\Query $search The criteria to find an existing record by, or a callable tha will
1708:      *   customize the find query.
1709:      * @param callable|null $callback A callback that will be invoked for newly
1710:      *   created entities. This callback will be called *before* the entity
1711:      *   is persisted.
1712:      * @param array $options The options to use when saving.
1713:      * @return \Cake\Datasource\EntityInterface An entity.
1714:      */
1715:     protected function _processFindOrCreate($search, callable $callback = null, $options = [])
1716:     {
1717:         $query = $this->_getFindOrCreateQuery($search);
1718:         $row = $query->first();
1719:         if ($row !== null) {
1720:             return $row;
1721:         }
1722: 
1723:         $entity = $this->newEntity();
1724:         if ($options['defaults'] && is_array($search)) {
1725:             $entity->set($search, ['guard' => false]);
1726:         }
1727:         if ($callback !== null) {
1728:             $entity = $callback($entity) ?: $entity;
1729:         }
1730:         unset($options['defaults']);
1731: 
1732:         return $this->save($entity, $options) ?: $entity;
1733:     }
1734: 
1735:     /**
1736:      * Gets the query object for findOrCreate().
1737:      *
1738:      * @param array|callable|\Cake\ORM\Query $search The criteria to find existing records by.
1739:      * @return \Cake\ORM\Query
1740:      */
1741:     protected function _getFindOrCreateQuery($search)
1742:     {
1743:         if (is_callable($search)) {
1744:             $query = $this->find();
1745:             $search($query);
1746:         } elseif (is_array($search)) {
1747:             $query = $this->find()->where($search);
1748:         } elseif ($search instanceof Query) {
1749:             $query = $search;
1750:         } else {
1751:             throw new InvalidArgumentException('Search criteria must be an array, callable or Query');
1752:         }
1753: 
1754:         return $query;
1755:     }
1756: 
1757:     /**
1758:      * Creates a new Query instance for a table.
1759:      *
1760:      * @return \Cake\ORM\Query
1761:      */
1762:     public function query()
1763:     {
1764:         return new Query($this->getConnection(), $this);
1765:     }
1766: 
1767:     /**
1768:      * {@inheritDoc}
1769:      */
1770:     public function updateAll($fields, $conditions)
1771:     {
1772:         $query = $this->query();
1773:         $query->update()
1774:             ->set($fields)
1775:             ->where($conditions);
1776:         $statement = $query->execute();
1777:         $statement->closeCursor();
1778: 
1779:         return $statement->rowCount();
1780:     }
1781: 
1782:     /**
1783:      * {@inheritDoc}
1784:      */
1785:     public function deleteAll($conditions)
1786:     {
1787:         $query = $this->query()
1788:             ->delete()
1789:             ->where($conditions);
1790:         $statement = $query->execute();
1791:         $statement->closeCursor();
1792: 
1793:         return $statement->rowCount();
1794:     }
1795: 
1796:     /**
1797:      * {@inheritDoc}
1798:      */
1799:     public function exists($conditions)
1800:     {
1801:         return (bool)count(
1802:             $this->find('all')
1803:             ->select(['existing' => 1])
1804:             ->where($conditions)
1805:             ->limit(1)
1806:             ->disableHydration()
1807:             ->toArray()
1808:         );
1809:     }
1810: 
1811:     /**
1812:      * {@inheritDoc}
1813:      *
1814:      * ### Options
1815:      *
1816:      * The options array accepts the following keys:
1817:      *
1818:      * - atomic: Whether to execute the save and callbacks inside a database
1819:      *   transaction (default: true)
1820:      * - checkRules: Whether or not to check the rules on entity before saving, if the checking
1821:      *   fails, it will abort the save operation. (default:true)
1822:      * - associated: If `true` it will save 1st level associated entities as they are found
1823:      *   in the passed `$entity` whenever the property defined for the association
1824:      *   is marked as dirty. If an array, it will be interpreted as the list of associations
1825:      *   to be saved. It is possible to provide different options for saving on associated
1826:      *   table objects using this key by making the custom options the array value.
1827:      *   If `false` no associated records will be saved. (default: `true`)
1828:      * - checkExisting: Whether or not to check if the entity already exists, assuming that the
1829:      *   entity is marked as not new, and the primary key has been set.
1830:      *
1831:      * ### Events
1832:      *
1833:      * When saving, this method will trigger four events:
1834:      *
1835:      * - Model.beforeRules: Will be triggered right before any rule checking is done
1836:      *   for the passed entity if the `checkRules` key in $options is not set to false.
1837:      *   Listeners will receive as arguments the entity, options array and the operation type.
1838:      *   If the event is stopped the rules check result will be set to the result of the event itself.
1839:      * - Model.afterRules: Will be triggered right after the `checkRules()` method is
1840:      *   called for the entity. Listeners will receive as arguments the entity,
1841:      *   options array, the result of checking the rules and the operation type.
1842:      *   If the event is stopped the checking result will be set to the result of
1843:      *   the event itself.
1844:      * - Model.beforeSave: Will be triggered just before the list of fields to be
1845:      *   persisted is calculated. It receives both the entity and the options as
1846:      *   arguments. The options array is passed as an ArrayObject, so any changes in
1847:      *   it will be reflected in every listener and remembered at the end of the event
1848:      *   so it can be used for the rest of the save operation. Returning false in any
1849:      *   of the listeners will abort the saving process. If the event is stopped
1850:      *   using the event API, the event object's `result` property will be returned.
1851:      *   This can be useful when having your own saving strategy implemented inside a
1852:      *   listener.
1853:      * - Model.afterSave: Will be triggered after a successful insert or save,
1854:      *   listeners will receive the entity and the options array as arguments. The type
1855:      *   of operation performed (insert or update) can be determined by checking the
1856:      *   entity's method `isNew`, true meaning an insert and false an update.
1857:      * - Model.afterSaveCommit: Will be triggered after the transaction is committed
1858:      *   for atomic save, listeners will receive the entity and the options array
1859:      *   as arguments.
1860:      *
1861:      * This method will determine whether the passed entity needs to be
1862:      * inserted or updated in the database. It does that by checking the `isNew`
1863:      * method on the entity. If the entity to be saved returns a non-empty value from
1864:      * its `errors()` method, it will not be saved.
1865:      *
1866:      * ### Saving on associated tables
1867:      *
1868:      * This method will by default persist entities belonging to associated tables,
1869:      * whenever a dirty property matching the name of the property name set for an
1870:      * association in this table. It is possible to control what associations will
1871:      * be saved and to pass additional option for saving them.
1872:      *
1873:      * ```
1874:      * // Only save the comments association
1875:      * $articles->save($entity, ['associated' => ['Comments']]);
1876:      *
1877:      * // Save the company, the employees and related addresses for each of them.
1878:      * // For employees do not check the entity rules
1879:      * $companies->save($entity, [
1880:      *   'associated' => [
1881:      *     'Employees' => [
1882:      *       'associated' => ['Addresses'],
1883:      *       'checkRules' => false
1884:      *     ]
1885:      *   ]
1886:      * ]);
1887:      *
1888:      * // Save no associations
1889:      * $articles->save($entity, ['associated' => false]);
1890:      * ```
1891:      *
1892:      * @param \Cake\Datasource\EntityInterface $entity
1893:      * @param array $options
1894:      * @return \Cake\Datasource\EntityInterface|false
1895:      * @throws \Cake\ORM\Exception\RolledbackTransactionException If the transaction is aborted in the afterSave event.
1896:      */
1897:     public function save(EntityInterface $entity, $options = [])
1898:     {
1899:         if ($options instanceof SaveOptionsBuilder) {
1900:             $options = $options->toArray();
1901:         }
1902: 
1903:         $options = new ArrayObject((array)$options + [
1904:             'atomic' => true,
1905:             'associated' => true,
1906:             'checkRules' => true,
1907:             'checkExisting' => true,
1908:             '_primary' => true
1909:         ]);
1910: 
1911:         if ($entity->hasErrors($options['associated'])) {
1912:             return false;
1913:         }
1914: 
1915:         if ($entity->isNew() === false && !$entity->isDirty()) {
1916:             return $entity;
1917:         }
1918: 
1919:         $success = $this->_executeTransaction(function () use ($entity, $options) {
1920:             return $this->_processSave($entity, $options);
1921:         }, $options['atomic']);
1922: 
1923:         if ($success) {
1924:             if ($this->_transactionCommitted($options['atomic'], $options['_primary'])) {
1925:                 $this->dispatchEvent('Model.afterSaveCommit', compact('entity', 'options'));
1926:             }
1927:             if ($options['atomic'] || $options['_primary']) {
1928:                 $entity->clean();
1929:                 $entity->isNew(false);
1930:                 $entity->setSource($this->getRegistryAlias());
1931:             }
1932:         }
1933: 
1934:         return $success;
1935:     }
1936: 
1937:     /**
1938:      * Try to save an entity or throw a PersistenceFailedException if the application rules checks failed,
1939:      * the entity contains errors or the save was aborted by a callback.
1940:      *
1941:      * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
1942:      * @param array|\ArrayAccess $options The options to use when saving.
1943:      * @return \Cake\Datasource\EntityInterface
1944:      * @throws \Cake\ORM\Exception\PersistenceFailedException When the entity couldn't be saved
1945:      * @see \Cake\ORM\Table::save()
1946:      */
1947:     public function saveOrFail(EntityInterface $entity, $options = [])
1948:     {
1949:         $saved = $this->save($entity, $options);
1950:         if ($saved === false) {
1951:             throw new PersistenceFailedException($entity, ['save']);
1952:         }
1953: 
1954:         return $saved;
1955:     }
1956: 
1957:     /**
1958:      * Performs the actual saving of an entity based on the passed options.
1959:      *
1960:      * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
1961:      * @param \ArrayObject $options the options to use for the save operation
1962:      * @return \Cake\Datasource\EntityInterface|bool
1963:      * @throws \RuntimeException When an entity is missing some of the primary keys.
1964:      * @throws \Cake\ORM\Exception\RolledbackTransactionException If the transaction
1965:      *   is aborted in the afterSave event.
1966:      */
1967:     protected function _processSave($entity, $options)
1968:     {
1969:         $primaryColumns = (array)$this->getPrimaryKey();
1970: 
1971:         if ($options['checkExisting'] && $primaryColumns && $entity->isNew() && $entity->has($primaryColumns)) {
1972:             $alias = $this->getAlias();
1973:             $conditions = [];
1974:             foreach ($entity->extract($primaryColumns) as $k => $v) {
1975:                 $conditions["$alias.$k"] = $v;
1976:             }
1977:             $entity->isNew(!$this->exists($conditions));
1978:         }
1979: 
1980:         $mode = $entity->isNew() ? RulesChecker::CREATE : RulesChecker::UPDATE;
1981:         if ($options['checkRules'] && !$this->checkRules($entity, $mode, $options)) {
1982:             return false;
1983:         }
1984: 
1985:         $options['associated'] = $this->_associations->normalizeKeys($options['associated']);
1986:         $event = $this->dispatchEvent('Model.beforeSave', compact('entity', 'options'));
1987: 
1988:         if ($event->isStopped()) {
1989:             return $event->getResult();
1990:         }
1991: 
1992:         $saved = $this->_associations->saveParents(
1993:             $this,
1994:             $entity,
1995:             $options['associated'],
1996:             ['_primary' => false] + $options->getArrayCopy()
1997:         );
1998: 
1999:         if (!$saved && $options['atomic']) {
2000:             return false;
2001:         }
2002: 
2003:         $data = $entity->extract($this->getSchema()->columns(), true);
2004:         $isNew = $entity->isNew();
2005: 
2006:         if ($isNew) {
2007:             $success = $this->_insert($entity, $data);
2008:         } else {
2009:             $success = $this->_update($entity, $data);
2010:         }
2011: 
2012:         if ($success) {
2013:             $success = $this->_onSaveSuccess($entity, $options);
2014:         }
2015: 
2016:         if (!$success && $isNew) {
2017:             $entity->unsetProperty($this->getPrimaryKey());
2018:             $entity->isNew(true);
2019:         }
2020: 
2021:         return $success ? $entity : false;
2022:     }
2023: 
2024:     /**
2025:      * Handles the saving of children associations and executing the afterSave logic
2026:      * once the entity for this table has been saved successfully.
2027:      *
2028:      * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
2029:      * @param \ArrayObject $options the options to use for the save operation
2030:      * @return bool True on success
2031:      * @throws \Cake\ORM\Exception\RolledbackTransactionException If the transaction
2032:      *   is aborted in the afterSave event.
2033:      */
2034:     protected function _onSaveSuccess($entity, $options)
2035:     {
2036:         $success = $this->_associations->saveChildren(
2037:             $this,
2038:             $entity,
2039:             $options['associated'],
2040:             ['_primary' => false] + $options->getArrayCopy()
2041:         );
2042: 
2043:         if (!$success && $options['atomic']) {
2044:             return false;
2045:         }
2046: 
2047:         $this->dispatchEvent('Model.afterSave', compact('entity', 'options'));
2048: 
2049:         if ($options['atomic'] && !$this->getConnection()->inTransaction()) {
2050:             throw new RolledbackTransactionException(['table' => get_class($this)]);
2051:         }
2052: 
2053:         if (!$options['atomic'] && !$options['_primary']) {
2054:             $entity->clean();
2055:             $entity->isNew(false);
2056:             $entity->setSource($this->getRegistryAlias());
2057:         }
2058: 
2059:         return true;
2060:     }
2061: 
2062:     /**
2063:      * Auxiliary function to handle the insert of an entity's data in the table
2064:      *
2065:      * @param \Cake\Datasource\EntityInterface $entity the subject entity from were $data was extracted
2066:      * @param array $data The actual data that needs to be saved
2067:      * @return \Cake\Datasource\EntityInterface|bool
2068:      * @throws \RuntimeException if not all the primary keys where supplied or could
2069:      * be generated when the table has composite primary keys. Or when the table has no primary key.
2070:      */
2071:     protected function _insert($entity, $data)
2072:     {
2073:         $primary = (array)$this->getPrimaryKey();
2074:         if (empty($primary)) {
2075:             $msg = sprintf(
2076:                 'Cannot insert row in "%s" table, it has no primary key.',
2077:                 $this->getTable()
2078:             );
2079:             throw new RuntimeException($msg);
2080:         }
2081:         $keys = array_fill(0, count($primary), null);
2082:         $id = (array)$this->_newId($primary) + $keys;
2083: 
2084:         // Generate primary keys preferring values in $data.
2085:         $primary = array_combine($primary, $id);
2086:         $primary = array_intersect_key($data, $primary) + $primary;
2087: 
2088:         $filteredKeys = array_filter($primary, function ($v) {
2089:             return $v !== null;
2090:         });
2091:         $data += $filteredKeys;
2092: 
2093:         if (count($primary) > 1) {
2094:             $schema = $this->getSchema();
2095:             foreach ($primary as $k => $v) {
2096:                 if (!isset($data[$k]) && empty($schema->getColumn($k)['autoIncrement'])) {
2097:                     $msg = 'Cannot insert row, some of the primary key values are missing. ';
2098:                     $msg .= sprintf(
2099:                         'Got (%s), expecting (%s)',
2100:                         implode(', ', $filteredKeys + $entity->extract(array_keys($primary))),
2101:                         implode(', ', array_keys($primary))
2102:                     );
2103:                     throw new RuntimeException($msg);
2104:                 }
2105:             }
2106:         }
2107: 
2108:         $success = false;
2109:         if (empty($data)) {
2110:             return $success;
2111:         }
2112: 
2113:         $statement = $this->query()->insert(array_keys($data))
2114:             ->values($data)
2115:             ->execute();
2116: 
2117:         if ($statement->rowCount() !== 0) {
2118:             $success = $entity;
2119:             $entity->set($filteredKeys, ['guard' => false]);
2120:             $schema = $this->getSchema();
2121:             $driver = $this->getConnection()->getDriver();
2122:             foreach ($primary as $key => $v) {
2123:                 if (!isset($data[$key])) {
2124:                     $id = $statement->lastInsertId($this->getTable(), $key);
2125:                     $type = $schema->getColumnType($key);
2126:                     $entity->set($key, Type::build($type)->toPHP($id, $driver));
2127:                     break;
2128:                 }
2129:             }
2130:         }
2131:         $statement->closeCursor();
2132: 
2133:         return $success;
2134:     }
2135: 
2136:     /**
2137:      * Generate a primary key value for a new record.
2138:      *
2139:      * By default, this uses the type system to generate a new primary key
2140:      * value if possible. You can override this method if you have specific requirements
2141:      * for id generation.
2142:      *
2143:      * Note: The ORM will not generate primary key values for composite primary keys.
2144:      * You can overwrite _newId() in your table class.
2145:      *
2146:      * @param array $primary The primary key columns to get a new ID for.
2147:      * @return null|string|array Either null or the primary key value or a list of primary key values.
2148:      */
2149:     protected function _newId($primary)
2150:     {
2151:         if (!$primary || count((array)$primary) > 1) {
2152:             return null;
2153:         }
2154:         $typeName = $this->getSchema()->getColumnType($primary[0]);
2155:         $type = Type::build($typeName);
2156: 
2157:         return $type->newId();
2158:     }
2159: 
2160:     /**
2161:      * Auxiliary function to handle the update of an entity's data in the table
2162:      *
2163:      * @param \Cake\Datasource\EntityInterface $entity the subject entity from were $data was extracted
2164:      * @param array $data The actual data that needs to be saved
2165:      * @return \Cake\Datasource\EntityInterface|bool
2166:      * @throws \InvalidArgumentException When primary key data is missing.
2167:      */
2168:     protected function _update($entity, $data)
2169:     {
2170:         $primaryColumns = (array)$this->getPrimaryKey();
2171:         $primaryKey = $entity->extract($primaryColumns);
2172: 
2173:         $data = array_diff_key($data, $primaryKey);
2174:         if (empty($data)) {
2175:             return $entity;
2176:         }
2177: 
2178:         if (count($primaryColumns) === 0) {
2179:             $entityClass = get_class($entity);
2180:             $table = $this->getTable();
2181:             $message = "Cannot update `$entityClass`. The `$table` has no primary key.";
2182:             throw new InvalidArgumentException($message);
2183:         }
2184: 
2185:         if (!$entity->has($primaryColumns)) {
2186:             $message = 'All primary key value(s) are needed for updating, ';
2187:             $message .= get_class($entity) . ' is missing ' . implode(', ', $primaryColumns);
2188:             throw new InvalidArgumentException($message);
2189:         }
2190: 
2191:         $query = $this->query();
2192:         $statement = $query->update()
2193:             ->set($data)
2194:             ->where($primaryKey)
2195:             ->execute();
2196: 
2197:         $success = false;
2198:         if ($statement->errorCode() === '00000') {
2199:             $success = $entity;
2200:         }
2201:         $statement->closeCursor();
2202: 
2203:         return $success;
2204:     }
2205: 
2206:     /**
2207:      * Persists multiple entities of a table.
2208:      *
2209:      * The records will be saved in a transaction which will be rolled back if
2210:      * any one of the records fails to save due to failed validation or database
2211:      * error.
2212:      *
2213:      * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save.
2214:      * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity.
2215:      * @return bool|\Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface False on failure, entities list on success.
2216:      * @throws \Exception
2217:      */
2218:     public function saveMany($entities, $options = [])
2219:     {
2220:         $isNew = [];
2221:         $cleanup = function ($entities) use (&$isNew) {
2222:             foreach ($entities as $key => $entity) {
2223:                 if (isset($isNew[$key]) && $isNew[$key]) {
2224:                     $entity->unsetProperty($this->getPrimaryKey());
2225:                     $entity->isNew(true);
2226:                 }
2227:             }
2228:         };
2229: 
2230:         try {
2231:             $return = $this->getConnection()
2232:                 ->transactional(function () use ($entities, $options, &$isNew) {
2233:                     foreach ($entities as $key => $entity) {
2234:                         $isNew[$key] = $entity->isNew();
2235:                         if ($this->save($entity, $options) === false) {
2236:                             return false;
2237:                         }
2238:                     }
2239:                 });
2240:         } catch (\Exception $e) {
2241:             $cleanup($entities);
2242: 
2243:             throw $e;
2244:         }
2245: 
2246:         if ($return === false) {
2247:             $cleanup($entities);
2248: 
2249:             return false;
2250:         }
2251: 
2252:         return $entities;
2253:     }
2254: 
2255:     /**
2256:      * {@inheritDoc}
2257:      *
2258:      * For HasMany and HasOne associations records will be removed based on
2259:      * the dependent option. Join table records in BelongsToMany associations
2260:      * will always be removed. You can use the `cascadeCallbacks` option
2261:      * when defining associations to change how associated data is deleted.
2262:      *
2263:      * ### Options
2264:      *
2265:      * - `atomic` Defaults to true. When true the deletion happens within a transaction.
2266:      * - `checkRules` Defaults to true. Check deletion rules before deleting the record.
2267:      *
2268:      * ### Events
2269:      *
2270:      * - `Model.beforeDelete` Fired before the delete occurs. If stopped the delete
2271:      *   will be aborted. Receives the event, entity, and options.
2272:      * - `Model.afterDelete` Fired after the delete has been successful. Receives
2273:      *   the event, entity, and options.
2274:      * - `Model.afterDeleteCommit` Fired after the transaction is committed for
2275:      *   an atomic delete. Receives the event, entity, and options.
2276:      *
2277:      * The options argument will be converted into an \ArrayObject instance
2278:      * for the duration of the callbacks, this allows listeners to modify
2279:      * the options used in the delete operation.
2280:      *
2281:      */
2282:     public function delete(EntityInterface $entity, $options = [])
2283:     {
2284:         $options = new ArrayObject((array)$options + [
2285:             'atomic' => true,
2286:             'checkRules' => true,
2287:             '_primary' => true,
2288:         ]);
2289: 
2290:         $success = $this->_executeTransaction(function () use ($entity, $options) {
2291:             return $this->_processDelete($entity, $options);
2292:         }, $options['atomic']);
2293: 
2294:         if ($success && $this->_transactionCommitted($options['atomic'], $options['_primary'])) {
2295:             $this->dispatchEvent('Model.afterDeleteCommit', [
2296:                 'entity' => $entity,
2297:                 'options' => $options
2298:             ]);
2299:         }
2300: 
2301:         return $success;
2302:     }
2303: 
2304:     /**
2305:      * Try to delete an entity or throw a PersistenceFailedException if the entity is new,
2306:      * has no primary key value, application rules checks failed or the delete was aborted by a callback.
2307:      *
2308:      * @param \Cake\Datasource\EntityInterface $entity The entity to remove.
2309:      * @param array|\ArrayAccess $options The options for the delete.
2310:      * @return bool success
2311:      * @throws \Cake\ORM\Exception\PersistenceFailedException
2312:      * @see \Cake\ORM\Table::delete()
2313:      */
2314:     public function deleteOrFail(EntityInterface $entity, $options = [])
2315:     {
2316:         $deleted = $this->delete($entity, $options);
2317:         if ($deleted === false) {
2318:             throw new PersistenceFailedException($entity, ['delete']);
2319:         }
2320: 
2321:         return $deleted;
2322:     }
2323: 
2324:     /**
2325:      * Perform the delete operation.
2326:      *
2327:      * Will delete the entity provided. Will remove rows from any
2328:      * dependent associations, and clear out join tables for BelongsToMany associations.
2329:      *
2330:      * @param \Cake\Datasource\EntityInterface $entity The entity to delete.
2331:      * @param \ArrayObject $options The options for the delete.
2332:      * @throws \InvalidArgumentException if there are no primary key values of the
2333:      * passed entity
2334:      * @return bool success
2335:      */
2336:     protected function _processDelete($entity, $options)
2337:     {
2338:         if ($entity->isNew()) {
2339:             return false;
2340:         }
2341: 
2342:         $primaryKey = (array)$this->getPrimaryKey();
2343:         if (!$entity->has($primaryKey)) {
2344:             $msg = 'Deleting requires all primary key values.';
2345:             throw new InvalidArgumentException($msg);
2346:         }
2347: 
2348:         if ($options['checkRules'] && !$this->checkRules($entity, RulesChecker::DELETE, $options)) {
2349:             return false;
2350:         }
2351: 
2352:         $event = $this->dispatchEvent('Model.beforeDelete', [
2353:             'entity' => $entity,
2354:             'options' => $options
2355:         ]);
2356: 
2357:         if ($event->isStopped()) {
2358:             return $event->getResult();
2359:         }
2360: 
2361:         $this->_associations->cascadeDelete(
2362:             $entity,
2363:             ['_primary' => false] + $options->getArrayCopy()
2364:         );
2365: 
2366:         $query = $this->query();
2367:         $conditions = (array)$entity->extract($primaryKey);
2368:         $statement = $query->delete()
2369:             ->where($conditions)
2370:             ->execute();
2371: 
2372:         $success = $statement->rowCount() > 0;
2373:         if (!$success) {
2374:             return $success;
2375:         }
2376: 
2377:         $this->dispatchEvent('Model.afterDelete', [
2378:             'entity' => $entity,
2379:             'options' => $options
2380:         ]);
2381: 
2382:         return $success;
2383:     }
2384: 
2385:     /**
2386:      * Returns true if the finder exists for the table
2387:      *
2388:      * @param string $type name of finder to check
2389:      *
2390:      * @return bool
2391:      */
2392:     public function hasFinder($type)
2393:     {
2394:         $finder = 'find' . $type;
2395: 
2396:         return method_exists($this, $finder) || ($this->_behaviors && $this->_behaviors->hasFinder($type));
2397:     }
2398: 
2399:     /**
2400:      * Calls a finder method directly and applies it to the passed query,
2401:      * if no query is passed a new one will be created and returned
2402:      *
2403:      * @param string $type name of the finder to be called
2404:      * @param \Cake\ORM\Query $query The query object to apply the finder options to
2405:      * @param array $options List of options to pass to the finder
2406:      * @return \Cake\ORM\Query
2407:      * @throws \BadMethodCallException
2408:      */
2409:     public function callFinder($type, Query $query, array $options = [])
2410:     {
2411:         $query->applyOptions($options);
2412:         $options = $query->getOptions();
2413:         $finder = 'find' . $type;
2414:         if (method_exists($this, $finder)) {
2415:             return $this->{$finder}($query, $options);
2416:         }
2417: 
2418:         if ($this->_behaviors && $this->_behaviors->hasFinder($type)) {
2419:             return $this->_behaviors->callFinder($type, [$query, $options]);
2420:         }
2421: 
2422:         throw new BadMethodCallException(
2423:             sprintf('Unknown finder method "%s"', $type)
2424:         );
2425:     }
2426: 
2427:     /**
2428:      * Provides the dynamic findBy and findByAll methods.
2429:      *
2430:      * @param string $method The method name that was fired.
2431:      * @param array $args List of arguments passed to the function.
2432:      * @return mixed
2433:      * @throws \BadMethodCallException when there are missing arguments, or when
2434:      *  and & or are combined.
2435:      */
2436:     protected function _dynamicFinder($method, $args)
2437:     {
2438:         $method = Inflector::underscore($method);
2439:         preg_match('/^find_([\w]+)_by_/', $method, $matches);
2440:         if (empty($matches)) {
2441:             // find_by_ is 8 characters.
2442:             $fields = substr($method, 8);
2443:             $findType = 'all';
2444:         } else {
2445:             $fields = substr($method, strlen($matches[0]));
2446:             $findType = Inflector::variable($matches[1]);
2447:         }
2448:         $hasOr = strpos($fields, '_or_');
2449:         $hasAnd = strpos($fields, '_and_');
2450: 
2451:         $makeConditions = function ($fields, $args) {
2452:             $conditions = [];
2453:             if (count($args) < count($fields)) {
2454:                 throw new BadMethodCallException(sprintf(
2455:                     'Not enough arguments for magic finder. Got %s required %s',
2456:                     count($args),
2457:                     count($fields)
2458:                 ));
2459:             }
2460:             foreach ($fields as $field) {
2461:                 $conditions[$this->aliasField($field)] = array_shift($args);
2462:             }
2463: 
2464:             return $conditions;
2465:         };
2466: 
2467:         if ($hasOr !== false && $hasAnd !== false) {
2468:             throw new BadMethodCallException(
2469:                 'Cannot mix "and" & "or" in a magic finder. Use find() instead.'
2470:             );
2471:         }
2472: 
2473:         $conditions = [];
2474:         if ($hasOr === false && $hasAnd === false) {
2475:             $conditions = $makeConditions([$fields], $args);
2476:         } elseif ($hasOr !== false) {
2477:             $fields = explode('_or_', $fields);
2478:             $conditions = [
2479:             'OR' => $makeConditions($fields, $args)
2480:             ];
2481:         } elseif ($hasAnd !== false) {
2482:             $fields = explode('_and_', $fields);
2483:             $conditions = $makeConditions($fields, $args);
2484:         }
2485: 
2486:         return $this->find($findType, [
2487:             'conditions' => $conditions,
2488:         ]);
2489:     }
2490: 
2491:     /**
2492:      * Handles behavior delegation + dynamic finders.
2493:      *
2494:      * If your Table uses any behaviors you can call them as if
2495:      * they were on the table object.
2496:      *
2497:      * @param string $method name of the method to be invoked
2498:      * @param array $args List of arguments passed to the function
2499:      * @return mixed
2500:      * @throws \BadMethodCallException
2501:      */
2502:     public function __call($method, $args)
2503:     {
2504:         if ($this->_behaviors && $this->_behaviors->hasMethod($method)) {
2505:             return $this->_behaviors->call($method, $args);
2506:         }
2507:         if (preg_match('/^find(?:\w+)?By/', $method) > 0) {
2508:             return $this->_dynamicFinder($method, $args);
2509:         }
2510: 
2511:         throw new BadMethodCallException(
2512:             sprintf('Unknown method "%s"', $method)
2513:         );
2514:     }
2515: 
2516:     /**
2517:      * Returns the association named after the passed value if exists, otherwise
2518:      * throws an exception.
2519:      *
2520:      * @param string $property the association name
2521:      * @return \Cake\ORM\Association
2522:      * @throws \RuntimeException if no association with such name exists
2523:      */
2524:     public function __get($property)
2525:     {
2526:         $association = $this->_associations->get($property);
2527:         if (!$association) {
2528:             throw new RuntimeException(sprintf(
2529:                 'Table "%s" is not associated with "%s"',
2530:                 get_class($this),
2531:                 $property
2532:             ));
2533:         }
2534: 
2535:         return $association;
2536:     }
2537: 
2538:     /**
2539:      * Returns whether an association named after the passed value
2540:      * exists for this table.
2541:      *
2542:      * @param string $property the association name
2543:      * @return bool
2544:      */
2545:     public function __isset($property)
2546:     {
2547:         return $this->_associations->has($property);
2548:     }
2549: 
2550:     /**
2551:      * Get the object used to marshal/convert array data into objects.
2552:      *
2553:      * Override this method if you want a table object to use custom
2554:      * marshalling logic.
2555:      *
2556:      * @return \Cake\ORM\Marshaller
2557:      * @see \Cake\ORM\Marshaller
2558:      */
2559:     public function marshaller()
2560:     {
2561:         return new Marshaller($this);
2562:     }
2563: 
2564:     /**
2565:      * {@inheritDoc}
2566:      *
2567:      * By default all the associations on this table will be hydrated. You can
2568:      * limit which associations are built, or include deeper associations
2569:      * using the options parameter:
2570:      *
2571:      * ```
2572:      * $article = $this->Articles->newEntity(
2573:      *   $this->request->getData(),
2574:      *   ['associated' => ['Tags', 'Comments.Users']]
2575:      * );
2576:      * ```
2577:      *
2578:      * You can limit fields that will be present in the constructed entity by
2579:      * passing the `fields` option, which is also accepted for associations:
2580:      *
2581:      * ```
2582:      * $article = $this->Articles->newEntity($this->request->getData(), [
2583:      *  'fields' => ['title', 'body', 'tags', 'comments'],
2584:      *  'associated' => ['Tags', 'Comments.Users' => ['fields' => 'username']]
2585:      * ]
2586:      * );
2587:      * ```
2588:      *
2589:      * The `fields` option lets remove or restrict input data from ending up in
2590:      * the entity. If you'd like to relax the entity's default accessible fields,
2591:      * you can use the `accessibleFields` option:
2592:      *
2593:      * ```
2594:      * $article = $this->Articles->newEntity(
2595:      *   $this->request->getData(),
2596:      *   ['accessibleFields' => ['protected_field' => true]]
2597:      * );
2598:      * ```
2599:      *
2600:      * By default, the data is validated before being passed to the new entity. In
2601:      * the case of invalid fields, those will not be present in the resulting object.
2602:      * The `validate` option can be used to disable validation on the passed data:
2603:      *
2604:      * ```
2605:      * $article = $this->Articles->newEntity(
2606:      *   $this->request->getData(),
2607:      *   ['validate' => false]
2608:      * );
2609:      * ```
2610:      *
2611:      * You can also pass the name of the validator to use in the `validate` option.
2612:      * If `null` is passed to the first param of this function, no validation will
2613:      * be performed.
2614:      *
2615:      * You can use the `Model.beforeMarshal` event to modify request data
2616:      * before it is converted into entities.
2617:      */
2618:     public function newEntity($data = null, array $options = [])
2619:     {
2620:         if ($data === null) {
2621:             $class = $this->getEntityClass();
2622: 
2623:             return new $class([], ['source' => $this->getRegistryAlias()]);
2624:         }
2625:         if (!isset($options['associated'])) {
2626:             $options['associated'] = $this->_associations->keys();
2627:         }
2628:         $marshaller = $this->marshaller();
2629: 
2630:         return $marshaller->one($data, $options);
2631:     }
2632: 
2633:     /**
2634:      * {@inheritDoc}
2635:      *
2636:      * By default all the associations on this table will be hydrated. You can
2637:      * limit which associations are built, or include deeper associations
2638:      * using the options parameter:
2639:      *
2640:      * ```
2641:      * $articles = $this->Articles->newEntities(
2642:      *   $this->request->getData(),
2643:      *   ['associated' => ['Tags', 'Comments.Users']]
2644:      * );
2645:      * ```
2646:      *
2647:      * You can limit fields that will be present in the constructed entities by
2648:      * passing the `fields` option, which is also accepted for associations:
2649:      *
2650:      * ```
2651:      * $articles = $this->Articles->newEntities($this->request->getData(), [
2652:      *  'fields' => ['title', 'body', 'tags', 'comments'],
2653:      *  'associated' => ['Tags', 'Comments.Users' => ['fields' => 'username']]
2654:      *  ]
2655:      * );
2656:      * ```
2657:      *
2658:      * You can use the `Model.beforeMarshal` event to modify request data
2659:      * before it is converted into entities.
2660:      */
2661:     public function newEntities(array $data, array $options = [])
2662:     {
2663:         if (!isset($options['associated'])) {
2664:             $options['associated'] = $this->_associations->keys();
2665:         }
2666:         $marshaller = $this->marshaller();
2667: 
2668:         return $marshaller->many($data, $options);
2669:     }
2670: 
2671:     /**
2672:      * {@inheritDoc}
2673:      *
2674:      * When merging HasMany or BelongsToMany associations, all the entities in the
2675:      * `$data` array will appear, those that can be matched by primary key will get
2676:      * the data merged, but those that cannot, will be discarded.
2677:      *
2678:      * You can limit fields that will be present in the merged entity by
2679:      * passing the `fields` option, which is also accepted for associations:
2680:      *
2681:      * ```
2682:      * $article = $this->Articles->patchEntity($article, $this->request->getData(), [
2683:      *  'fields' => ['title', 'body', 'tags', 'comments'],
2684:      *  'associated' => ['Tags', 'Comments.Users' => ['fields' => 'username']]
2685:      *  ]
2686:      * );
2687:      * ```
2688:      *
2689:      * By default, the data is validated before being passed to the entity. In
2690:      * the case of invalid fields, those will not be assigned to the entity.
2691:      * The `validate` option can be used to disable validation on the passed data:
2692:      *
2693:      * ```
2694:      * $article = $this->patchEntity($article, $this->request->getData(),[
2695:      *  'validate' => false
2696:      * ]);
2697:      * ```
2698:      *
2699:      * You can use the `Model.beforeMarshal` event to modify request data
2700:      * before it is converted into entities.
2701:      *
2702:      * When patching scalar values (null/booleans/string/integer/float), if the property
2703:      * presently has an identical value, the setter will not be called, and the
2704:      * property will not be marked as dirty. This is an optimization to prevent unnecessary field
2705:      * updates when persisting entities.
2706:      */
2707:     public function patchEntity(EntityInterface $entity, array $data, array $options = [])
2708:     {
2709:         if (!isset($options['associated'])) {
2710:             $options['associated'] = $this->_associations->keys();
2711:         }
2712:         $marshaller = $this->marshaller();
2713: 
2714:         return $marshaller->merge($entity, $data, $options);
2715:     }
2716: 
2717:     /**
2718:      * {@inheritDoc}
2719:      *
2720:      * Those entries in `$entities` that cannot be matched to any record in
2721:      * `$data` will be discarded. Records in `$data` that could not be matched will
2722:      * be marshalled as a new entity.
2723:      *
2724:      * When merging HasMany or BelongsToMany associations, all the entities in the
2725:      * `$data` array will appear, those that can be matched by primary key will get
2726:      * the data merged, but those that cannot, will be discarded.
2727:      *
2728:      * You can limit fields that will be present in the merged entities by
2729:      * passing the `fields` option, which is also accepted for associations:
2730:      *
2731:      * ```
2732:      * $articles = $this->Articles->patchEntities($articles, $this->request->getData(), [
2733:      *  'fields' => ['title', 'body', 'tags', 'comments'],
2734:      *  'associated' => ['Tags', 'Comments.Users' => ['fields' => 'username']]
2735:      *  ]
2736:      * );
2737:      * ```
2738:      *
2739:      * You can use the `Model.beforeMarshal` event to modify request data
2740:      * before it is converted into entities.
2741:      */
2742:     public function patchEntities($entities, array $data, array $options = [])
2743:     {
2744:         if (!isset($options['associated'])) {
2745:             $options['associated'] = $this->_associations->keys();
2746:         }
2747:         $marshaller = $this->marshaller();
2748: 
2749:         return $marshaller->mergeMany($entities, $data, $options);
2750:     }
2751: 
2752:     /**
2753:      * Validator method used to check the uniqueness of a value for a column.
2754:      * This is meant to be used with the validation API and not to be called
2755:      * directly.
2756:      *
2757:      * ### Example:
2758:      *
2759:      * ```
2760:      * $validator->add('email', [
2761:      *  'unique' => ['rule' => 'validateUnique', 'provider' => 'table']
2762:      * ])
2763:      * ```
2764:      *
2765:      * Unique validation can be scoped to the value of another column:
2766:      *
2767:      * ```
2768:      * $validator->add('email', [
2769:      *  'unique' => [
2770:      *      'rule' => ['validateUnique', ['scope' => 'site_id']],
2771:      *      'provider' => 'table'
2772:      *  ]
2773:      * ]);
2774:      * ```
2775:      *
2776:      * In the above example, the email uniqueness will be scoped to only rows having
2777:      * the same site_id. Scoping will only be used if the scoping field is present in
2778:      * the data to be validated.
2779:      *
2780:      * @param mixed $value The value of column to be checked for uniqueness.
2781:      * @param array $options The options array, optionally containing the 'scope' key.
2782:      *   May also be the validation context, if there are no options.
2783:      * @param array|null $context Either the validation context or null.
2784:      * @return bool True if the value is unique, or false if a non-scalar, non-unique value was given.
2785:      */
2786:     public function validateUnique($value, array $options, array $context = null)
2787:     {
2788:         if ($context === null) {
2789:             $context = $options;
2790:         }
2791:         $entity = new Entity(
2792:             $context['data'],
2793:             [
2794:                 'useSetters' => false,
2795:                 'markNew' => $context['newRecord'],
2796:                 'source' => $this->getRegistryAlias()
2797:             ]
2798:         );
2799:         $fields = array_merge(
2800:             [$context['field']],
2801:             isset($options['scope']) ? (array)$options['scope'] : []
2802:         );
2803:         $values = $entity->extract($fields);
2804:         foreach ($values as $field) {
2805:             if ($field !== null && !is_scalar($field)) {
2806:                 return false;
2807:             }
2808:         }
2809:         $class = static::IS_UNIQUE_CLASS;
2810:         $rule = new $class($fields, $options);
2811: 
2812:         return $rule($entity, ['repository' => $this]);
2813:     }
2814: 
2815:     /**
2816:      * Get the Model callbacks this table is interested in.
2817:      *
2818:      * By implementing the conventional methods a table class is assumed
2819:      * to be interested in the related event.
2820:      *
2821:      * Override this method if you need to add non-conventional event listeners.
2822:      * Or if you want you table to listen to non-standard events.
2823:      *
2824:      * The conventional method map is:
2825:      *
2826:      * - Model.beforeMarshal => beforeMarshal
2827:      * - Model.buildValidator => buildValidator
2828:      * - Model.beforeFind => beforeFind
2829:      * - Model.beforeSave => beforeSave
2830:      * - Model.afterSave => afterSave
2831:      * - Model.afterSaveCommit => afterSaveCommit
2832:      * - Model.beforeDelete => beforeDelete
2833:      * - Model.afterDelete => afterDelete
2834:      * - Model.afterDeleteCommit => afterDeleteCommit
2835:      * - Model.beforeRules => beforeRules
2836:      * - Model.afterRules => afterRules
2837:      *
2838:      * @return array
2839:      */
2840:     public function implementedEvents()
2841:     {
2842:         $eventMap = [
2843:             'Model.beforeMarshal' => 'beforeMarshal',
2844:             'Model.buildValidator' => 'buildValidator',
2845:             'Model.beforeFind' => 'beforeFind',
2846:             'Model.beforeSave' => 'beforeSave',
2847:             'Model.afterSave' => 'afterSave',
2848:             'Model.afterSaveCommit' => 'afterSaveCommit',
2849:             'Model.beforeDelete' => 'beforeDelete',
2850:             'Model.afterDelete' => 'afterDelete',
2851:             'Model.afterDeleteCommit' => 'afterDeleteCommit',
2852:             'Model.beforeRules' => 'beforeRules',
2853:             'Model.afterRules' => 'afterRules',
2854:         ];
2855:         $events = [];
2856: 
2857:         foreach ($eventMap as $event => $method) {
2858:             if (!method_exists($this, $method)) {
2859:                 continue;
2860:             }
2861:             $events[$event] = $method;
2862:         }
2863: 
2864:         return $events;
2865:     }
2866: 
2867:     /**
2868:      * {@inheritDoc}
2869:      *
2870:      * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
2871:      * @return \Cake\ORM\RulesChecker
2872:      */
2873:     public function buildRules(RulesChecker $rules)
2874:     {
2875:         return $rules;
2876:     }
2877: 
2878:     /**
2879:      * Gets a SaveOptionsBuilder instance.
2880:      *
2881:      * @param array $options Options to parse by the builder.
2882:      * @return \Cake\ORM\SaveOptionsBuilder
2883:      */
2884:     public function getSaveOptionsBuilder(array $options = [])
2885:     {
2886:         return new SaveOptionsBuilder($this, $options);
2887:     }
2888: 
2889:     /**
2890:      * Loads the specified associations in the passed entity or list of entities
2891:      * by executing extra queries in the database and merging the results in the
2892:      * appropriate properties.
2893:      *
2894:      * ### Example:
2895:      *
2896:      * ```
2897:      * $user = $usersTable->get(1);
2898:      * $user = $usersTable->loadInto($user, ['Articles.Tags', 'Articles.Comments']);
2899:      * echo $user->articles[0]->title;
2900:      * ```
2901:      *
2902:      * You can also load associations for multiple entities at once
2903:      *
2904:      * ### Example:
2905:      *
2906:      * ```
2907:      * $users = $usersTable->find()->where([...])->toList();
2908:      * $users = $usersTable->loadInto($users, ['Articles.Tags', 'Articles.Comments']);
2909:      * echo $user[1]->articles[0]->title;
2910:      * ```
2911:      *
2912:      * The properties for the associations to be loaded will be overwritten on each entity.
2913:      *
2914:      * @param \Cake\Datasource\EntityInterface|array $entities a single entity or list of entities
2915:      * @param array $contain A `contain()` compatible array.
2916:      * @see \Cake\ORM\Query::contain()
2917:      * @return \Cake\Datasource\EntityInterface|array
2918:      */
2919:     public function loadInto($entities, array $contain)
2920:     {
2921:         return (new LazyEagerLoader)->loadInto($entities, $contain, $this);
2922:     }
2923: 
2924:     /**
2925:      * {@inheritDoc}
2926:      */
2927:     protected function validationMethodExists($method)
2928:     {
2929:         return method_exists($this, $method) || $this->behaviors()->hasMethod($method);
2930:     }
2931: 
2932:     /**
2933:      * Returns an array that can be used to describe the internal state of this
2934:      * object.
2935:      *
2936:      * @return array
2937:      */
2938:     public function __debugInfo()
2939:     {
2940:         $conn = $this->getConnection();
2941:         $associations = $this->_associations;
2942:         $behaviors = $this->_behaviors;
2943: 
2944:         return [
2945:             'registryAlias' => $this->getRegistryAlias(),
2946:             'table' => $this->getTable(),
2947:             'alias' => $this->getAlias(),
2948:             'entityClass' => $this->getEntityClass(),
2949:             'associations' => $associations ? $associations->keys() : false,
2950:             'behaviors' => $behaviors ? $behaviors->loaded() : false,
2951:             'defaultConnection' => static::defaultConnectionName(),
2952:             'connectionName' => $conn ? $conn->configName() : null
2953:         ];
2954:     }
2955: }
2956: 
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