Table.php 52 KB

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