Table.php 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\ORM;
  16. use BadMethodCallException;
  17. use Cake\Core\App;
  18. use Cake\Database\Schema\Table as Schema;
  19. use Cake\Database\Type;
  20. use Cake\Datasource\EntityInterface;
  21. use Cake\Datasource\RepositoryInterface;
  22. use Cake\Event\EventListenerInterface;
  23. use Cake\Event\EventManager;
  24. use Cake\Event\EventManagerTrait;
  25. use Cake\ORM\AssociationCollection;
  26. use Cake\ORM\Association\BelongsTo;
  27. use Cake\ORM\Association\BelongsToMany;
  28. use Cake\ORM\Association\HasMany;
  29. use Cake\ORM\Association\HasOne;
  30. use Cake\ORM\BehaviorRegistry;
  31. use Cake\ORM\Exception\MissingEntityException;
  32. use Cake\ORM\Marshaller;
  33. use Cake\Utility\Inflector;
  34. use Cake\Validation\Validator;
  35. use RuntimeException;
  36. /**
  37. * Represents a single database table.
  38. *
  39. * Exposes methods for retrieving data out of it, and manages the associations
  40. * this table has to other tables. Multiple instances of this class can be created
  41. * for the same database table with different aliases, this allows you to address
  42. * your database structure in a richer and more expressive way.
  43. *
  44. * ### Retrieving data
  45. *
  46. * The primary way to retrieve data is using Table::find(). See that method
  47. * for more information.
  48. *
  49. * ### Dynamic finders
  50. *
  51. * In addition to the standard find($type) finder methods, CakePHP provides dynamic
  52. * finder methods. These methods allow you to easily set basic conditions up. For example
  53. * to filter users by username you would call
  54. *
  55. * {{{
  56. * $query = $users->findByUsername('mark');
  57. * }}}
  58. *
  59. * You can also combine conditions on multiple fields using either `Or` or `And`:
  60. *
  61. * {{{
  62. * $query = $users->findByUsernameOrEmail('mark', 'mark@example.org');
  63. * }}}
  64. *
  65. * ### Bulk updates/deletes
  66. *
  67. * You can use Table::updateAll() and Table::deleteAll() to do bulk updates/deletes.
  68. * You should be aware that events will *not* be fired for bulk updates/deletes.
  69. *
  70. * ### Callbacks/events
  71. *
  72. * Table objects provide a few callbacks/events you can hook into to augment/replace
  73. * find operations. Each event uses the standard event subsystem in CakePHP
  74. *
  75. * - `beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)`
  76. * Fired before each find operation. By stopping the event and supplying a
  77. * return value you can bypass the find operation entirely. Any changes done
  78. * to the $query instance will be retained for the rest of the find. The
  79. * $primary parameter indicates whether or not this is the root query,
  80. * or an associated query.
  81. *
  82. * - `beforeValidate(Event $event, Entity $entity, ArrayObject $options, Validator $validator)`
  83. * Fired before an entity is validated. By stopping this event, you can abort
  84. * the validate + save operations.
  85. *
  86. * - `afterValidate(Event $event, Entity $entity, ArrayObject $options, Validator $validator)`
  87. * Fired after an entity is validated.
  88. *
  89. * - `beforeSave(Event $event, Entity $entity, ArrayObject $options)`
  90. * Fired before each entity is saved. Stopping this event will abort the save
  91. * operation. When the event is stopped the result of the event will be returned.
  92. *
  93. * - `afterSave(Event $event, Entity $entity, ArrayObject $options)`
  94. * Fired after an entity is saved.
  95. *
  96. * - `beforeDelete(Event $event, Entity $entity, ArrayObject $options)`
  97. * Fired before an entity is deleted. By stopping this event you will abort
  98. * the delete operation.
  99. *
  100. * - `afterDelete(Event $event, Entity $entity, ArrayObject $options)`
  101. * Fired after an entity has been deleted.
  102. *
  103. * @see \Cake\Event\EventManager for reference on the events system.
  104. */
  105. class Table implements RepositoryInterface, EventListenerInterface {
  106. use EventManagerTrait;
  107. /**
  108. * Name of the table as it can be found in the database
  109. *
  110. * @var string
  111. */
  112. protected $_table;
  113. /**
  114. * Human name giving to this particular instance. Multiple objects representing
  115. * the same database table can exist by using different aliases.
  116. *
  117. * @var string
  118. */
  119. protected $_alias;
  120. /**
  121. * Connection instance
  122. *
  123. * @var \Cake\Database\Connection
  124. */
  125. protected $_connection;
  126. /**
  127. * The schema object containing a description of this table fields
  128. *
  129. * @var \Cake\Database\Schema\Table
  130. */
  131. protected $_schema;
  132. /**
  133. * The name of the field that represents the primary key in the table
  134. *
  135. * @var string|array
  136. */
  137. protected $_primaryKey;
  138. /**
  139. * The name of the field that represents a human readable representation of a row
  140. *
  141. * @var string
  142. */
  143. protected $_displayField;
  144. /**
  145. * The associations container for this Table.
  146. *
  147. * @var \Cake\ORM\AssociationCollection
  148. */
  149. protected $_associations;
  150. /**
  151. * BehaviorRegistry for this table
  152. *
  153. * @var \Cake\ORM\BehaviorRegistry
  154. */
  155. protected $_behaviors;
  156. /**
  157. * The name of the class that represent a single row for this table
  158. *
  159. * @var string
  160. */
  161. protected $_entityClass;
  162. /**
  163. * A list of validation objects indexed by name
  164. *
  165. * @var array
  166. */
  167. protected $_validators = [];
  168. /**
  169. * Initializes a new instance
  170. *
  171. * The $config array understands the following keys:
  172. *
  173. * - table: Name of the database table to represent
  174. * - alias: Alias to be assigned to this table (default to table name)
  175. * - connection: The connection instance to use
  176. * - entityClass: The fully namespaced class name of the entity class that will
  177. * represent rows in this table.
  178. * - schema: A \Cake\Database\Schema\Table object or an array that can be
  179. * passed to it.
  180. * - eventManager: An instance of an event manager to use for internal events
  181. * - behaviors: A BehaviorRegistry. Generally not used outside of tests.
  182. * - associations: An AssociationCollection instance.
  183. *
  184. * @param array $config List of options for this table
  185. */
  186. public function __construct(array $config = []) {
  187. if (!empty($config['table'])) {
  188. $this->table($config['table']);
  189. }
  190. if (!empty($config['alias'])) {
  191. $this->alias($config['alias']);
  192. }
  193. if (!empty($config['connection'])) {
  194. $this->connection($config['connection']);
  195. }
  196. if (!empty($config['schema'])) {
  197. $this->schema($config['schema']);
  198. }
  199. if (!empty($config['entityClass'])) {
  200. $this->entityClass($config['entityClass']);
  201. }
  202. $eventManager = $behaviors = $associations = null;
  203. if (!empty($config['eventManager'])) {
  204. $eventManager = $config['eventManager'];
  205. }
  206. if (!empty($config['behaviors'])) {
  207. $behaviors = $config['behaviors'];
  208. }
  209. if (!empty($config['associations'])) {
  210. $associations = $config['associations'];
  211. }
  212. $this->_eventManager = $eventManager ?: new EventManager();
  213. $this->_behaviors = $behaviors ?: new BehaviorRegistry($this);
  214. $this->_associations = $associations ?: new AssociationCollection();
  215. $this->initialize($config);
  216. $this->_eventManager->attach($this);
  217. $this->dispatchEvent('Model.initialize');
  218. }
  219. /**
  220. * Get the default connection name.
  221. *
  222. * This method is used to get the fallback connection name if an
  223. * instance is created through the TableRegistry without a connection.
  224. *
  225. * @return string
  226. * @see \Cake\ORM\TableRegistry::get()
  227. */
  228. public static function defaultConnectionName() {
  229. return 'default';
  230. }
  231. /**
  232. * Initialize a table instance. Called after the constructor.
  233. *
  234. * You can use this method to define associations, attach behaviors
  235. * define validation and do any other initialization logic you need.
  236. *
  237. * {{{
  238. * public function initialize(array $config) {
  239. * $this->belongsTo('Users');
  240. * $this->belongsToMany('Tagging.Tags');
  241. * $this->primaryKey('something_else');
  242. * }
  243. * }}}
  244. *
  245. * @param array $config Configuration options passed to the constructor
  246. * @return void
  247. */
  248. public function initialize(array $config) {
  249. }
  250. /**
  251. * Returns the database table name or sets a new one
  252. *
  253. * @param string|null $table the new table name
  254. * @return string
  255. */
  256. public function table($table = null) {
  257. if ($table !== null) {
  258. $this->_table = $table;
  259. }
  260. if ($this->_table === null) {
  261. $table = namespaceSplit(get_class($this));
  262. $table = substr(end($table), 0, -5);
  263. if (empty($table)) {
  264. $table = $this->alias();
  265. }
  266. $this->_table = Inflector::underscore($table);
  267. }
  268. return $this->_table;
  269. }
  270. /**
  271. * Returns the table alias or sets a new one
  272. *
  273. * @param string|null $alias the new table alias
  274. * @return string
  275. */
  276. public function alias($alias = null) {
  277. if ($alias !== null) {
  278. $this->_alias = $alias;
  279. }
  280. if ($this->_alias === null) {
  281. $alias = namespaceSplit(get_class($this));
  282. $alias = substr(end($alias), 0, -5) ?: $this->_table;
  283. $this->_alias = $alias;
  284. }
  285. return $this->_alias;
  286. }
  287. /**
  288. * Returns the connection instance or sets a new one
  289. *
  290. * @param \Cake\Database\Connection|null $conn The new connection instance
  291. * @return \Cake\Database\Connection
  292. */
  293. public function connection($conn = null) {
  294. if ($conn === null) {
  295. return $this->_connection;
  296. }
  297. return $this->_connection = $conn;
  298. }
  299. /**
  300. * Returns the schema table object describing this table's properties.
  301. *
  302. * If an \Cake\Database\Schema\Table is passed, it will be used for this table
  303. * instead of the default one.
  304. *
  305. * If an array is passed, a new \Cake\Database\Schema\Table will be constructed
  306. * out of it and used as the schema for this table.
  307. *
  308. * @param array|\Cake\Database\Schema\Table|null $schema New schema to be used for this table
  309. * @return \Cake\Database\Schema\Table
  310. */
  311. public function schema($schema = null) {
  312. if ($schema === null) {
  313. if ($this->_schema === null) {
  314. $this->_schema = $this->_initializeSchema(
  315. $this->connection()
  316. ->schemaCollection()
  317. ->describe($this->table())
  318. );
  319. }
  320. return $this->_schema;
  321. }
  322. if (is_array($schema)) {
  323. $constraints = [];
  324. if (isset($schema['_constraints'])) {
  325. $constraints = $schema['_constraints'];
  326. unset($schema['_constraints']);
  327. }
  328. $schema = new Schema($this->table(), $schema);
  329. foreach ($constraints as $name => $value) {
  330. $schema->addConstraint($name, $value);
  331. }
  332. }
  333. return $this->_schema = $schema;
  334. }
  335. /**
  336. * Override this function in order to alter the schema used by this table.
  337. * This function is only called after fetching the schema out of the database.
  338. * If you wish to provide your own schema to this table without touching the
  339. * database, you can override schema() or inject the definitions though that
  340. * method.
  341. *
  342. * ### Example:
  343. *
  344. * {{{
  345. * protected function _initializeSchema(\Cake\Database\Schema\Table $table) {
  346. * $table->columnType('preferences', 'json');
  347. * return $table;
  348. * }
  349. * }}}
  350. *
  351. * @param \Cake\Database\Schema\Table $table The table definition fetched from database.
  352. * @return \Cake\Database\Schema\Table the altered schema
  353. * @api
  354. */
  355. protected function _initializeSchema(Schema $table) {
  356. return $table;
  357. }
  358. /**
  359. * Test to see if a Table has a specific field/column.
  360. *
  361. * Delegates to the schema object and checks for column presence
  362. * using the Schema\Table instance.
  363. *
  364. * @param string $field The field to check for.
  365. * @return bool True if the field exists, false if it does not.
  366. */
  367. public function hasField($field) {
  368. $schema = $this->schema();
  369. return $schema->column($field) !== null;
  370. }
  371. /**
  372. * Returns the primary key field name or sets a new one
  373. *
  374. * @param string|array|null $key sets a new name to be used as primary key
  375. * @return string|array
  376. */
  377. public function primaryKey($key = null) {
  378. if ($key !== null) {
  379. $this->_primaryKey = $key;
  380. }
  381. if ($this->_primaryKey === null) {
  382. $key = (array)$this->schema()->primaryKey();
  383. if (count($key) === 1) {
  384. $key = $key[0];
  385. }
  386. $this->_primaryKey = $key;
  387. }
  388. return $this->_primaryKey;
  389. }
  390. /**
  391. * Returns the display field or sets a new one
  392. *
  393. * @param string|null $key sets a new name to be used as display field
  394. * @return string
  395. */
  396. public function displayField($key = null) {
  397. if ($key !== null) {
  398. $this->_displayField = $key;
  399. }
  400. if ($this->_displayField === null) {
  401. $schema = $this->schema();
  402. $primary = (array)$this->primaryKey();
  403. $this->_displayField = array_shift($primary);
  404. if ($schema->column('title')) {
  405. $this->_displayField = 'title';
  406. }
  407. if ($schema->column('name')) {
  408. $this->_displayField = 'name';
  409. }
  410. }
  411. return $this->_displayField;
  412. }
  413. /**
  414. * Returns the class used to hydrate rows for this table or sets
  415. * a new one
  416. *
  417. * @param string|null $name the name of the class to use
  418. * @throws \Cake\ORM\Exception\MissingEntityException when the entity class cannot be found
  419. * @return string
  420. */
  421. public function entityClass($name = null) {
  422. if ($name === null && !$this->_entityClass) {
  423. $default = '\Cake\ORM\Entity';
  424. $self = get_called_class();
  425. $parts = explode('\\', $self);
  426. if ($self === __CLASS__ || count($parts) < 3) {
  427. return $this->_entityClass = $default;
  428. }
  429. $alias = Inflector::singularize(substr(array_pop($parts), 0, -5));
  430. $name = implode('\\', array_slice($parts, 0, -1)) . '\Entity\\' . $alias;
  431. if (!class_exists($name)) {
  432. return $this->_entityClass = $default;
  433. }
  434. }
  435. if ($name !== null) {
  436. $class = App::className($name, 'Model/Entity');
  437. $this->_entityClass = $class;
  438. }
  439. if (!$this->_entityClass) {
  440. throw new MissingEntityException([$name]);
  441. }
  442. return $this->_entityClass;
  443. }
  444. /**
  445. * Add a behavior.
  446. *
  447. * Adds a behavior to this table's behavior collection. Behaviors
  448. * provide an easy way to create horizontally re-usable features
  449. * that can provide trait like functionality, and allow for events
  450. * to be listened to.
  451. *
  452. * Example:
  453. *
  454. * Load a behavior, with some settings.
  455. *
  456. * {{{
  457. * $this->addBehavior('Tree', ['parent' => 'parentId']);
  458. * }}}
  459. *
  460. * Behaviors are generally loaded during Table::initialize().
  461. *
  462. * @param string $name The name of the behavior. Can be a short class reference.
  463. * @param array $options The options for the behavior to use.
  464. * @return void
  465. * @throws \RuntimeException If a behavior is being reloaded.
  466. * @see \Cake\ORM\Behavior
  467. */
  468. public function addBehavior($name, array $options = []) {
  469. $this->_behaviors->load($name, $options);
  470. }
  471. /**
  472. * Removes a behavior from this table's behavior registry.
  473. *
  474. * Example:
  475. *
  476. * Remove a behavior from this table.
  477. *
  478. * {{{
  479. * $this->removeBehavior('Tree');
  480. * }}}
  481. *
  482. * @param string $name The alias that the behavior was added with.
  483. * @return void
  484. * @see \Cake\ORM\Behavior
  485. */
  486. public function removeBehavior($name) {
  487. $this->_behaviors->unload($name);
  488. }
  489. /**
  490. * Returns the behavior registry for this table.
  491. *
  492. * @return \Cake\ORM\BehaviorRegistry
  493. */
  494. public function behaviors() {
  495. return $this->_behaviors;
  496. }
  497. /**
  498. * Check if a behavior with the given alias has been loaded.
  499. *
  500. * @param string $name The behavior alias to check.
  501. * @return array
  502. */
  503. public function hasBehavior($name) {
  504. return $this->_behaviors->has($name);
  505. }
  506. /**
  507. * Returns an association object configured for the specified alias if any
  508. *
  509. * @param string $name the alias used for the association
  510. * @return \Cake\ORM\Association
  511. */
  512. public function association($name) {
  513. return $this->_associations->get($name);
  514. }
  515. /**
  516. * Get the associations collection for this table.
  517. *
  518. * @return \Cake\ORM\AssociationCollection
  519. */
  520. public function associations() {
  521. return $this->_associations;
  522. }
  523. /**
  524. * Creates a new BelongsTo association between this table and a target
  525. * table. A "belongs to" association is a N-1 relationship where this table
  526. * is the N side, and where there is a single associated record in the target
  527. * table for each one in this table.
  528. *
  529. * Target table can be inferred by its name, which is provided in the
  530. * first argument, or you can either pass the to be instantiated or
  531. * an instance of it directly.
  532. *
  533. * The options array accept the following keys:
  534. *
  535. * - className: The class name of the target table object
  536. * - targetTable: An instance of a table object to be used as the target table
  537. * - foreignKey: The name of the field to use as foreign key, if false none
  538. * will be used
  539. * - conditions: array with a list of conditions to filter the join with
  540. * - joinType: The type of join to be used (e.g. INNER)
  541. *
  542. * This method will return the association object that was built.
  543. *
  544. * @param string $associated the alias for the target table. This is used to
  545. * uniquely identify the association
  546. * @param array $options list of options to configure the association definition
  547. * @return \Cake\ORM\Association\BelongsTo
  548. */
  549. public function belongsTo($associated, array $options = []) {
  550. $options += ['sourceTable' => $this];
  551. $association = new BelongsTo($associated, $options);
  552. return $this->_associations->add($association->name(), $association);
  553. }
  554. /**
  555. * Creates a new HasOne association between this table and a target
  556. * table. A "has one" association is a 1-1 relationship.
  557. *
  558. * Target table can be inferred by its name, which is provided in the
  559. * first argument, or you can either pass the class name to be instantiated or
  560. * an instance of it directly.
  561. *
  562. * The options array accept the following keys:
  563. *
  564. * - className: The class name of the target table object
  565. * - targetTable: An instance of a table object to be used as the target table
  566. * - foreignKey: The name of the field to use as foreign key, if false none
  567. * will be used
  568. * - dependent: Set to true if you want CakePHP to cascade deletes to the
  569. * associated table when an entity is removed on this table. Set to false
  570. * if you don't want CakePHP to remove associated data, for when you are using
  571. * database constraints.
  572. * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
  573. * cascaded deletes. If false the ORM will use deleteAll() to remove data.
  574. * When true records will be loaded and then deleted.
  575. * - conditions: array with a list of conditions to filter the join with
  576. * - joinType: The type of join to be used (e.g. LEFT)
  577. *
  578. * This method will return the association object that was built.
  579. *
  580. * @param string $associated the alias for the target table. This is used to
  581. * uniquely identify the association
  582. * @param array $options list of options to configure the association definition
  583. * @return \Cake\ORM\Association\HasOne
  584. */
  585. public function hasOne($associated, array $options = []) {
  586. $options += ['sourceTable' => $this];
  587. $association = new HasOne($associated, $options);
  588. return $this->_associations->add($association->name(), $association);
  589. }
  590. /**
  591. * Creates a new HasMany association between this table and a target
  592. * table. A "has many" association is a 1-N relationship.
  593. *
  594. * Target table can be inferred by its name, which is provided in the
  595. * first argument, or you can either pass the class name to be instantiated or
  596. * an instance of it directly.
  597. *
  598. * The options array accept the following keys:
  599. *
  600. * - className: The class name of the target table object
  601. * - targetTable: An instance of a table object to be used as the target table
  602. * - foreignKey: The name of the field to use as foreign key, if false none
  603. * will be used
  604. * - dependent: Set to true if you want CakePHP to cascade deletes to the
  605. * associated table when an entity is removed on this table. Set to false
  606. * if you don't want CakePHP to remove associated data, for when you are using
  607. * database constraints.
  608. * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
  609. * cascaded deletes. If false the ORM will use deleteAll() to remove data.
  610. * When true records will be loaded and then deleted.
  611. * - conditions: array with a list of conditions to filter the join with
  612. * - sort: The order in which results for this association should be returned
  613. * - strategy: The strategy to be used for selecting results Either 'select'
  614. * or 'subquery'. If subquery is selected the query used to return results
  615. * in the source table will be used as conditions for getting rows in the
  616. * target table.
  617. *
  618. * This method will return the association object that was built.
  619. *
  620. * @param string $associated the alias for the target table. This is used to
  621. * uniquely identify the association
  622. * @param array $options list of options to configure the association definition
  623. * @return \Cake\ORM\Association\HasMany
  624. */
  625. public function hasMany($associated, array $options = []) {
  626. $options += ['sourceTable' => $this];
  627. $association = new HasMany($associated, $options);
  628. return $this->_associations->add($association->name(), $association);
  629. }
  630. /**
  631. * Creates a new BelongsToMany association between this table and a target
  632. * table. A "belongs to many" association is a M-N relationship.
  633. *
  634. * Target table can be inferred by its name, which is provided in the
  635. * first argument, or you can either pass the class name to be instantiated or
  636. * an instance of it directly.
  637. *
  638. * The options array accept the following keys:
  639. *
  640. * - className: The class name of the target table object.
  641. * - targetTable: An instance of a table object to be used as the target table.
  642. * - foreignKey: The name of the field to use as foreign key.
  643. * - targetForeignKey: The name of the field to use as the target foreign key.
  644. * - joinTable: The name of the table representing the link between the two
  645. * - through: If you choose to use an already instantiated link table, set this
  646. * key to a configured Table instance containing associations to both the source
  647. * and target tables in this association.
  648. * - cascadeCallbacks: Set to true if you want CakePHP to fire callbacks on
  649. * cascaded deletes. If false the ORM will use deleteAll() to remove data.
  650. * When true join/junction table records will be loaded and then deleted.
  651. * - conditions: array with a list of conditions to filter the join with.
  652. * - sort: The order in which results for this association should be returned.
  653. * - strategy: The strategy to be used for selecting results Either 'select'
  654. * or 'subquery'. If subquery is selected the query used to return results
  655. * in the source table will be used as conditions for getting rows in the
  656. * target table.
  657. * - saveStrategy: Either 'append' or 'replace'. Indicates the mode to be used
  658. * for saving associated entities. The former will only create new links
  659. * between both side of the relation and the latter will do a wipe and
  660. * replace to create the links between the passed entities when saving.
  661. *
  662. * This method will return the association object that was built.
  663. *
  664. * @param string $associated the alias for the target table. This is used to
  665. * uniquely identify the association
  666. * @param array $options list of options to configure the association definition
  667. * @return \Cake\ORM\Association\BelongsToMany
  668. */
  669. public function belongsToMany($associated, array $options = []) {
  670. $options += ['sourceTable' => $this];
  671. $association = new BelongsToMany($associated, $options);
  672. return $this->_associations->add($association->name(), $association);
  673. }
  674. /**
  675. * {@inheritDoc}
  676. *
  677. * By default, `$options` will recognize the following keys:
  678. *
  679. * - fields
  680. * - conditions
  681. * - order
  682. * - limit
  683. * - offset
  684. * - page
  685. * - order
  686. * - group
  687. * - having
  688. * - contain
  689. * - join
  690. * @return \Cake\ORM\Query
  691. */
  692. public function find($type = 'all', $options = []) {
  693. $query = $this->query();
  694. $query->select();
  695. return $this->callFinder($type, $query, $options);
  696. }
  697. /**
  698. * Returns the query as passed
  699. *
  700. * @param \Cake\ORM\Query $query The query to find with
  701. * @param array $options The options to use for the find
  702. * @return \Cake\ORM\Query
  703. */
  704. public function findAll(Query $query, array $options) {
  705. return $query;
  706. }
  707. /**
  708. * Sets up a query object so results appear as an indexed array, useful for any
  709. * place where you would want a list such as for populating input select boxes.
  710. *
  711. * When calling this finder, the fields passed are used to determine what should
  712. * be used as the array key, value and optionally what to group the results by.
  713. * By default the primary key for the model is used for the key, and the display
  714. * field as value.
  715. *
  716. * The results of this finder will be in the following form:
  717. *
  718. * {{{
  719. * [
  720. * 1 => 'value for id 1',
  721. * 2 => 'value for id 2',
  722. * 4 => 'value for id 4'
  723. * ]
  724. * }}}
  725. *
  726. * You can specify which property will be used as the key and which as value
  727. * by using the `$options` array, when not specified, it will use the results
  728. * of calling `primaryKey` and `displayField` respectively in this table:
  729. *
  730. * {{{
  731. * $table->find('list', [
  732. * 'idField' => 'name',
  733. * 'valueField' => 'age'
  734. * ]);
  735. * }}}
  736. *
  737. * Results can be put together in bigger groups when they share a property, you
  738. * can customize the property to use for grouping by setting `groupField`:
  739. *
  740. * {{{
  741. * $table->find('list', [
  742. * 'groupField' => 'category_id',
  743. * ]);
  744. * }}}
  745. *
  746. * When using a `groupField` results will be returned in this format:
  747. *
  748. * {{{
  749. * [
  750. * 'group_1' => [
  751. * 1 => 'value for id 1',
  752. * 2 => 'value for id 2',
  753. * ]
  754. * 'group_2' => [
  755. * 4 => 'value for id 4'
  756. * ]
  757. * ]
  758. * }}}
  759. *
  760. * @param \Cake\ORM\Query $query The query to find with
  761. * @param array $options The options for the find
  762. * @return \Cake\ORM\Query
  763. */
  764. public function findList(Query $query, array $options) {
  765. $options += [
  766. 'idField' => $this->primaryKey(),
  767. 'valueField' => $this->displayField(),
  768. 'groupField' => null
  769. ];
  770. $options = $this->_setFieldMatchers(
  771. $options,
  772. ['idField', 'valueField', 'groupField']
  773. );
  774. return $query->formatResults(function ($results) use ($options) {
  775. return $results->combine(
  776. $options['idField'],
  777. $options['valueField'],
  778. $options['groupField']
  779. );
  780. });
  781. }
  782. /**
  783. * Results for this finder will be a nested array, and is appropriate if you want
  784. * to use the parent_id field of your model data to build nested results.
  785. *
  786. * Values belonging to a parent row based on their parent_id value will be
  787. * recursively nested inside the parent row values using the `children` property
  788. *
  789. * You can customize what fields are used for nesting results, by default the
  790. * primary key and the `parent_id` fields are used. If you you wish to change
  791. * these defaults you need to provide the keys `idField` or `parentField` in
  792. * `$options`:
  793. *
  794. * {{{
  795. * $table->find('threaded', [
  796. * 'idField' => 'id',
  797. * 'parentField' => 'ancestor_id'
  798. * ]);
  799. * }}}
  800. *
  801. * @param \Cake\ORM\Query $query The query to find with
  802. * @param array $options The options to find with
  803. * @return \Cake\ORM\Query
  804. */
  805. public function findThreaded(Query $query, array $options) {
  806. $options += [
  807. 'idField' => $this->primaryKey(),
  808. 'parentField' => 'parent_id',
  809. ];
  810. $options = $this->_setFieldMatchers($options, ['idField', 'parentField']);
  811. return $query->formatResults(function ($results) use ($options) {
  812. return $results->nest($options['idField'], $options['parentField']);
  813. });
  814. }
  815. /**
  816. * Out of an options array, check if the keys described in `$keys` are arrays
  817. * and change the values for closures that will concatenate the each of the
  818. * properties in the value array when passed a row.
  819. *
  820. * This is an auxiliary function used for result formatters that can accept
  821. * composite keys when comparing values.
  822. *
  823. * @param array $options the original options passed to a finder
  824. * @param array $keys the keys to check in $options to build matchers from
  825. * the associated value
  826. * @return array
  827. */
  828. protected function _setFieldMatchers($options, $keys) {
  829. foreach ($keys as $field) {
  830. if (!is_array($options[$field])) {
  831. continue;
  832. }
  833. if (count($options[$field]) === 1) {
  834. $options[$field] = current($options[$field]);
  835. continue;
  836. }
  837. $fields = $options[$field];
  838. $options[$field] = function ($row) use ($fields) {
  839. $matches = [];
  840. foreach ($fields as $field) {
  841. $matches[] = $row[$field];
  842. }
  843. return implode(';', $matches);
  844. };
  845. }
  846. return $options;
  847. }
  848. /**
  849. * {@inheritDoc}
  850. *
  851. * @throws \Cake\ORM\Exception\RecordNotFoundException if no record can be found given
  852. * a primary key value.
  853. * @throws \InvalidArgumentException When $primaryKey has an incorrect number of elements.
  854. */
  855. public function get($primaryKey, $options = []) {
  856. $key = (array)$this->primaryKey();
  857. $alias = $this->alias();
  858. foreach ($key as $index => $keyname) {
  859. $key[$index] = $alias . '.' . $keyname;
  860. }
  861. $primaryKey = (array)$primaryKey;
  862. if (count($key) !== count($primaryKey)) {
  863. throw new \InvalidArgumentException(sprintf(
  864. "Incorrect number of primary key values. Expected %d got %d.",
  865. count($key),
  866. count($primaryKey)
  867. ));
  868. }
  869. $conditions = array_combine($key, $primaryKey);
  870. $cacheConfig = isset($options['cache']) ? $options['cache'] : false;
  871. $cacheKey = isset($options['key']) ? $options['key'] : false;
  872. unset($options['key'], $options['cache']);
  873. $query = $this->find('all', $options)->where($conditions);
  874. if ($cacheConfig) {
  875. if (!$cacheKey) {
  876. $cacheKey = sprintf(
  877. "get:%s.%s%s",
  878. $this->connection()->configName(), $this->table(), json_encode($primaryKey)
  879. );
  880. }
  881. $query->cache($cacheKey, $cacheConfig);
  882. }
  883. return $query->firstOrFail();
  884. }
  885. /**
  886. * Finds an existing record or creates a new one.
  887. *
  888. * Using the attributes defined in $search a find() will be done to locate
  889. * an existing record. If that record exists it will be returned. If it does
  890. * not exist, a new entity will be created with the $search properties, and
  891. * the $defaults. When a new entity is created, it will be saved.
  892. *
  893. * @param array $search The criteria to find existing records by.
  894. * @param callable|null $callback A callback that will be invoked for newly
  895. * created entities. This callback will be called *before* the entity
  896. * is persisted.
  897. * @return \Cake\Datasource\EntityInterface An entity.
  898. */
  899. public function findOrCreate($search, callable $callback = null) {
  900. $query = $this->find()->where($search);
  901. $row = $query->first();
  902. if ($row) {
  903. return $row;
  904. }
  905. $entity = $this->newEntity();
  906. $entity->set($search, ['guard' => false]);
  907. if ($callback) {
  908. $callback($entity);
  909. }
  910. return $this->save($entity) ?: $entity;
  911. }
  912. /**
  913. * {@inheritDoc}
  914. */
  915. public function query() {
  916. return new Query($this->connection(), $this);
  917. }
  918. /**
  919. * {@inheritDoc}
  920. */
  921. public function updateAll($fields, $conditions) {
  922. $query = $this->query();
  923. $query->update()
  924. ->set($fields)
  925. ->where($conditions);
  926. $statement = $query->execute();
  927. $success = $statement->rowCount() > 0;
  928. $statement->closeCursor();
  929. return $success;
  930. }
  931. /**
  932. * Returns the validation rules tagged with $name. It is possible to have
  933. * multiple different named validation sets, this is useful when you need
  934. * to use varying rules when saving from different routines in your system.
  935. *
  936. * There are two different ways of creating and naming validation sets: by
  937. * creating a new method inside your own Table subclass, or by building
  938. * the validator object yourself and storing it using this method.
  939. *
  940. * For example, if you wish to create a validation set called 'forSubscription',
  941. * you will need to create a method in your Table subclass as follows:
  942. *
  943. * {{{
  944. * public function validationForSubscription($validator) {
  945. * return $validator
  946. * ->add('email', 'valid-email', ['rule' => 'email'])
  947. * ->add('password', 'valid', ['rule' => 'notEmpty'])
  948. * ->requirePresence('username');
  949. * }
  950. * }}}
  951. *
  952. * Otherwise, you can build the object by yourself and store it in the Table object:
  953. *
  954. * {{{
  955. * $validator = new \Cake\Validation\Validator($table);
  956. * $validator
  957. * ->add('email', 'valid-email', ['rule' => 'email'])
  958. * ->add('password', 'valid', ['rule' => 'notEmpty'])
  959. * ->allowEmpty('bio');
  960. * $table->validator('forSubscription', $validator);
  961. * }}}
  962. *
  963. * You can implement the method in `validationDefault` in your Table subclass
  964. * should you wish to have a validation set that applies in cases where no other
  965. * set is specified.
  966. *
  967. * @param string $name the name of the validation set to return
  968. * @param \Cake\Validation\Validator|null $validator The validator instance to store,
  969. * use null to get a validator.
  970. * @return \Cake\Validation\Validator
  971. */
  972. public function validator($name = 'default', Validator $validator = null) {
  973. if ($validator === null && isset($this->_validators[$name])) {
  974. return $this->_validators[$name];
  975. }
  976. if ($validator === null) {
  977. $validator = new Validator();
  978. $validator = $this->{'validation' . ucfirst($name)}($validator);
  979. }
  980. $validator->provider('table', $this);
  981. return $this->_validators[$name] = $validator;
  982. }
  983. /**
  984. * Returns the default validator object. Subclasses can override this function
  985. * to add a default validation set to the validator object.
  986. *
  987. * @param \Cake\Validation\Validator $validator The validator that can be modified to
  988. * add some rules to it.
  989. * @return \Cake\Validation\Validator
  990. */
  991. public function validationDefault(Validator $validator) {
  992. return $validator;
  993. }
  994. /**
  995. * {@inheritDoc}
  996. */
  997. public function deleteAll($conditions) {
  998. $query = $this->query()
  999. ->delete()
  1000. ->where($conditions);
  1001. $statement = $query->execute();
  1002. $success = $statement->rowCount() > 0;
  1003. $statement->closeCursor();
  1004. return $success;
  1005. }
  1006. /**
  1007. * {@inheritDoc}
  1008. */
  1009. public function exists($conditions) {
  1010. return (bool)count($this->find('all')
  1011. ->select(['existing' => 1])
  1012. ->where($conditions)
  1013. ->limit(1)
  1014. ->hydrate(false)
  1015. ->toArray());
  1016. }
  1017. /**
  1018. * {@inheritDoc}
  1019. *
  1020. * ### Options
  1021. *
  1022. * The options array can receive the following keys:
  1023. *
  1024. * - atomic: Whether to execute the save and callbacks inside a database
  1025. * transaction (default: true)
  1026. * - validate: Whether or not validate the entity before saving, if validation
  1027. * fails, it will abort the save operation. If this key is set to a string value,
  1028. * the validator object registered in this table under the provided name will be
  1029. * used instead of the default one. (default:true)
  1030. * - associated: If true it will save all associated entities as they are found
  1031. * in the passed `$entity` whenever the property defined for the association
  1032. * is marked as dirty. Associated records are saved recursively unless told
  1033. * otherwise. If an array, it will be interpreted as the list of associations
  1034. * to be saved. It is possible to provide different options for saving on associated
  1035. * table objects using this key by making the custom options the array value.
  1036. * If false no associated records will be saved. (default: true)
  1037. *
  1038. * ### Events
  1039. *
  1040. * When saving, this method will trigger four events:
  1041. *
  1042. * - Model.beforeValidate: Will be triggered right before any validation is done
  1043. * for the passed entity if the validate key in $options is not set to false.
  1044. * Listeners will receive as arguments the entity, the options array and the
  1045. * validation object to be used for validating the entity. If the event is
  1046. * stopped the validation result will be set to the result of the event itself.
  1047. * - Model.afterValidate: Will be triggered right after the `validate()` method is
  1048. * called in the entity. Listeners will receive as arguments the entity, the
  1049. * options array and the validation object to be used for validating the entity.
  1050. * If the event is stopped the validation result will be set to the result of
  1051. * the event itself.
  1052. * - Model.beforeSave: Will be triggered just before the list of fields to be
  1053. * persisted is calculated. It receives both the entity and the options as
  1054. * arguments. The options array is passed as an ArrayObject, so any changes in
  1055. * it will be reflected in every listener and remembered at the end of the event
  1056. * so it can be used for the rest of the save operation. Returning false in any
  1057. * of the listeners will abort the saving process. If the event is stopped
  1058. * using the event API, the event object's `result` property will be returned.
  1059. * This can be useful when having your own saving strategy implemented inside a
  1060. * listener.
  1061. * - Model.afterSave: Will be triggered after a successful insert or save,
  1062. * listeners will receive the entity and the options array as arguments. The type
  1063. * of operation performed (insert or update) can be determined by checking the
  1064. * entity's method `isNew`, true meaning an insert and false an update.
  1065. *
  1066. * This method will determine whether the passed entity needs to be
  1067. * inserted or updated in the database. It does that by checking the `isNew`
  1068. * method on the entity.
  1069. *
  1070. * ### Saving on associated tables
  1071. *
  1072. * This method will by default persist entities belonging to associated tables,
  1073. * whenever a dirty property matching the name of the property name set for an
  1074. * association in this table. It is possible to control what associations will
  1075. * be saved and to pass additional option for saving them.
  1076. *
  1077. * {{{
  1078. * // Only save the comments association
  1079. * $articles->save($entity, ['associated' => ['Comments']);
  1080. *
  1081. * // Save the company, the employees and related addresses for each of them.
  1082. * // For employees use the 'special' validation group
  1083. * $companies->save($entity, [
  1084. * 'associated' => [
  1085. * 'Employees' => [
  1086. * 'associated' => ['Addresses'],
  1087. * 'validate' => 'special'
  1088. * ]
  1089. * ]
  1090. * ]);
  1091. *
  1092. * // Save no associations
  1093. * $articles->save($entity, ['associated' => false]);
  1094. * }}}
  1095. *
  1096. */
  1097. public function save(EntityInterface $entity, $options = []) {
  1098. $options = new \ArrayObject($options + [
  1099. 'atomic' => true,
  1100. 'validate' => true,
  1101. 'associated' => true
  1102. ]);
  1103. if ($entity->isNew() === false && !$entity->dirty()) {
  1104. return $entity;
  1105. }
  1106. if ($options['atomic']) {
  1107. $connection = $this->connection();
  1108. $success = $connection->transactional(function () use ($entity, $options) {
  1109. return $this->_processSave($entity, $options);
  1110. });
  1111. } else {
  1112. $success = $this->_processSave($entity, $options);
  1113. }
  1114. return $success;
  1115. }
  1116. /**
  1117. * Performs the actual saving of an entity based on the passed options.
  1118. *
  1119. * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
  1120. * @param array $options the options to use for the save operation
  1121. * @return \Cake\Datasource\EntityInterface|bool
  1122. * @throws \RuntimeException When an entity is missing some of the primary keys.
  1123. */
  1124. protected function _processSave($entity, $options) {
  1125. $primaryColumns = (array)$this->primaryKey();
  1126. if ($primaryColumns && $entity->isNew() && $entity->has($primaryColumns)) {
  1127. $alias = $this->alias();
  1128. $conditions = [];
  1129. foreach ($entity->extract($primaryColumns) as $k => $v) {
  1130. $conditions["$alias.$k"] = $v;
  1131. }
  1132. $entity->isNew(!$this->exists($conditions));
  1133. }
  1134. $options['associated'] = $this->_associations->normalizeKeys($options['associated']);
  1135. $validate = $options['validate'];
  1136. if ($validate && !$this->validate($entity, $options)) {
  1137. return false;
  1138. }
  1139. $event = $this->dispatchEvent('Model.beforeSave', compact('entity', 'options'));
  1140. if ($event->isStopped()) {
  1141. return $event->result;
  1142. }
  1143. $saved = $this->_associations->saveParents(
  1144. $this,
  1145. $entity,
  1146. $options['associated'],
  1147. ['validate' => false] + $options->getArrayCopy()
  1148. );
  1149. if (!$saved && $options['atomic']) {
  1150. return false;
  1151. }
  1152. $data = $entity->extract($this->schema()->columns(), true);
  1153. $isNew = $entity->isNew();
  1154. if ($isNew) {
  1155. $success = $this->_insert($entity, $data);
  1156. } else {
  1157. $success = $this->_update($entity, $data);
  1158. }
  1159. if ($success) {
  1160. $success = $this->_associations->saveChildren(
  1161. $this,
  1162. $entity,
  1163. $options['associated'],
  1164. ['validate' => (bool)$validate] + $options->getArrayCopy()
  1165. );
  1166. if ($success || !$options['atomic']) {
  1167. $entity->clean();
  1168. $this->dispatchEvent('Model.afterSave', compact('entity', 'options'));
  1169. $entity->isNew(false);
  1170. $entity->source($this->alias());
  1171. $success = true;
  1172. }
  1173. }
  1174. if (!$success && $isNew) {
  1175. $entity->unsetProperty($this->primaryKey());
  1176. $entity->isNew(true);
  1177. }
  1178. if ($success) {
  1179. return $entity;
  1180. }
  1181. return false;
  1182. }
  1183. /**
  1184. * Auxiliary function to handle the insert of an entity's data in the table
  1185. *
  1186. * @param \Cake\Datasource\EntityInterface $entity the subject entity from were $data was extracted
  1187. * @param array $data The actual data that needs to be saved
  1188. * @return \Cake\Datasource\EntityInterface|bool
  1189. * @throws \RuntimeException if not all the primary keys where supplied or could
  1190. * be generated when the table has composite primary keys. Or when the table has no primary key.
  1191. */
  1192. protected function _insert($entity, $data) {
  1193. $primary = (array)$this->primaryKey();
  1194. if (empty($primary)) {
  1195. $msg = sprintf(
  1196. 'Cannot insert row in "%s" table, it has no primary key.',
  1197. $this->table()
  1198. );
  1199. throw new \RuntimeException($msg);
  1200. }
  1201. $keys = array_fill(0, count($primary), null);
  1202. $id = (array)$this->_newId($primary) + $keys;
  1203. $primary = array_combine($primary, $id);
  1204. $filteredKeys = array_filter($primary, 'strlen');
  1205. $data = $filteredKeys + $data;
  1206. if (count($primary) > 1) {
  1207. foreach ($primary as $k => $v) {
  1208. if (!isset($data[$k])) {
  1209. $msg = 'Cannot insert row, some of the primary key values are missing. ';
  1210. $msg .= sprintf(
  1211. 'Got (%s), expecting (%s)',
  1212. implode(', ', $filteredKeys + $entity->extract(array_keys($primary))),
  1213. implode(', ', array_keys($primary))
  1214. );
  1215. throw new \RuntimeException($msg);
  1216. }
  1217. }
  1218. }
  1219. $success = false;
  1220. if (empty($data)) {
  1221. return $success;
  1222. }
  1223. $statement = $this->query()->insert(array_keys($data))
  1224. ->values($data)
  1225. ->execute();
  1226. if ($statement->rowCount() !== 0) {
  1227. $success = $entity;
  1228. $entity->set($filteredKeys, ['guard' => false]);
  1229. foreach ($primary as $key => $v) {
  1230. if (!isset($data[$key])) {
  1231. $id = $statement->lastInsertId($this->table(), $key);
  1232. $entity->set($key, $id);
  1233. break;
  1234. }
  1235. }
  1236. }
  1237. $statement->closeCursor();
  1238. return $success;
  1239. }
  1240. /**
  1241. * Generate a primary key value for a new record.
  1242. *
  1243. * By default, this uses the type system to generate a new primary key
  1244. * value if possible. You can override this method if you have specific requirements
  1245. * for id generation.
  1246. *
  1247. * @param array $primary The primary key columns to get a new ID for.
  1248. * @return mixed Either null or the new primary key value.
  1249. */
  1250. protected function _newId($primary) {
  1251. if (!$primary || count((array)$primary) > 1) {
  1252. return null;
  1253. }
  1254. $typeName = $this->schema()->columnType($primary[0]);
  1255. $type = Type::build($typeName);
  1256. return $type->newId();
  1257. }
  1258. /**
  1259. * Auxiliary function to handle the update of an entity's data in the table
  1260. *
  1261. * @param \Cake\Datasource\EntityInterface $entity the subject entity from were $data was extracted
  1262. * @param array $data The actual data that needs to be saved
  1263. * @return \Cake\Datasource\EntityInterface|bool
  1264. * @throws \InvalidArgumentException When primary key data is missing.
  1265. */
  1266. protected function _update($entity, $data) {
  1267. $primaryColumns = (array)$this->primaryKey();
  1268. $primaryKey = $entity->extract($primaryColumns);
  1269. $data = array_diff_key($data, $primaryKey);
  1270. if (empty($data)) {
  1271. return $entity;
  1272. }
  1273. if (!$entity->has($primaryColumns)) {
  1274. $message = 'All primary key value(s) are needed for updating';
  1275. throw new \InvalidArgumentException($message);
  1276. }
  1277. $query = $this->query();
  1278. $statement = $query->update()
  1279. ->set($data)
  1280. ->where($primaryKey)
  1281. ->execute();
  1282. $success = false;
  1283. if ($statement->errorCode() === '00000') {
  1284. $success = $entity;
  1285. }
  1286. $statement->closeCursor();
  1287. return $success;
  1288. }
  1289. /**
  1290. * {@inheritDoc}
  1291. *
  1292. * For HasMany and HasOne associations records will be removed based on
  1293. * the dependent option. Join table records in BelongsToMany associations
  1294. * will always be removed. You can use the `cascadeCallbacks` option
  1295. * when defining associations to change how associated data is deleted.
  1296. *
  1297. * ### Options
  1298. *
  1299. * - `atomic` Defaults to true. When true the deletion happens within a transaction.
  1300. *
  1301. * ### Events
  1302. *
  1303. * - `beforeDelete` Fired before the delete occurs. If stopped the delete
  1304. * will be aborted. Receives the event, entity, and options.
  1305. * - `afterDelete` Fired after the delete has been successful. Receives
  1306. * the event, entity, and options.
  1307. *
  1308. * The options argument will be converted into an \ArrayObject instance
  1309. * for the duration of the callbacks, this allows listeners to modify
  1310. * the options used in the delete operation.
  1311. *
  1312. */
  1313. public function delete(EntityInterface $entity, $options = []) {
  1314. $options = new \ArrayObject($options + ['atomic' => true]);
  1315. $process = function () use ($entity, $options) {
  1316. return $this->_processDelete($entity, $options);
  1317. };
  1318. if ($options['atomic']) {
  1319. return $this->connection()->transactional($process);
  1320. }
  1321. return $process();
  1322. }
  1323. /**
  1324. * Perform the delete operation.
  1325. *
  1326. * Will delete the entity provided. Will remove rows from any
  1327. * dependent associations, and clear out join tables for BelongsToMany associations.
  1328. *
  1329. * @param \Cake\DataSource\EntityInterface $entity The entity to delete.
  1330. * @param \ArrayObject $options The options for the delete.
  1331. * @throws \InvalidArgumentException if there are no primary key values of the
  1332. * passed entity
  1333. * @return bool success
  1334. */
  1335. protected function _processDelete($entity, $options) {
  1336. $event = $this->dispatchEvent('Model.beforeDelete', [
  1337. 'entity' => $entity,
  1338. 'options' => $options
  1339. ]);
  1340. if ($event->isStopped()) {
  1341. return $event->result;
  1342. }
  1343. if ($entity->isNew()) {
  1344. return false;
  1345. }
  1346. $primaryKey = (array)$this->primaryKey();
  1347. if (!$entity->has($primaryKey)) {
  1348. $msg = 'Deleting requires all primary key values.';
  1349. throw new \InvalidArgumentException($msg);
  1350. }
  1351. $this->_associations->cascadeDelete($entity, $options->getArrayCopy());
  1352. $query = $this->query();
  1353. $conditions = (array)$entity->extract($primaryKey);
  1354. $statement = $query->delete()
  1355. ->where($conditions)
  1356. ->execute();
  1357. $success = $statement->rowCount() > 0;
  1358. if (!$success) {
  1359. return $success;
  1360. }
  1361. $this->dispatchEvent('Model.afterDelete', [
  1362. 'entity' => $entity,
  1363. 'options' => $options
  1364. ]);
  1365. return $success;
  1366. }
  1367. /**
  1368. * Calls a finder method directly and applies it to the passed query,
  1369. * if no query is passed a new one will be created and returned
  1370. *
  1371. * @param string $type name of the finder to be called
  1372. * @param \Cake\ORM\Query $query The query object to apply the finder options to
  1373. * @param array $options List of options to pass to the finder
  1374. * @return \Cake\ORM\Query
  1375. * @throws \BadMethodCallException
  1376. */
  1377. public function callFinder($type, Query $query, array $options = []) {
  1378. $query->applyOptions($options);
  1379. $options = $query->getOptions();
  1380. $finder = 'find' . $type;
  1381. if (method_exists($this, $finder)) {
  1382. return $this->{$finder}($query, $options);
  1383. }
  1384. if ($this->_behaviors && $this->_behaviors->hasFinder($type)) {
  1385. return $this->_behaviors->callFinder($type, [$query, $options]);
  1386. }
  1387. throw new \BadMethodCallException(
  1388. sprintf('Unknown finder method "%s"', $type)
  1389. );
  1390. }
  1391. /**
  1392. * Provides the dynamic findBy and findByAll methods.
  1393. *
  1394. * @param string $method The method name that was fired.
  1395. * @param array $args List of arguments passed to the function.
  1396. * @return mixed
  1397. * @throws \BadMethodCallException when there are missing arguments, or when
  1398. * and & or are combined.
  1399. */
  1400. protected function _dynamicFinder($method, $args) {
  1401. $method = Inflector::underscore($method);
  1402. preg_match('/^find_([\w]+)_by_/', $method, $matches);
  1403. if (empty($matches)) {
  1404. // find_by_ is 8 characters.
  1405. $fields = substr($method, 8);
  1406. $findType = 'all';
  1407. } else {
  1408. $fields = substr($method, strlen($matches[0]));
  1409. $findType = Inflector::variable($matches[1]);
  1410. }
  1411. $hasOr = strpos($fields, '_or_');
  1412. $hasAnd = strpos($fields, '_and_');
  1413. $makeConditions = function ($fields, $args) {
  1414. $conditions = [];
  1415. if (count($args) < count($fields)) {
  1416. throw new BadMethodCallException(sprintf(
  1417. 'Not enough arguments for magic finder. Got %s required %s',
  1418. count($args),
  1419. count($fields)
  1420. ));
  1421. }
  1422. foreach ($fields as $field) {
  1423. $conditions[$field] = array_shift($args);
  1424. }
  1425. return $conditions;
  1426. };
  1427. if ($hasOr !== false && $hasAnd !== false) {
  1428. throw new BadMethodCallException(
  1429. 'Cannot mix "and" & "or" in a magic finder. Use find() instead.'
  1430. );
  1431. }
  1432. if ($hasOr === false && $hasAnd === false) {
  1433. $conditions = $makeConditions([$fields], $args);
  1434. } elseif ($hasOr !== false) {
  1435. $fields = explode('_or_', $fields);
  1436. $conditions = [
  1437. 'OR' => $makeConditions($fields, $args)
  1438. ];
  1439. } elseif ($hasAnd !== false) {
  1440. $fields = explode('_and_', $fields);
  1441. $conditions = $makeConditions($fields, $args);
  1442. }
  1443. return $this->find($findType, [
  1444. 'conditions' => $conditions,
  1445. ]);
  1446. }
  1447. /**
  1448. * Handles behavior delegation + dynamic finders.
  1449. *
  1450. * If your Table uses any behaviors you can call them as if
  1451. * they were on the table object.
  1452. *
  1453. * @param string $method name of the method to be invoked
  1454. * @param array $args List of arguments passed to the function
  1455. * @return mixed
  1456. * @throws \BadMethodCallException
  1457. */
  1458. public function __call($method, $args) {
  1459. if ($this->_behaviors && $this->_behaviors->hasMethod($method)) {
  1460. return $this->_behaviors->call($method, $args);
  1461. }
  1462. if (preg_match('/^find(?:\w+)?By/', $method) > 0) {
  1463. return $this->_dynamicFinder($method, $args);
  1464. }
  1465. throw new \BadMethodCallException(
  1466. sprintf('Unknown method "%s"', $method)
  1467. );
  1468. }
  1469. /**
  1470. * Returns the association named after the passed value if exists, otherwise
  1471. * throws an exception.
  1472. *
  1473. * @param string $property the association name
  1474. * @return \Cake\ORM\Association
  1475. * @throws \RuntimeException if no association with such name exists
  1476. */
  1477. public function __get($property) {
  1478. $association = $this->_associations->get($property);
  1479. if (!$association) {
  1480. throw new \RuntimeException(sprintf(
  1481. 'Table "%s" is not associated with "%s"',
  1482. get_class($this),
  1483. $property
  1484. ));
  1485. }
  1486. return $association;
  1487. }
  1488. /**
  1489. * Returns whether an association named after the passed value
  1490. * exists for this table.
  1491. *
  1492. * @param string $property the association name
  1493. * @return bool
  1494. */
  1495. public function __isset($property) {
  1496. return $this->_associations->has($property);
  1497. }
  1498. /**
  1499. * Get the object used to marshal/convert array data into objects.
  1500. *
  1501. * Override this method if you want a table object to use custom
  1502. * marshalling logic.
  1503. *
  1504. * @return \Cake\ORM\Marshaller
  1505. * @see \Cake\ORM\Marshaller
  1506. */
  1507. public function marshaller() {
  1508. return new Marshaller($this);
  1509. }
  1510. /**
  1511. * Returns a new instance of an EntityValidator that is configured to be used
  1512. * for entities generated by this table. An EntityValidator can be used to
  1513. * process validation rules on a single or multiple entities and any of its
  1514. * associated values.
  1515. *
  1516. * @return EntityValidator
  1517. */
  1518. public function entityValidator() {
  1519. return new EntityValidator($this);
  1520. }
  1521. /**
  1522. * {@inheritDoc}
  1523. *
  1524. * By default all the associations on this table will be hydrated. You can
  1525. * limit which associations are built, or include deeper associations
  1526. * using the options parameter:
  1527. *
  1528. * {{{
  1529. * $article = $this->Articles->newEntity(
  1530. * $this->request->data(),
  1531. * ['associated' => ['Tags', 'Comments.Users']]
  1532. * );
  1533. * }}}
  1534. *
  1535. * You can limit fields that will be present in the constructed entity by
  1536. * passing the `fieldList` option, which is also accepted for associations:
  1537. *
  1538. * {{{
  1539. * $article = $this->Articles->newEntity($this->request->data(), [
  1540. * 'fieldList' => ['title', 'body'],
  1541. * 'associated' => ['Tags', 'Comments.Users' => ['fieldList' => 'username']]
  1542. * ]
  1543. * );
  1544. * }}}
  1545. *
  1546. * The `fieldList` option lets remove or restrict input data from ending up in
  1547. * the entity. If you'd like to relax the entity's default accessible fields,
  1548. * you can use the `accessibleFields` option:
  1549. *
  1550. * {{{
  1551. * $article = $this->Articles->newEntity(
  1552. * $this->request->data(),
  1553. * ['accessibleFields' => ['protected_field' => true]]
  1554. * );
  1555. * }}}
  1556. */
  1557. public function newEntity(array $data = [], array $options = []) {
  1558. if (!isset($options['associated'])) {
  1559. $options['associated'] = $this->_associations->keys();
  1560. }
  1561. $marshaller = $this->marshaller();
  1562. return $marshaller->one($data, $options);
  1563. }
  1564. /**
  1565. * {@inheritDoc}
  1566. *
  1567. * By default all the associations on this table will be hydrated. You can
  1568. * limit which associations are built, or include deeper associations
  1569. * using the options parameter:
  1570. *
  1571. * {{{
  1572. * $articles = $this->Articles->newEntities(
  1573. * $this->request->data(),
  1574. * ['associated' => ['Tags', 'Comments.Users']]
  1575. * );
  1576. * }}}
  1577. *
  1578. * You can limit fields that will be present in the constructed entities by
  1579. * passing the `fieldList` option, which is also accepted for associations:
  1580. *
  1581. * {{{
  1582. * $articles = $this->Articles->newEntities($this->request->data(), [
  1583. * 'fieldList' => ['title', 'body'],
  1584. * 'associated' => ['Tags', 'Comments.Users' => ['fieldList' => 'username']]
  1585. * ]
  1586. * );
  1587. * }}}
  1588. *
  1589. */
  1590. public function newEntities(array $data, array $options = []) {
  1591. if (!isset($options['associated'])) {
  1592. $options['associated'] = $this->_associations->keys();
  1593. }
  1594. $marshaller = $this->marshaller();
  1595. return $marshaller->many($data, $options);
  1596. }
  1597. /**
  1598. * {@inheritDoc}
  1599. *
  1600. * When merging HasMany or BelongsToMany associations, all the entities in the
  1601. * `$data` array will appear, those that can be matched by primary key will get
  1602. * the data merged, but those that cannot, will be discarded.
  1603. *
  1604. * You can limit fields that will be present in the merged entity by
  1605. * passing the `fieldList` option, which is also accepted for associations:
  1606. *
  1607. * {{{
  1608. * $articles = $this->Articles->patchEntity($article, $this->request->data(), [
  1609. * 'fieldList' => ['title', 'body'],
  1610. * 'associated' => ['Tags', 'Comments.Users' => ['fieldList' => 'username']]
  1611. * ]
  1612. * );
  1613. * }}}
  1614. */
  1615. public function patchEntity(EntityInterface $entity, array $data, array $options = []) {
  1616. if (!isset($options['associated'])) {
  1617. $options['associated'] = $this->_associations->keys();
  1618. }
  1619. $marshaller = $this->marshaller();
  1620. return $marshaller->merge($entity, $data, $options);
  1621. }
  1622. /**
  1623. * {@inheritDoc}
  1624. *
  1625. * Those entries in `$entities` that cannot be matched to any record in
  1626. * `$data` will be discarded. Records in `$data` that could not be matched will
  1627. * be marshalled as a new entity.
  1628. *
  1629. * When merging HasMany or BelongsToMany associations, all the entities in the
  1630. * `$data` array will appear, those that can be matched by primary key will get
  1631. * the data merged, but those that cannot, will be discarded.
  1632. *
  1633. * You can limit fields that will be present in the merged entities by
  1634. * passing the `fieldList` option, which is also accepted for associations:
  1635. *
  1636. * {{{
  1637. * $articles = $this->Articles->patchEntities($articles, $this->request->data(), [
  1638. * 'fieldList' => ['title', 'body'],
  1639. * 'associated' => ['Tags', 'Comments.Users' => ['fieldList' => 'username']]
  1640. * ]
  1641. * );
  1642. * }}}
  1643. */
  1644. public function patchEntities($entities, array $data, array $options = []) {
  1645. if (!isset($options['associated'])) {
  1646. $options['associated'] = $this->_associations->keys();
  1647. }
  1648. $marshaller = $this->marshaller();
  1649. return $marshaller->mergeMany($entities, $data, $options);
  1650. }
  1651. /**
  1652. * Validates a single entity based on the passed options and validates
  1653. * any nested entity for this table associations as requested in the
  1654. * options array.
  1655. *
  1656. * Calling this function directly is mostly useful when you need to get
  1657. * validation errors for an entity and associated nested entities before
  1658. * they are saved.
  1659. *
  1660. * {{{
  1661. * $articles->validate($article);
  1662. * }}}
  1663. *
  1664. * You can specify which validation set to use using the options array:
  1665. *
  1666. * {{{
  1667. * $users->validate($user, ['validate' => 'forSignup']);
  1668. * }}}
  1669. *
  1670. * By default all the associations on this table will be validated if they can
  1671. * be found in the passed entity. You can limit which associations are built,
  1672. * or include deeper associations using the options parameter
  1673. *
  1674. * {{{
  1675. * $articles->validate($article, [
  1676. * 'associated' => [
  1677. * 'Tags',
  1678. * 'Comments' => [
  1679. * 'validate' => 'myCustomSet',
  1680. * 'associated' => ['Users']
  1681. * ]
  1682. * ]
  1683. * ]);
  1684. * }}}
  1685. *
  1686. * @param \Cake\Datasource\EntityInterface $entity The entity to be validated
  1687. * @param array|\ArrayObject $options A list of options to use while validating, the following
  1688. * keys are accepted:
  1689. * - validate: The name of the validation set to use
  1690. * - associated: map of association names to validate as well
  1691. * @return bool true if the passed entity and its associations are valid
  1692. */
  1693. public function validate($entity, $options = []) {
  1694. if (!isset($options['associated'])) {
  1695. $options['associated'] = $this->_associations->keys();
  1696. }
  1697. $entityValidator = $this->entityValidator();
  1698. return $entityValidator->one($entity, $options);
  1699. }
  1700. /**
  1701. * Validates a list of entities based on the passed options and validates
  1702. * any nested entity for this table associations as requested in the
  1703. * options array.
  1704. *
  1705. * Calling this function directly is mostly useful when you need to get
  1706. * validation errors for a list of entities and associations before they are
  1707. * saved.
  1708. *
  1709. * {{{
  1710. * $articles->validateMany([$article1, $article2]);
  1711. * }}}
  1712. *
  1713. * You can specify which validation set to use using the options array:
  1714. *
  1715. * {{{
  1716. * $users->validateMany([$user1, $user2], ['validate' => 'forSignup']);
  1717. * }}}
  1718. *
  1719. * By default all the associations on this table will be validated if they can
  1720. * be found in the passed entities. You can limit which associations are built,
  1721. * or include deeper associations using the options parameter
  1722. *
  1723. * {{{
  1724. * $articles->validateMany([$article1, $article2], [
  1725. * 'associated' => [
  1726. * 'Tags',
  1727. * 'Comments' => [
  1728. * 'validate' => 'myCustomSet',
  1729. * 'associated' => ['Users']
  1730. * ]
  1731. * ]
  1732. * ]);
  1733. * }}}
  1734. *
  1735. * @param array|\ArrayAccess $entities The entities to be validated
  1736. * @param array $options A list of options to use while validating, the following
  1737. * keys are accepted:
  1738. * - validate: The name of the validation set to use
  1739. * - associated: map of association names to validate as well
  1740. * @return bool true if the passed entities and their associations are valid
  1741. */
  1742. public function validateMany($entities, array $options = []) {
  1743. if (!isset($options['associated'])) {
  1744. $options['associated'] = $this->_associations->keys();
  1745. }
  1746. $entityValidator = $this->entityValidator();
  1747. return $entityValidator->many($entities, $options);
  1748. }
  1749. /**
  1750. * Validator method used to check the uniqueness of a value for a column.
  1751. * This is meant to be used with the validation API and not to be called
  1752. * directly.
  1753. *
  1754. * ### Example:
  1755. *
  1756. * {{{
  1757. * $validator->add('email', [
  1758. * 'unique' => ['rule' => 'validateUnique', 'provider' => 'table']
  1759. * ])
  1760. * }}}
  1761. *
  1762. * Unique validation can be scoped to the value of another column:
  1763. *
  1764. * {{{
  1765. * $validator->add('email', [
  1766. * 'unique' => [
  1767. * 'rule' => ['validateUnique', ['scope' => 'site_id']],
  1768. * 'provider' => 'table'
  1769. * ]
  1770. * ]);
  1771. * }}}
  1772. *
  1773. * In the above example, the email uniqueness will be scoped to only rows having
  1774. * the same site_id. Scoping will only be used if the scoping field is present in
  1775. * the data to be validated.
  1776. *
  1777. * @param mixed $value The value of column to be checked for uniqueness
  1778. * @param array $options The options array, optionally containing the 'scope' key
  1779. * @param array $context The validation context as provided by the validation routine
  1780. * @return bool true if the value is unique
  1781. */
  1782. public function validateUnique($value, array $options, array $context = []) {
  1783. if (empty($context)) {
  1784. $context = $options;
  1785. }
  1786. $conditions = [$context['field'] => $value];
  1787. if (!empty($options['scope']) && isset($context['data'][$options['scope']])) {
  1788. $scope = $options['scope'];
  1789. $scopedValue = $context['data'][$scope];
  1790. $conditions[$scope] = $scopedValue;
  1791. }
  1792. if (!$context['newRecord']) {
  1793. $keys = (array)$this->primaryKey();
  1794. $not = [];
  1795. foreach ($keys as $key) {
  1796. if (isset($context['data'][$key])) {
  1797. $not[$key] = $context['data'][$key];
  1798. }
  1799. }
  1800. $conditions['NOT'] = $not;
  1801. }
  1802. return !$this->exists($conditions);
  1803. }
  1804. /**
  1805. * Get the Model callbacks this table is interested in.
  1806. *
  1807. * By implementing the conventional methods a table class is assumed
  1808. * to be interested in the related event.
  1809. *
  1810. * Override this method if you need to add non-conventional event listeners.
  1811. * Or if you want you table to listen to non-standard events.
  1812. *
  1813. * @return array
  1814. */
  1815. public function implementedEvents() {
  1816. $eventMap = [
  1817. 'Model.beforeFind' => 'beforeFind',
  1818. 'Model.beforeSave' => 'beforeSave',
  1819. 'Model.afterSave' => 'afterSave',
  1820. 'Model.beforeDelete' => 'beforeDelete',
  1821. 'Model.afterDelete' => 'afterDelete',
  1822. 'Model.beforeValidate' => 'beforeValidate',
  1823. 'Model.afterValidate' => 'afterValidate',
  1824. ];
  1825. $events = [];
  1826. foreach ($eventMap as $event => $method) {
  1827. if (!method_exists($this, $method)) {
  1828. continue;
  1829. }
  1830. $events[$event] = $method;
  1831. }
  1832. return $events;
  1833. }
  1834. /**
  1835. * Returns an array that can be used to describe the internal state of this
  1836. * object.
  1837. *
  1838. * @return array
  1839. */
  1840. public function __debugInfo() {
  1841. $conn = $this->connection();
  1842. return [
  1843. 'table' => $this->table(),
  1844. 'alias' => $this->alias(),
  1845. 'entityClass' => $this->entityClass(),
  1846. 'associations' => $this->_associations->keys(),
  1847. 'behaviors' => $this->_behaviors->loaded(),
  1848. 'defaultConnection' => $this->defaultConnectionName(),
  1849. 'connectionName' => $conn ? $conn->configName() : null
  1850. ];
  1851. }
  1852. }