TableTest.php 98 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589
  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\Test\TestCase\ORM;
  16. use Cake\Core\Configure;
  17. use Cake\Database\Expression\OrderByExpression;
  18. use Cake\Database\Expression\QueryExpression;
  19. use Cake\Database\TypeMap;
  20. use Cake\Datasource\ConnectionManager;
  21. use Cake\I18n\Time;
  22. use Cake\ORM\Table;
  23. use Cake\ORM\TableRegistry;
  24. use Cake\Validation\Validator;
  25. /**
  26. * Used to test correct class is instantiated when using TableRegistry::get();
  27. */
  28. class UsersTable extends Table {
  29. }
  30. /**
  31. * Tests Table class
  32. *
  33. */
  34. class TableTest extends \Cake\TestSuite\TestCase {
  35. public $fixtures = [
  36. 'core.users', 'core.categories', 'core.articles', 'core.authors',
  37. 'core.tags', 'core.articles_tags'
  38. ];
  39. /**
  40. * Handy variable containing the next primary key that will be inserted in the
  41. * users table
  42. *
  43. * @var int
  44. */
  45. public static $nextUserId = 5;
  46. public function setUp() {
  47. parent::setUp();
  48. $this->connection = ConnectionManager::get('test');
  49. Configure::write('App.namespace', 'TestApp');
  50. $this->usersTypeMap = new TypeMap([
  51. 'Users.id' => 'integer',
  52. 'id' => 'integer',
  53. 'Users.username' => 'string',
  54. 'username' => 'string',
  55. 'Users.password' => 'string',
  56. 'password' => 'string',
  57. 'Users.created' => 'timestamp',
  58. 'created' => 'timestamp',
  59. 'Users.updated' => 'timestamp',
  60. 'updated' => 'timestamp',
  61. ]);
  62. $this->articlesTypeMap = new TypeMap([
  63. 'Articles.id' => 'integer',
  64. 'id' => 'integer',
  65. 'Articles.title' => 'string',
  66. 'title' => 'string',
  67. 'Articles.author_id' => 'integer',
  68. 'author_id' => 'integer',
  69. 'Articles.body' => 'text',
  70. 'body' => 'text',
  71. 'Articles.published' => 'string',
  72. 'published' => 'string',
  73. ]);
  74. }
  75. public function tearDown() {
  76. parent::tearDown();
  77. TableRegistry::clear();
  78. }
  79. /**
  80. * Tests the table method
  81. *
  82. * @return void
  83. */
  84. public function testTableMethod() {
  85. $table = new Table(['table' => 'users']);
  86. $this->assertEquals('users', $table->table());
  87. $table = new UsersTable;
  88. $this->assertEquals('users', $table->table());
  89. $table = $this->getMockBuilder('\Cake\ORM\Table')
  90. ->setMethods(['find'])
  91. ->setMockClassName('SpecialThingsTable')
  92. ->getMock();
  93. $this->assertEquals('special_things', $table->table());
  94. $table = new Table(['alias' => 'LoveBoats']);
  95. $this->assertEquals('love_boats', $table->table());
  96. $table->table('other');
  97. $this->assertEquals('other', $table->table());
  98. }
  99. /**
  100. * Tests the alias method
  101. *
  102. * @return void
  103. */
  104. public function testAliasMethod() {
  105. $table = new Table(['alias' => 'users']);
  106. $this->assertEquals('users', $table->alias());
  107. $table = new Table(['table' => 'stuffs']);
  108. $this->assertEquals('stuffs', $table->alias());
  109. $table = new UsersTable;
  110. $this->assertEquals('Users', $table->alias());
  111. $table = $this->getMockBuilder('\Cake\ORM\Table')
  112. ->setMethods(['find'])
  113. ->setMockClassName('SpecialThingTable')
  114. ->getMock();
  115. $this->assertEquals('SpecialThing', $table->alias());
  116. $table->alias('AnotherOne');
  117. $this->assertEquals('AnotherOne', $table->alias());
  118. }
  119. /**
  120. * Tests connection method
  121. *
  122. * @return void
  123. */
  124. public function testConnection() {
  125. $table = new Table(['table' => 'users']);
  126. $this->assertNull($table->connection());
  127. $table->connection($this->connection);
  128. $this->assertSame($this->connection, $table->connection());
  129. }
  130. /**
  131. * Tests primaryKey method
  132. *
  133. * @return void
  134. */
  135. public function testPrimaryKey() {
  136. $table = new Table([
  137. 'table' => 'users',
  138. 'schema' => [
  139. 'id' => ['type' => 'integer'],
  140. '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]]
  141. ]
  142. ]);
  143. $this->assertEquals('id', $table->primaryKey());
  144. $table->primaryKey('thingID');
  145. $this->assertEquals('thingID', $table->primaryKey());
  146. $table->primaryKey(['thingID', 'user_id']);
  147. $this->assertEquals(['thingID', 'user_id'], $table->primaryKey());
  148. }
  149. /**
  150. * Tests that name will be selected as a displayField
  151. *
  152. * @return void
  153. */
  154. public function testDisplayFieldName() {
  155. $table = new Table([
  156. 'table' => 'users',
  157. 'schema' => [
  158. 'foo' => ['type' => 'string'],
  159. 'name' => ['type' => 'string']
  160. ]
  161. ]);
  162. $this->assertEquals('name', $table->displayField());
  163. }
  164. /**
  165. * Tests that title will be selected as a displayField
  166. *
  167. * @return void
  168. */
  169. public function testDisplayFieldTitle() {
  170. $table = new Table([
  171. 'table' => 'users',
  172. 'schema' => [
  173. 'foo' => ['type' => 'string'],
  174. 'title' => ['type' => 'string']
  175. ]
  176. ]);
  177. $this->assertEquals('title', $table->displayField());
  178. }
  179. /**
  180. * Tests that no displayField will fallback to primary key
  181. *
  182. * @return void
  183. */
  184. public function testDisplayFallback() {
  185. $table = new Table([
  186. 'table' => 'users',
  187. 'schema' => [
  188. 'id' => ['type' => 'string'],
  189. 'foo' => ['type' => 'string'],
  190. '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]]
  191. ]
  192. ]);
  193. $this->assertEquals('id', $table->displayField());
  194. }
  195. /**
  196. * Tests that displayField can be changed
  197. *
  198. * @return void
  199. */
  200. public function testDisplaySet() {
  201. $table = new Table([
  202. 'table' => 'users',
  203. 'schema' => [
  204. 'id' => ['type' => 'string'],
  205. 'foo' => ['type' => 'string'],
  206. '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]]
  207. ]
  208. ]);
  209. $this->assertEquals('id', $table->displayField());
  210. $table->displayField('foo');
  211. $this->assertEquals('foo', $table->displayField());
  212. }
  213. /**
  214. * Tests schema method
  215. *
  216. * @return void
  217. */
  218. public function testSchema() {
  219. $schema = $this->connection->schemaCollection()->describe('users');
  220. $table = new Table([
  221. 'table' => 'users',
  222. 'connection' => $this->connection,
  223. ]);
  224. $this->assertEquals($schema, $table->schema());
  225. $table = new Table(['table' => 'stuff']);
  226. $table->schema($schema);
  227. $this->assertSame($schema, $table->schema());
  228. $table = new Table(['table' => 'another']);
  229. $schema = ['id' => ['type' => 'integer']];
  230. $table->schema($schema);
  231. $this->assertEquals(
  232. new \Cake\Database\Schema\Table('another', $schema),
  233. $table->schema()
  234. );
  235. }
  236. /**
  237. * Tests that _initializeSchema can be used to alter the database schema
  238. *
  239. * @return void
  240. */
  241. public function testSchemaInitialize() {
  242. $schema = $this->connection->schemaCollection()->describe('users');
  243. $table = $this->getMock('Cake\ORM\Table', ['_initializeSchema'], [
  244. ['table' => 'users', 'connection' => $this->connection]
  245. ]);
  246. $table->expects($this->once())
  247. ->method('_initializeSchema')
  248. ->with($schema)
  249. ->will($this->returnCallback(function ($schema) {
  250. $schema->columnType('username', 'integer');
  251. return $schema;
  252. }));
  253. $result = $table->schema();
  254. $schema->columnType('username', 'integer');
  255. $this->assertEquals($schema, $result);
  256. $this->assertEquals($schema, $table->schema(), '_initializeSchema should be called once');
  257. }
  258. /**
  259. * Tests that all fields for a table are added by default in a find when no
  260. * other fields are specified
  261. *
  262. * @return void
  263. */
  264. public function testFindAllNoFieldsAndNoHydration() {
  265. $table = new Table([
  266. 'table' => 'users',
  267. 'connection' => $this->connection,
  268. ]);
  269. $results = $table
  270. ->find('all')
  271. ->where(['id IN' => [1, 2]])
  272. ->order('id')
  273. ->hydrate(false)
  274. ->toArray();
  275. $expected = [
  276. [
  277. 'id' => 1,
  278. 'username' => 'mariano',
  279. 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO',
  280. 'created' => new Time('2007-03-17 01:16:23'),
  281. 'updated' => new Time('2007-03-17 01:18:31'),
  282. ],
  283. [
  284. 'id' => 2,
  285. 'username' => 'nate',
  286. 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO',
  287. 'created' => new Time('2008-03-17 01:18:23'),
  288. 'updated' => new Time('2008-03-17 01:20:31'),
  289. ],
  290. ];
  291. $this->assertEquals($expected, $results);
  292. }
  293. /**
  294. * Tests that it is possible to select only a few fields when finding over a table
  295. *
  296. * @return void
  297. */
  298. public function testFindAllSomeFieldsNoHydration() {
  299. $table = new Table([
  300. 'table' => 'users',
  301. 'connection' => $this->connection,
  302. ]);
  303. $results = $table->find('all')
  304. ->select(['username', 'password'])
  305. ->hydrate(false)
  306. ->order('username')->toArray();
  307. $expected = [
  308. ['username' => 'garrett', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  309. ['username' => 'larry', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  310. ['username' => 'mariano', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  311. ['username' => 'nate', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  312. ];
  313. $this->assertSame($expected, $results);
  314. $results = $table->find('all')
  315. ->select(['foo' => 'username', 'password'])
  316. ->order('username')
  317. ->hydrate(false)
  318. ->toArray();
  319. $expected = [
  320. ['foo' => 'garrett', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  321. ['foo' => 'larry', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  322. ['foo' => 'mariano', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  323. ['foo' => 'nate', 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO'],
  324. ];
  325. $this->assertSame($expected, $results);
  326. }
  327. /**
  328. * Tests that the query will automatically casts complex conditions to the correct
  329. * types when the columns belong to the default table
  330. *
  331. * @return void
  332. */
  333. public function testFindAllConditionAutoTypes() {
  334. $table = new Table([
  335. 'table' => 'users',
  336. 'connection' => $this->connection,
  337. ]);
  338. $query = $table->find('all')
  339. ->select(['id', 'username'])
  340. ->where(['created >=' => new Time('2010-01-22 00:00')])
  341. ->hydrate(false)
  342. ->order('id');
  343. $expected = [
  344. ['id' => 3, 'username' => 'larry'],
  345. ['id' => 4, 'username' => 'garrett']
  346. ];
  347. $this->assertSame($expected, $query->toArray());
  348. $query->orWhere(['users.created' => new Time('2008-03-17 01:18:23')]);
  349. $expected = [
  350. ['id' => 2, 'username' => 'nate'],
  351. ['id' => 3, 'username' => 'larry'],
  352. ['id' => 4, 'username' => 'garrett']
  353. ];
  354. $this->assertSame($expected, $query->toArray());
  355. }
  356. /**
  357. * Test that beforeFind events can mutate the query.
  358. *
  359. * @return void
  360. */
  361. public function testFindBeforeFindEventMutateQuery() {
  362. $table = new Table([
  363. 'table' => 'users',
  364. 'connection' => $this->connection,
  365. ]);
  366. $table->eventManager()->attach(function ($event, $query, $options) {
  367. $query->limit(1);
  368. }, 'Model.beforeFind');
  369. $result = $table->find('all')->all();
  370. $this->assertCount(1, $result, 'Should only have 1 record, limit 1 applied.');
  371. }
  372. /**
  373. * Test that beforeFind events are fired and can stop the find and
  374. * return custom results.
  375. *
  376. * @return void
  377. */
  378. public function testFindBeforeFindEventOverrideReturn() {
  379. $table = new Table([
  380. 'table' => 'users',
  381. 'connection' => $this->connection,
  382. ]);
  383. $expected = ['One', 'Two', 'Three'];
  384. $table->eventManager()->attach(function ($event, $query, $options) use ($expected) {
  385. $query->setResult($expected);
  386. $event->stopPropagation();
  387. }, 'Model.beforeFind');
  388. $query = $table->find('all');
  389. $query->limit(1);
  390. $this->assertEquals($expected, $query->all());
  391. }
  392. /**
  393. * Tests that belongsTo() creates and configures correctly the association
  394. *
  395. * @return void
  396. */
  397. public function testBelongsTo() {
  398. $options = ['foreignKey' => 'fake_id', 'conditions' => ['a' => 'b']];
  399. $table = new Table(['table' => 'dates']);
  400. $belongsTo = $table->belongsTo('user', $options);
  401. $this->assertInstanceOf('\Cake\ORM\Association\BelongsTo', $belongsTo);
  402. $this->assertSame($belongsTo, $table->association('user'));
  403. $this->assertEquals('user', $belongsTo->name());
  404. $this->assertEquals('fake_id', $belongsTo->foreignKey());
  405. $this->assertEquals(['a' => 'b'], $belongsTo->conditions());
  406. $this->assertSame($table, $belongsTo->source());
  407. }
  408. /**
  409. * Tests that hasOne() creates and configures correctly the association
  410. *
  411. * @return void
  412. */
  413. public function testHasOne() {
  414. $options = ['foreignKey' => 'user_id', 'conditions' => ['b' => 'c']];
  415. $table = new Table(['table' => 'users']);
  416. $hasOne = $table->hasOne('profile', $options);
  417. $this->assertInstanceOf('\Cake\ORM\Association\HasOne', $hasOne);
  418. $this->assertSame($hasOne, $table->association('profile'));
  419. $this->assertEquals('profile', $hasOne->name());
  420. $this->assertEquals('user_id', $hasOne->foreignKey());
  421. $this->assertEquals(['b' => 'c'], $hasOne->conditions());
  422. $this->assertSame($table, $hasOne->source());
  423. }
  424. /**
  425. * Tests that hasMany() creates and configures correctly the association
  426. *
  427. * @return void
  428. */
  429. public function testHasMany() {
  430. $options = [
  431. 'foreignKey' => 'author_id',
  432. 'conditions' => ['b' => 'c'],
  433. 'sort' => ['foo' => 'asc']
  434. ];
  435. $table = new Table(['table' => 'authors']);
  436. $hasMany = $table->hasMany('article', $options);
  437. $this->assertInstanceOf('\Cake\ORM\Association\HasMany', $hasMany);
  438. $this->assertSame($hasMany, $table->association('article'));
  439. $this->assertEquals('article', $hasMany->name());
  440. $this->assertEquals('author_id', $hasMany->foreignKey());
  441. $this->assertEquals(['b' => 'c'], $hasMany->conditions());
  442. $this->assertEquals(['foo' => 'asc'], $hasMany->sort());
  443. $this->assertSame($table, $hasMany->source());
  444. }
  445. /**
  446. * Tests that BelongsToMany() creates and configures correctly the association
  447. *
  448. * @return void
  449. */
  450. public function testBelongsToMany() {
  451. $options = [
  452. 'foreignKey' => 'thing_id',
  453. 'joinTable' => 'things_tags',
  454. 'conditions' => ['b' => 'c'],
  455. 'sort' => ['foo' => 'asc']
  456. ];
  457. $table = new Table(['table' => 'authors', 'connection' => $this->connection]);
  458. $belongsToMany = $table->belongsToMany('tag', $options);
  459. $this->assertInstanceOf('\Cake\ORM\Association\BelongsToMany', $belongsToMany);
  460. $this->assertSame($belongsToMany, $table->association('tag'));
  461. $this->assertEquals('tag', $belongsToMany->name());
  462. $this->assertEquals('thing_id', $belongsToMany->foreignKey());
  463. $this->assertEquals(['b' => 'c'], $belongsToMany->conditions());
  464. $this->assertEquals(['foo' => 'asc'], $belongsToMany->sort());
  465. $this->assertSame($table, $belongsToMany->source());
  466. $this->assertSame('things_tags', $belongsToMany->junction()->table());
  467. }
  468. /**
  469. * Test basic multi row updates.
  470. *
  471. * @return void
  472. */
  473. public function testUpdateAll() {
  474. $table = new Table([
  475. 'table' => 'users',
  476. 'connection' => $this->connection,
  477. ]);
  478. $fields = ['username' => 'mark'];
  479. $result = $table->updateAll($fields, ['id <' => 4]);
  480. $this->assertTrue($result);
  481. $result = $table->find('all')
  482. ->select(['username'])
  483. ->order(['id' => 'asc'])
  484. ->hydrate(false)
  485. ->toArray();
  486. $expected = array_fill(0, 3, $fields);
  487. $expected[] = ['username' => 'garrett'];
  488. $this->assertEquals($expected, $result);
  489. }
  490. /**
  491. * Test that exceptions from the Query bubble up.
  492. *
  493. * @expectedException \Cake\Database\Exception
  494. */
  495. public function testUpdateAllFailure() {
  496. $table = $this->getMock(
  497. 'Cake\ORM\Table',
  498. ['query'],
  499. [['table' => 'users', 'connection' => $this->connection]]
  500. );
  501. $query = $this->getMock('Cake\ORM\Query', ['execute'], [$this->connection, $table]);
  502. $table->expects($this->once())
  503. ->method('query')
  504. ->will($this->returnValue($query));
  505. $query->expects($this->once())
  506. ->method('execute')
  507. ->will($this->throwException(new \Cake\Database\Exception('Not good')));
  508. $table->updateAll(['username' => 'mark'], []);
  509. }
  510. /**
  511. * Test deleting many records.
  512. *
  513. * @return void
  514. */
  515. public function testDeleteAll() {
  516. $table = new Table([
  517. 'table' => 'users',
  518. 'connection' => $this->connection,
  519. ]);
  520. $result = $table->deleteAll(['id <' => 4]);
  521. $this->assertTrue($result);
  522. $result = $table->find('all')->toArray();
  523. $this->assertCount(1, $result, 'Only one record should remain');
  524. $this->assertEquals(4, $result[0]['id']);
  525. }
  526. /**
  527. * Test deleting many records with conditions using the alias
  528. *
  529. * @return void
  530. */
  531. public function testDeleteAllAliasedConditions() {
  532. $table = new Table([
  533. 'table' => 'users',
  534. 'alias' => 'Managers',
  535. 'connection' => $this->connection,
  536. ]);
  537. $result = $table->deleteAll(['Managers.id <' => 4]);
  538. $this->assertTrue($result);
  539. $result = $table->find('all')->toArray();
  540. $this->assertCount(1, $result, 'Only one record should remain');
  541. $this->assertEquals(4, $result[0]['id']);
  542. }
  543. /**
  544. * Test that exceptions from the Query bubble up.
  545. *
  546. * @expectedException \Cake\Database\Exception
  547. */
  548. public function testDeleteAllFailure() {
  549. $table = $this->getMock(
  550. 'Cake\ORM\Table',
  551. ['query'],
  552. [['table' => 'users', 'connection' => $this->connection]]
  553. );
  554. $query = $this->getMock('Cake\ORM\Query', ['execute'], [$this->connection, $table]);
  555. $table->expects($this->once())
  556. ->method('query')
  557. ->will($this->returnValue($query));
  558. $query->expects($this->once())
  559. ->method('execute')
  560. ->will($this->throwException(new \Cake\Database\Exception('Not good')));
  561. $table->deleteAll(['id >' => 4]);
  562. }
  563. /**
  564. * Tests that array options are passed to the query object using applyOptions
  565. *
  566. * @return void
  567. */
  568. public function testFindApplyOptions() {
  569. $table = $this->getMock(
  570. 'Cake\ORM\Table',
  571. ['query', 'findAll'],
  572. [['table' => 'users', 'connection' => $this->connection]]
  573. );
  574. $query = $this->getMock('Cake\ORM\Query', [], [$this->connection, $table]);
  575. $table->expects($this->once())
  576. ->method('query')
  577. ->will($this->returnValue($query));
  578. $options = ['fields' => ['a', 'b'], 'connections' => ['a >' => 1]];
  579. $query->expects($this->any())
  580. ->method('select')
  581. ->will($this->returnSelf());
  582. $query->expects($this->once())->method('getOptions')
  583. ->will($this->returnValue(['connections' => ['a >' => 1]]));
  584. $query->expects($this->once())
  585. ->method('applyOptions')
  586. ->with($options);
  587. $table->expects($this->once())->method('findAll')
  588. ->with($query, ['connections' => ['a >' => 1]]);
  589. $table->find('all', $options);
  590. }
  591. /**
  592. * Tests find('list')
  593. *
  594. * @return void
  595. */
  596. public function testFindListNoHydration() {
  597. $table = new Table([
  598. 'table' => 'users',
  599. 'connection' => $this->connection,
  600. ]);
  601. $table->displayField('username');
  602. $query = $table->find('list')
  603. ->hydrate(false)
  604. ->order('id');
  605. $expected = [
  606. 1 => 'mariano',
  607. 2 => 'nate',
  608. 3 => 'larry',
  609. 4 => 'garrett'
  610. ];
  611. $this->assertSame($expected, $query->toArray());
  612. $query = $table->find('list', ['fields' => ['id', 'username']])
  613. ->hydrate(false)
  614. ->order('id');
  615. $expected = [
  616. 1 => 'mariano',
  617. 2 => 'nate',
  618. 3 => 'larry',
  619. 4 => 'garrett'
  620. ];
  621. $this->assertSame($expected, $query->toArray());
  622. $query = $table->find('list', ['groupField' => 'odd'])
  623. ->select(['id', 'username', 'odd' => new QueryExpression('id % 2')])
  624. ->hydrate(false)
  625. ->order('id');
  626. $expected = [
  627. 1 => [
  628. 1 => 'mariano',
  629. 3 => 'larry'
  630. ],
  631. 0 => [
  632. 2 => 'nate',
  633. 4 => 'garrett'
  634. ]
  635. ];
  636. $this->assertSame($expected, $query->toArray());
  637. }
  638. /**
  639. * Tests find('threaded')
  640. *
  641. * @return void
  642. */
  643. public function testFindThreadedNoHydration() {
  644. $table = new Table([
  645. 'table' => 'categories',
  646. 'connection' => $this->connection,
  647. ]);
  648. $expected = [
  649. [
  650. 'id' => 1,
  651. 'parent_id' => 0,
  652. 'name' => 'Category 1',
  653. 'children' => [
  654. [
  655. 'id' => 2,
  656. 'parent_id' => 1,
  657. 'name' => 'Category 1.1',
  658. 'children' => [
  659. [
  660. 'id' => 7,
  661. 'parent_id' => 2,
  662. 'name' => 'Category 1.1.1',
  663. 'children' => []
  664. ],
  665. [
  666. 'id' => 8,
  667. 'parent_id' => '2',
  668. 'name' => 'Category 1.1.2',
  669. 'children' => []
  670. ]
  671. ],
  672. ],
  673. [
  674. 'id' => 3,
  675. 'parent_id' => '1',
  676. 'name' => 'Category 1.2',
  677. 'children' => []
  678. ],
  679. ]
  680. ],
  681. [
  682. 'id' => 4,
  683. 'parent_id' => 0,
  684. 'name' => 'Category 2',
  685. 'children' => []
  686. ],
  687. [
  688. 'id' => 5,
  689. 'parent_id' => 0,
  690. 'name' => 'Category 3',
  691. 'children' => [
  692. [
  693. 'id' => '6',
  694. 'parent_id' => '5',
  695. 'name' => 'Category 3.1',
  696. 'children' => []
  697. ]
  698. ]
  699. ]
  700. ];
  701. $results = $table->find('all')
  702. ->select(['id', 'parent_id', 'name'])
  703. ->hydrate(false)
  704. ->find('threaded')
  705. ->toArray();
  706. $this->assertEquals($expected, $results);
  707. }
  708. /**
  709. * Tests that finders can be stacked
  710. *
  711. * @return void
  712. */
  713. public function testStackingFinders() {
  714. $table = $this->getMock('\Cake\ORM\Table', ['find', 'findList'], [], '', false);
  715. $params = [$this->connection, $table];
  716. $query = $this->getMock('\Cake\ORM\Query', ['addDefaultTypes'], $params);
  717. $table->expects($this->once())
  718. ->method('find')
  719. ->with('threaded', ['order' => ['name' => 'ASC']])
  720. ->will($this->returnValue($query));
  721. $table->expects($this->once())
  722. ->method('findList')
  723. ->with($query, ['keyPath' => 'id'])
  724. ->will($this->returnValue($query));
  725. $result = $table
  726. ->find('threaded', ['order' => ['name' => 'ASC']])
  727. ->find('list', ['keyPath' => 'id']);
  728. $this->assertSame($query, $result);
  729. }
  730. /**
  731. * Tests find('threaded') with hydrated results
  732. *
  733. * @return void
  734. */
  735. public function testFindThreadedHydrated() {
  736. $table = new Table([
  737. 'table' => 'categories',
  738. 'connection' => $this->connection,
  739. ]);
  740. $results = $table->find('all')
  741. ->find('threaded')
  742. ->select(['id', 'parent_id', 'name'])
  743. ->toArray();
  744. $this->assertEquals(1, $results[0]->id);
  745. $expected = [
  746. 'id' => 8,
  747. 'parent_id' => 2,
  748. 'name' => 'Category 1.1.2',
  749. 'children' => []
  750. ];
  751. $this->assertEquals($expected, $results[0]->children[0]->children[1]->toArray());
  752. }
  753. /**
  754. * Tests find('list') with hydrated records
  755. *
  756. * @return void
  757. */
  758. public function testFindListHydrated() {
  759. $table = new Table([
  760. 'table' => 'users',
  761. 'connection' => $this->connection,
  762. ]);
  763. $table->displayField('username');
  764. $query = $table
  765. ->find('list', ['fields' => ['id', 'username']])
  766. ->order('id');
  767. $expected = [
  768. 1 => 'mariano',
  769. 2 => 'nate',
  770. 3 => 'larry',
  771. 4 => 'garrett'
  772. ];
  773. $this->assertSame($expected, $query->toArray());
  774. $query = $table->find('list', ['groupField' => 'odd'])
  775. ->select(['id', 'username', 'odd' => new QueryExpression('id % 2')])
  776. ->hydrate(true)
  777. ->order('id');
  778. $expected = [
  779. 1 => [
  780. 1 => 'mariano',
  781. 3 => 'larry'
  782. ],
  783. 0 => [
  784. 2 => 'nate',
  785. 4 => 'garrett'
  786. ]
  787. ];
  788. $this->assertSame($expected, $query->toArray());
  789. }
  790. /**
  791. * Test the default entityClass.
  792. *
  793. * @return void
  794. */
  795. public function testEntityClassDefault() {
  796. $table = new Table();
  797. $this->assertEquals('\Cake\ORM\Entity', $table->entityClass());
  798. }
  799. /**
  800. * Tests that using a simple string for entityClass will try to
  801. * load the class from the App namespace
  802. *
  803. * @return void
  804. */
  805. public function testTableClassInApp() {
  806. $class = $this->getMockClass('\Cake\ORM\Entity');
  807. if (!class_exists('TestApp\Model\Entity\TestUser')) {
  808. class_alias($class, 'TestApp\Model\Entity\TestUser');
  809. }
  810. $table = new Table();
  811. $this->assertEquals('TestApp\Model\Entity\TestUser', $table->entityClass('TestUser'));
  812. }
  813. /**
  814. * Tests that using a simple string for entityClass will try to
  815. * load the class from the Plugin namespace when using plugin notation
  816. *
  817. * @return void
  818. */
  819. public function testTableClassInPlugin() {
  820. $class = $this->getMockClass('\Cake\ORM\Entity');
  821. if (!class_exists('MyPlugin\Model\Entity\SuperUser')) {
  822. class_alias($class, 'MyPlugin\Model\Entity\SuperUser');
  823. }
  824. $table = new Table();
  825. $this->assertEquals(
  826. 'MyPlugin\Model\Entity\SuperUser',
  827. $table->entityClass('MyPlugin.SuperUser')
  828. );
  829. }
  830. /**
  831. * Tests that using a simple string for entityClass will throw an exception
  832. * when the class does not exist in the namespace
  833. *
  834. * @expectedException \Cake\ORM\Exception\MissingEntityException
  835. * @expectedExceptionMessage Entity class FooUser could not be found.
  836. * @return void
  837. */
  838. public function testTableClassNonExisting() {
  839. $table = new Table;
  840. $this->assertFalse($table->entityClass('FooUser'));
  841. }
  842. /**
  843. * Tests getting the entityClass based on conventions for the entity
  844. * namespace
  845. *
  846. * @return void
  847. */
  848. public function testTableClassConventionForAPP() {
  849. $table = new \TestApp\Model\Table\ArticlesTable;
  850. $this->assertEquals('TestApp\Model\Entity\Article', $table->entityClass());
  851. }
  852. /**
  853. * Tests setting a entity class object using the setter method
  854. *
  855. * @return void
  856. */
  857. public function testSetEntityClass() {
  858. $table = new Table;
  859. $class = '\\' . $this->getMockClass('\Cake\ORM\Entity');
  860. $table->entityClass($class);
  861. $this->assertEquals($class, $table->entityClass());
  862. }
  863. /**
  864. * Proves that associations, even though they are lazy loaded, will fetch
  865. * records using the correct table class and hydrate with the correct entity
  866. *
  867. * @return void
  868. */
  869. public function testReciprocalBelongsToLoading() {
  870. $table = new \TestApp\Model\Table\ArticlesTable([
  871. 'connection' => $this->connection,
  872. ]);
  873. $result = $table->find('all')->contain(['authors'])->first();
  874. $this->assertInstanceOf('TestApp\Model\Entity\Author', $result->author);
  875. }
  876. /**
  877. * Proves that associations, even though they are lazy loaded, will fetch
  878. * records using the correct table class and hydrate with the correct entity
  879. *
  880. * @return void
  881. */
  882. public function testReciprocalHasManyLoading() {
  883. $table = new \TestApp\Model\Table\ArticlesTable([
  884. 'connection' => $this->connection,
  885. ]);
  886. $result = $table->find('all')->contain(['authors' => ['articles']])->first();
  887. $this->assertCount(2, $result->author->articles);
  888. foreach ($result->author->articles as $article) {
  889. $this->assertInstanceOf('TestApp\Model\Entity\Article', $article);
  890. }
  891. }
  892. /**
  893. * Tests that the correct table and entity are loaded for the join association in
  894. * a belongsToMany setup
  895. *
  896. * @return void
  897. */
  898. public function testReciprocalBelongsToMany() {
  899. $table = new \TestApp\Model\Table\ArticlesTable([
  900. 'connection' => $this->connection,
  901. ]);
  902. $result = $table->find('all')->contain(['tags'])->first();
  903. $this->assertInstanceOf('TestApp\Model\Entity\Tag', $result->tags[0]);
  904. $this->assertInstanceOf(
  905. 'TestApp\Model\Entity\ArticlesTag',
  906. $result->tags[0]->_joinData
  907. );
  908. }
  909. /**
  910. * Tests that recently fetched entities are always clean
  911. *
  912. * @return void
  913. */
  914. public function testFindCleanEntities() {
  915. $table = new \TestApp\Model\Table\ArticlesTable([
  916. 'connection' => $this->connection,
  917. ]);
  918. $results = $table->find('all')->contain(['tags', 'authors'])->toArray();
  919. $this->assertCount(3, $results);
  920. foreach ($results as $article) {
  921. $this->assertFalse($article->dirty('id'));
  922. $this->assertFalse($article->dirty('title'));
  923. $this->assertFalse($article->dirty('author_id'));
  924. $this->assertFalse($article->dirty('body'));
  925. $this->assertFalse($article->dirty('published'));
  926. $this->assertFalse($article->dirty('author'));
  927. $this->assertFalse($article->author->dirty('id'));
  928. $this->assertFalse($article->author->dirty('name'));
  929. $this->assertFalse($article->dirty('tag'));
  930. if ($article->tag) {
  931. $this->assertFalse($article->tag[0]->_joinData->dirty('tag_id'));
  932. }
  933. }
  934. }
  935. /**
  936. * Tests that recently fetched entities are marked as not new
  937. *
  938. * @return void
  939. */
  940. public function testFindPersistedEntities() {
  941. $table = new \TestApp\Model\Table\ArticlesTable([
  942. 'connection' => $this->connection,
  943. ]);
  944. $results = $table->find('all')->contain(['tags', 'authors'])->toArray();
  945. $this->assertCount(3, $results);
  946. foreach ($results as $article) {
  947. $this->assertFalse($article->isNew());
  948. foreach ((array)$article->tag as $tag) {
  949. $this->assertFalse($tag->isNew());
  950. $this->assertFalse($tag->_joinData->isNew());
  951. }
  952. }
  953. }
  954. /**
  955. * Tests the exists function
  956. *
  957. * @return void
  958. */
  959. public function testExists() {
  960. $table = TableRegistry::get('users');
  961. $this->assertTrue($table->exists(['id' => 1]));
  962. $this->assertFalse($table->exists(['id' => 501]));
  963. $this->assertTrue($table->exists(['id' => 3, 'username' => 'larry']));
  964. }
  965. /**
  966. * Test adding a behavior to a table.
  967. *
  968. * @return void
  969. */
  970. public function testAddBehavior() {
  971. $mock = $this->getMock('Cake\ORM\BehaviorRegistry', [], [], '', false);
  972. $mock->expects($this->once())
  973. ->method('load')
  974. ->with('Sluggable');
  975. $table = new Table([
  976. 'table' => 'articles',
  977. 'behaviors' => $mock
  978. ]);
  979. $table->addBehavior('Sluggable');
  980. }
  981. /**
  982. * Test adding a behavior that is a duplicate.
  983. *
  984. * @return void
  985. */
  986. public function testAddBehaviorDuplicate() {
  987. $table = new Table(['table' => 'articles']);
  988. $this->assertNull($table->addBehavior('Sluggable', ['test' => 'value']));
  989. $this->assertNull($table->addBehavior('Sluggable', ['test' => 'value']));
  990. try {
  991. $table->addBehavior('Sluggable', ['thing' => 'thing']);
  992. $this->fail('No exception raised');
  993. } catch (\RuntimeException $e) {
  994. $this->assertContains('The "Sluggable" alias has already been loaded', $e->getMessage());
  995. }
  996. }
  997. /**
  998. * Test removing a behavior from a table.
  999. *
  1000. * @return void
  1001. */
  1002. public function testRemoveBehavior() {
  1003. $mock = $this->getMock('Cake\ORM\BehaviorRegistry', [], [], '', false);
  1004. $mock->expects($this->once())
  1005. ->method('unload')
  1006. ->with('Sluggable');
  1007. $table = new Table([
  1008. 'table' => 'articles',
  1009. 'behaviors' => $mock
  1010. ]);
  1011. $table->removeBehavior('Sluggable');
  1012. }
  1013. /**
  1014. * Test getting a behavior instance from a table.
  1015. *
  1016. * @return void
  1017. */
  1018. public function testBehaviors() {
  1019. $table = TableRegistry::get('article');
  1020. $result = $table->behaviors();
  1021. $this->assertInstanceOf('\Cake\ORM\BehaviorRegistry', $result);
  1022. }
  1023. /**
  1024. * Ensure exceptions are raised on missing behaviors.
  1025. *
  1026. * @expectedException \Cake\ORM\Exception\MissingBehaviorException
  1027. */
  1028. public function testAddBehaviorMissing() {
  1029. $table = TableRegistry::get('article');
  1030. $this->assertNull($table->addBehavior('NopeNotThere'));
  1031. }
  1032. /**
  1033. * Test mixin methods from behaviors.
  1034. *
  1035. * @return void
  1036. */
  1037. public function testCallBehaviorMethod() {
  1038. $table = TableRegistry::get('article');
  1039. $table->addBehavior('Sluggable');
  1040. $this->assertEquals('some_value', $table->slugify('some value'));
  1041. }
  1042. /**
  1043. * Test you can alias a behavior method
  1044. *
  1045. * @return void
  1046. */
  1047. public function testCallBehaviorAliasedMethod() {
  1048. $table = TableRegistry::get('article');
  1049. $table->addBehavior('Sluggable', ['implementedMethods' => ['wednesday' => 'slugify']]);
  1050. $this->assertEquals('some_value', $table->wednesday('some value'));
  1051. }
  1052. /**
  1053. * Test finder methods from behaviors.
  1054. *
  1055. * @return void
  1056. */
  1057. public function testCallBehaviorFinder() {
  1058. $table = TableRegistry::get('articles');
  1059. $table->addBehavior('Sluggable');
  1060. $query = $table->find('noSlug');
  1061. $this->assertInstanceOf('Cake\ORM\Query', $query);
  1062. $this->assertNotEmpty($query->clause('where'));
  1063. }
  1064. /**
  1065. * testCallBehaviorAliasedFinder
  1066. *
  1067. * @return void
  1068. */
  1069. public function testCallBehaviorAliasedFinder() {
  1070. $table = TableRegistry::get('articles');
  1071. $table->addBehavior('Sluggable', ['implementedFinders' => ['special' => 'findNoSlug']]);
  1072. $query = $table->find('special');
  1073. $this->assertInstanceOf('Cake\ORM\Query', $query);
  1074. $this->assertNotEmpty($query->clause('where'));
  1075. }
  1076. /**
  1077. * Test implementedEvents
  1078. *
  1079. * @return void
  1080. */
  1081. public function testImplementedEvents() {
  1082. $table = $this->getMock(
  1083. 'Cake\ORM\Table',
  1084. ['beforeFind', 'beforeSave', 'afterSave', 'beforeDelete', 'afterDelete']
  1085. );
  1086. $result = $table->implementedEvents();
  1087. $expected = [
  1088. 'Model.beforeFind' => 'beforeFind',
  1089. 'Model.beforeSave' => 'beforeSave',
  1090. 'Model.afterSave' => 'afterSave',
  1091. 'Model.beforeDelete' => 'beforeDelete',
  1092. 'Model.afterDelete' => 'afterDelete',
  1093. ];
  1094. $this->assertEquals($expected, $result, 'Events do not match.');
  1095. }
  1096. /**
  1097. * Tests that it is possible to insert a new row using the save method
  1098. *
  1099. * @group save
  1100. * @return void
  1101. */
  1102. public function testSaveNewEntity() {
  1103. $entity = new \Cake\ORM\Entity([
  1104. 'username' => 'superuser',
  1105. 'password' => 'root',
  1106. 'created' => new Time('2013-10-10 00:00'),
  1107. 'updated' => new Time('2013-10-10 00:00')
  1108. ]);
  1109. $table = TableRegistry::get('users');
  1110. $this->assertSame($entity, $table->save($entity));
  1111. $this->assertEquals($entity->id, self::$nextUserId);
  1112. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1113. $this->assertEquals($entity->toArray(), $row->toArray());
  1114. }
  1115. /**
  1116. * Test that saving a new empty entity does nothing.
  1117. *
  1118. * @group save
  1119. * @return void
  1120. */
  1121. public function testSaveNewEmptyEntity() {
  1122. $entity = new \Cake\ORM\Entity();
  1123. $table = TableRegistry::get('users');
  1124. $this->assertFalse($table->save($entity));
  1125. }
  1126. /**
  1127. * Test that saving a new empty entity does not call exists.
  1128. *
  1129. * @group save
  1130. * @return void
  1131. */
  1132. public function testSaveNewEntityNoExists() {
  1133. $table = $this->getMock(
  1134. 'Cake\ORM\Table',
  1135. ['exists'],
  1136. [[
  1137. 'connection' => $this->connection,
  1138. 'alias' => 'Users',
  1139. 'table' => 'users',
  1140. ]]
  1141. );
  1142. $entity = $table->newEntity(['username' => 'mark']);
  1143. $this->assertTrue($entity->isNew());
  1144. $table->expects($this->never())
  1145. ->method('exists');
  1146. $this->assertSame($entity, $table->save($entity));
  1147. }
  1148. /**
  1149. * Tests that saving an entity will filter out properties that
  1150. * are not present in the table schema when saving
  1151. *
  1152. * @group save
  1153. * @return void
  1154. */
  1155. public function testSaveEntityOnlySchemaFields() {
  1156. $entity = new \Cake\ORM\Entity([
  1157. 'username' => 'superuser',
  1158. 'password' => 'root',
  1159. 'crazyness' => 'super crazy value',
  1160. 'created' => new Time('2013-10-10 00:00'),
  1161. 'updated' => new Time('2013-10-10 00:00'),
  1162. ]);
  1163. $table = TableRegistry::get('users');
  1164. $this->assertSame($entity, $table->save($entity));
  1165. $this->assertEquals($entity->id, self::$nextUserId);
  1166. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1167. $entity->unsetProperty('crazyness');
  1168. $this->assertEquals($entity->toArray(), $row->toArray());
  1169. }
  1170. /**
  1171. * Tests that it is possible to modify data from the beforeSave callback
  1172. *
  1173. * @group save
  1174. * @return void
  1175. */
  1176. public function testBeforeSaveModifyData() {
  1177. $table = TableRegistry::get('users');
  1178. $data = new \Cake\ORM\Entity([
  1179. 'username' => 'superuser',
  1180. 'created' => new Time('2013-10-10 00:00'),
  1181. 'updated' => new Time('2013-10-10 00:00')
  1182. ]);
  1183. $listener = function ($e, $entity, $options) use ($data) {
  1184. $this->assertSame($data, $entity);
  1185. $entity->set('password', 'foo');
  1186. };
  1187. $table->eventManager()->attach($listener, 'Model.beforeSave');
  1188. $this->assertSame($data, $table->save($data));
  1189. $this->assertEquals($data->id, self::$nextUserId);
  1190. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1191. $this->assertEquals('foo', $row->get('password'));
  1192. }
  1193. /**
  1194. * Tests that it is possible to modify the options array in beforeSave
  1195. *
  1196. * @group save
  1197. * @return void
  1198. */
  1199. public function testBeforeSaveModifyOptions() {
  1200. $table = TableRegistry::get('users');
  1201. $data = new \Cake\ORM\Entity([
  1202. 'username' => 'superuser',
  1203. 'password' => 'foo',
  1204. 'created' => new Time('2013-10-10 00:00'),
  1205. 'updated' => new Time('2013-10-10 00:00')
  1206. ]);
  1207. $listener1 = function ($e, $entity, $options) {
  1208. $options['crazy'] = true;
  1209. };
  1210. $listener2 = function ($e, $entity, $options) {
  1211. $this->assertTrue($options['crazy']);
  1212. };
  1213. $table->eventManager()->attach($listener1, 'Model.beforeSave');
  1214. $table->eventManager()->attach($listener2, 'Model.beforeSave');
  1215. $this->assertSame($data, $table->save($data));
  1216. $this->assertEquals($data->id, self::$nextUserId);
  1217. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1218. $this->assertEquals($data->toArray(), $row->toArray());
  1219. }
  1220. /**
  1221. * Tests that it is possible to stop the saving altogether, without implying
  1222. * the save operation failed
  1223. *
  1224. * @group save
  1225. * @return void
  1226. */
  1227. public function testBeforeSaveStopEvent() {
  1228. $table = TableRegistry::get('users');
  1229. $data = new \Cake\ORM\Entity([
  1230. 'username' => 'superuser',
  1231. 'created' => new Time('2013-10-10 00:00'),
  1232. 'updated' => new Time('2013-10-10 00:00')
  1233. ]);
  1234. $listener = function ($e, $entity) {
  1235. $e->stopPropagation();
  1236. return $entity;
  1237. };
  1238. $table->eventManager()->attach($listener, 'Model.beforeSave');
  1239. $this->assertSame($data, $table->save($data));
  1240. $this->assertNull($data->id);
  1241. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1242. $this->assertNull($row);
  1243. }
  1244. /**
  1245. * Asserts that afterSave callback is called on successful save
  1246. *
  1247. * @group save
  1248. * @return void
  1249. */
  1250. public function testAfterSave() {
  1251. $table = TableRegistry::get('users');
  1252. $data = new \Cake\ORM\Entity([
  1253. 'username' => 'superuser',
  1254. 'created' => new Time('2013-10-10 00:00'),
  1255. 'updated' => new Time('2013-10-10 00:00')
  1256. ]);
  1257. $called = false;
  1258. $listener = function ($e, $entity, $options) use ($data, &$called) {
  1259. $this->assertSame($data, $entity);
  1260. $called = true;
  1261. };
  1262. $table->eventManager()->attach($listener, 'Model.afterSave');
  1263. $this->assertSame($data, $table->save($data));
  1264. $this->assertEquals($data->id, self::$nextUserId);
  1265. $this->assertTrue($called);
  1266. }
  1267. /**
  1268. * Asserts that afterSave callback not is called on unsuccessful save
  1269. *
  1270. * @group save
  1271. * @return void
  1272. */
  1273. public function testAfterSaveNotCalled() {
  1274. $table = $this->getMock(
  1275. '\Cake\ORM\Table',
  1276. ['query'],
  1277. [['table' => 'users', 'connection' => $this->connection]]
  1278. );
  1279. $query = $this->getMock(
  1280. '\Cake\ORM\Query',
  1281. ['execute', 'addDefaultTypes'],
  1282. [null, $table]
  1283. );
  1284. $statement = $this->getMock('\Cake\Database\Statement\StatementDecorator');
  1285. $data = new \Cake\ORM\Entity([
  1286. 'username' => 'superuser',
  1287. 'created' => new Time('2013-10-10 00:00'),
  1288. 'updated' => new Time('2013-10-10 00:00')
  1289. ]);
  1290. $table->expects($this->once())->method('query')
  1291. ->will($this->returnValue($query));
  1292. $query->expects($this->once())->method('execute')
  1293. ->will($this->returnValue($statement));
  1294. $statement->expects($this->once())->method('rowCount')
  1295. ->will($this->returnValue(0));
  1296. $called = false;
  1297. $listener = function ($e, $entity, $options) use ($data, &$called) {
  1298. $called = true;
  1299. };
  1300. $table->eventManager()->attach($listener, 'Model.afterSave');
  1301. $this->assertFalse($table->save($data));
  1302. $this->assertFalse($called);
  1303. }
  1304. /**
  1305. * Test that you cannot save rows without a primary key.
  1306. *
  1307. * @group save
  1308. * @expectedException \RuntimeException
  1309. * @expectedExceptionMessage Cannot insert row in "users" table, it has no primary key
  1310. * @return void
  1311. */
  1312. public function testSaveNewErrorOnNoPrimaryKey() {
  1313. $entity = new \Cake\ORM\Entity(['username' => 'superuser']);
  1314. $table = TableRegistry::get('users', [
  1315. 'schema' => [
  1316. 'id' => ['type' => 'integer'],
  1317. 'username' => ['type' => 'string'],
  1318. ]
  1319. ]);
  1320. $table->save($entity);
  1321. }
  1322. /**
  1323. * Tests that save is wrapped around a transaction
  1324. *
  1325. * @group save
  1326. * @return void
  1327. */
  1328. public function testAtomicSave() {
  1329. $config = ConnectionManager::config('test');
  1330. $connection = $this->getMock(
  1331. '\Cake\Database\Connection',
  1332. ['begin', 'commit'],
  1333. [$config]
  1334. );
  1335. $connection->driver($this->connection->driver());
  1336. $table = $this->getMock('\Cake\ORM\Table', ['connection'], [['table' => 'users']]);
  1337. $table->expects($this->any())->method('connection')
  1338. ->will($this->returnValue($connection));
  1339. $connection->expects($this->once())->method('begin');
  1340. $connection->expects($this->once())->method('commit');
  1341. $data = new \Cake\ORM\Entity([
  1342. 'username' => 'superuser',
  1343. 'created' => new Time('2013-10-10 00:00'),
  1344. 'updated' => new Time('2013-10-10 00:00')
  1345. ]);
  1346. $this->assertSame($data, $table->save($data));
  1347. }
  1348. /**
  1349. * Tests that save will rollback the transaction in the case of an exception
  1350. *
  1351. * @group save
  1352. * @expectedException \PDOException
  1353. * @return void
  1354. */
  1355. public function testAtomicSaveRollback() {
  1356. $connection = $this->getMock(
  1357. '\Cake\Database\Connection',
  1358. ['begin', 'rollback'],
  1359. [ConnectionManager::config('test')]
  1360. );
  1361. $connection->driver(ConnectionManager::get('test')->driver());
  1362. $table = $this->getMock(
  1363. '\Cake\ORM\Table',
  1364. ['query', 'connection'],
  1365. [['table' => 'users']]
  1366. );
  1367. $query = $this->getMock(
  1368. '\Cake\ORM\Query',
  1369. ['execute', 'addDefaultTypes'],
  1370. [null, $table]
  1371. );
  1372. $table->expects($this->any())->method('connection')
  1373. ->will($this->returnValue($connection));
  1374. $table->expects($this->once())->method('query')
  1375. ->will($this->returnValue($query));
  1376. $connection->expects($this->once())->method('begin');
  1377. $connection->expects($this->once())->method('rollback');
  1378. $query->expects($this->once())->method('execute')
  1379. ->will($this->throwException(new \PDOException));
  1380. $data = new \Cake\ORM\Entity([
  1381. 'username' => 'superuser',
  1382. 'created' => new Time('2013-10-10 00:00'),
  1383. 'updated' => new Time('2013-10-10 00:00')
  1384. ]);
  1385. $table->save($data);
  1386. }
  1387. /**
  1388. * Tests that save will rollback the transaction in the case of an exception
  1389. *
  1390. * @group save
  1391. * @return void
  1392. */
  1393. public function testAtomicSaveRollbackOnFailure() {
  1394. $connection = $this->getMock(
  1395. '\Cake\Database\Connection',
  1396. ['begin', 'rollback'],
  1397. [ConnectionManager::config('test')]
  1398. );
  1399. $connection->driver(ConnectionManager::get('test')->driver());
  1400. $table = $this->getMock(
  1401. '\Cake\ORM\Table',
  1402. ['query', 'connection', 'exists'],
  1403. [['table' => 'users']]
  1404. );
  1405. $query = $this->getMock(
  1406. '\Cake\ORM\Query',
  1407. ['execute', 'addDefaultTypes'],
  1408. [null, $table]
  1409. );
  1410. $table->expects($this->any())->method('connection')
  1411. ->will($this->returnValue($connection));
  1412. $table->expects($this->once())->method('query')
  1413. ->will($this->returnValue($query));
  1414. $statement = $this->getMock('\Cake\Database\Statement\StatementDecorator');
  1415. $statement->expects($this->once())
  1416. ->method('rowCount')
  1417. ->will($this->returnValue(0));
  1418. $connection->expects($this->once())->method('begin');
  1419. $connection->expects($this->once())->method('rollback');
  1420. $query->expects($this->once())
  1421. ->method('execute')
  1422. ->will($this->returnValue($statement));
  1423. $data = new \Cake\ORM\Entity([
  1424. 'username' => 'superuser',
  1425. 'created' => new Time('2013-10-10 00:00'),
  1426. 'updated' => new Time('2013-10-10 00:00')
  1427. ]);
  1428. $table->save($data);
  1429. }
  1430. /**
  1431. * Tests that only the properties marked as dirty are actually saved
  1432. * to the database
  1433. *
  1434. * @group save
  1435. * @return void
  1436. */
  1437. public function testSaveOnlyDirtyProperties() {
  1438. $entity = new \Cake\ORM\Entity([
  1439. 'username' => 'superuser',
  1440. 'password' => 'root',
  1441. 'created' => new Time('2013-10-10 00:00'),
  1442. 'updated' => new Time('2013-10-10 00:00')
  1443. ]);
  1444. $entity->clean();
  1445. $entity->dirty('username', true);
  1446. $entity->dirty('created', true);
  1447. $entity->dirty('updated', true);
  1448. $table = TableRegistry::get('users');
  1449. $this->assertSame($entity, $table->save($entity));
  1450. $this->assertEquals($entity->id, self::$nextUserId);
  1451. $row = $table->find('all')->where(['id' => self::$nextUserId])->first();
  1452. $entity->set('password', null);
  1453. $this->assertEquals($entity->toArray(), $row->toArray());
  1454. }
  1455. /**
  1456. * Tests that a recently saved entity is marked as clean
  1457. *
  1458. * @group save
  1459. * @return void
  1460. */
  1461. public function testASavedEntityIsClean() {
  1462. $entity = new \Cake\ORM\Entity([
  1463. 'username' => 'superuser',
  1464. 'password' => 'root',
  1465. 'created' => new Time('2013-10-10 00:00'),
  1466. 'updated' => new Time('2013-10-10 00:00')
  1467. ]);
  1468. $table = TableRegistry::get('users');
  1469. $this->assertSame($entity, $table->save($entity));
  1470. $this->assertFalse($entity->dirty('usermane'));
  1471. $this->assertFalse($entity->dirty('password'));
  1472. $this->assertFalse($entity->dirty('created'));
  1473. $this->assertFalse($entity->dirty('updated'));
  1474. }
  1475. /**
  1476. * Tests that a recently saved entity is marked as not new
  1477. *
  1478. * @group save
  1479. * @return void
  1480. */
  1481. public function testASavedEntityIsNotNew() {
  1482. $entity = new \Cake\ORM\Entity([
  1483. 'username' => 'superuser',
  1484. 'password' => 'root',
  1485. 'created' => new Time('2013-10-10 00:00'),
  1486. 'updated' => new Time('2013-10-10 00:00')
  1487. ]);
  1488. $table = TableRegistry::get('users');
  1489. $this->assertSame($entity, $table->save($entity));
  1490. $this->assertFalse($entity->isNew());
  1491. }
  1492. /**
  1493. * Tests that save can detect automatically if it needs to insert
  1494. * or update a row
  1495. *
  1496. * @group save
  1497. * @return void
  1498. */
  1499. public function testSaveUpdateAuto() {
  1500. $entity = new \Cake\ORM\Entity([
  1501. 'id' => 2,
  1502. 'username' => 'baggins'
  1503. ]);
  1504. $table = TableRegistry::get('users');
  1505. $original = $table->find('all')->where(['id' => 2])->first();
  1506. $this->assertSame($entity, $table->save($entity));
  1507. $row = $table->find('all')->where(['id' => 2])->first();
  1508. $this->assertEquals('baggins', $row->username);
  1509. $this->assertEquals($original->password, $row->password);
  1510. $this->assertEquals($original->created, $row->created);
  1511. $this->assertEquals($original->updated, $row->updated);
  1512. $this->assertFalse($entity->isNew());
  1513. $this->assertFalse($entity->dirty('id'));
  1514. $this->assertFalse($entity->dirty('username'));
  1515. }
  1516. /**
  1517. * Tests that beforeFind gets the correct isNew() state for the entity
  1518. *
  1519. * @return void
  1520. */
  1521. public function testBeforeSaveGetsCorrectPersistance() {
  1522. $entity = new \Cake\ORM\Entity([
  1523. 'id' => 2,
  1524. 'username' => 'baggins'
  1525. ]);
  1526. $table = TableRegistry::get('users');
  1527. $called = false;
  1528. $listener = function ($event, $entity) use (&$called) {
  1529. $this->assertFalse($entity->isNew());
  1530. $called = true;
  1531. };
  1532. $table->eventManager()->attach($listener, 'Model.beforeSave');
  1533. $this->assertSame($entity, $table->save($entity));
  1534. $this->assertTrue($called);
  1535. }
  1536. /**
  1537. * Tests that marking an entity as already persisted will prevent the save
  1538. * method from trying to infer the entity's actual status.
  1539. *
  1540. * @group save
  1541. * @return void
  1542. */
  1543. public function testSaveUpdateWithHint() {
  1544. $table = $this->getMock(
  1545. '\Cake\ORM\Table',
  1546. ['exists'],
  1547. [['table' => 'users', 'connection' => ConnectionManager::get('test')]]
  1548. );
  1549. $entity = new \Cake\ORM\Entity([
  1550. 'id' => 2,
  1551. 'username' => 'baggins'
  1552. ], ['markNew' => false]);
  1553. $this->assertFalse($entity->isNew());
  1554. $table->expects($this->never())->method('exists');
  1555. $this->assertSame($entity, $table->save($entity));
  1556. }
  1557. /**
  1558. * Tests that when updating the primary key is not passed to the list of
  1559. * attributes to change
  1560. *
  1561. * @group save
  1562. * @return void
  1563. */
  1564. public function testSaveUpdatePrimaryKeyNotModified() {
  1565. $table = $this->getMock(
  1566. '\Cake\ORM\Table',
  1567. ['query'],
  1568. [['table' => 'users', 'connection' => $this->connection]]
  1569. );
  1570. $query = $this->getMock(
  1571. '\Cake\ORM\Query',
  1572. ['execute', 'addDefaultTypes', 'set'],
  1573. [null, $table]
  1574. );
  1575. $table->expects($this->once())->method('query')
  1576. ->will($this->returnValue($query));
  1577. $statement = $this->getMock('\Cake\Database\Statement\StatementDecorator');
  1578. $statement->expects($this->once())
  1579. ->method('errorCode')
  1580. ->will($this->returnValue('00000'));
  1581. $query->expects($this->once())
  1582. ->method('execute')
  1583. ->will($this->returnValue($statement));
  1584. $query->expects($this->once())->method('set')
  1585. ->with(['username' => 'baggins'])
  1586. ->will($this->returnValue($query));
  1587. $entity = new \Cake\ORM\Entity([
  1588. 'id' => 2,
  1589. 'username' => 'baggins'
  1590. ], ['markNew' => false]);
  1591. $this->assertSame($entity, $table->save($entity));
  1592. }
  1593. /**
  1594. * Tests that passing only the primary key to save will not execute any queries
  1595. * but still return success
  1596. *
  1597. * @group save
  1598. * @return void
  1599. */
  1600. public function testUpdateNoChange() {
  1601. $table = $this->getMock(
  1602. '\Cake\ORM\Table',
  1603. ['query'],
  1604. [['table' => 'users', 'connection' => $this->connection]]
  1605. );
  1606. $table->expects($this->never())->method('query');
  1607. $entity = new \Cake\ORM\Entity([
  1608. 'id' => 2,
  1609. ], ['markNew' => false]);
  1610. $this->assertSame($entity, $table->save($entity));
  1611. }
  1612. /**
  1613. * Tests that passing only the primary key to save will not execute any queries
  1614. * but still return success
  1615. *
  1616. * @group save
  1617. * @group integration
  1618. * @return void
  1619. */
  1620. public function testUpdateDirtyNoActualChanges() {
  1621. $table = TableRegistry::get('Articles');
  1622. $entity = $table->get(1);
  1623. $entity->accessible('*', true);
  1624. $entity->set($entity->toArray());
  1625. $this->assertSame($entity, $table->save($entity));
  1626. }
  1627. /**
  1628. * Tests that failing to pass a primary key to save will result in exception
  1629. *
  1630. * @group save
  1631. * @expectedException \InvalidArgumentException
  1632. * @return void
  1633. */
  1634. public function testUpdateNoPrimaryButOtherKeys() {
  1635. $table = $this->getMock(
  1636. '\Cake\ORM\Table',
  1637. ['query'],
  1638. [['table' => 'users', 'connection' => $this->connection]]
  1639. );
  1640. $table->expects($this->never())->method('query');
  1641. $entity = new \Cake\ORM\Entity([
  1642. 'username' => 'mariano',
  1643. ], ['markNew' => false]);
  1644. $this->assertSame($entity, $table->save($entity));
  1645. }
  1646. /**
  1647. * Test simple delete.
  1648. *
  1649. * @return void
  1650. */
  1651. public function testDelete() {
  1652. $table = TableRegistry::get('users');
  1653. $conditions = [
  1654. 'limit' => 1,
  1655. 'conditions' => [
  1656. 'username' => 'nate'
  1657. ]
  1658. ];
  1659. $query = $table->find('all', $conditions);
  1660. $entity = $query->first();
  1661. $result = $table->delete($entity);
  1662. $this->assertTrue($result);
  1663. $query = $table->find('all', $conditions);
  1664. $results = $query->execute();
  1665. $this->assertCount(0, $results, 'Find should fail.');
  1666. }
  1667. /**
  1668. * Test delete with dependent records
  1669. *
  1670. * @return void
  1671. */
  1672. public function testDeleteDependent() {
  1673. $table = TableRegistry::get('authors');
  1674. $table->hasOne('articles', [
  1675. 'foreignKey' => 'author_id',
  1676. 'dependent' => true,
  1677. ]);
  1678. $query = $table->find('all')->where(['id' => 1]);
  1679. $entity = $query->first();
  1680. $result = $table->delete($entity);
  1681. $articles = $table->association('articles')->target();
  1682. $query = $articles->find('all', [
  1683. 'conditions' => [
  1684. 'author_id' => $entity->id
  1685. ]
  1686. ]);
  1687. $this->assertNull($query->all()->first(), 'Should not find any rows.');
  1688. }
  1689. /**
  1690. * Test delete with dependent = false does not cascade.
  1691. *
  1692. * @return void
  1693. */
  1694. public function testDeleteNoDependentNoCascade() {
  1695. $table = TableRegistry::get('authors');
  1696. $table->hasMany('article', [
  1697. 'foreignKey' => 'author_id',
  1698. 'dependent' => false,
  1699. ]);
  1700. $query = $table->find('all')->where(['id' => 1]);
  1701. $entity = $query->first();
  1702. $result = $table->delete($entity);
  1703. $articles = $table->association('articles')->target();
  1704. $query = $articles->find('all')->where(['author_id' => $entity->id]);
  1705. $this->assertCount(2, $query->execute(), 'Should find rows.');
  1706. }
  1707. /**
  1708. * Test delete with BelongsToMany
  1709. *
  1710. * @return void
  1711. */
  1712. public function testDeleteBelongsToMany() {
  1713. $table = TableRegistry::get('articles');
  1714. $table->belongsToMany('tag', [
  1715. 'foreignKey' => 'article_id',
  1716. 'joinTable' => 'articles_tags'
  1717. ]);
  1718. $query = $table->find('all')->where(['id' => 1]);
  1719. $entity = $query->first();
  1720. $table->delete($entity);
  1721. $junction = $table->association('tags')->junction();
  1722. $query = $junction->find('all')->where(['article_id' => 1]);
  1723. $this->assertNull($query->all()->first(), 'Should not find any rows.');
  1724. }
  1725. /**
  1726. * Test delete callbacks
  1727. *
  1728. * @return void
  1729. */
  1730. public function testDeleteCallbacks() {
  1731. $entity = new \Cake\ORM\Entity(['id' => 1, 'name' => 'mark']);
  1732. $options = new \ArrayObject(['atomic' => true]);
  1733. $mock = $this->getMock('Cake\Event\EventManager');
  1734. $mock->expects($this->at(0))
  1735. ->method('attach');
  1736. $mock->expects($this->at(1))
  1737. ->method('dispatch')
  1738. ->with($this->logicalAnd(
  1739. $this->attributeEqualTo('_name', 'Model.beforeDelete'),
  1740. $this->attributeEqualTo(
  1741. 'data',
  1742. ['entity' => $entity, 'options' => $options]
  1743. )
  1744. ));
  1745. $mock->expects($this->at(2))
  1746. ->method('dispatch')
  1747. ->with($this->logicalAnd(
  1748. $this->attributeEqualTo('_name', 'Model.afterDelete'),
  1749. $this->attributeEqualTo(
  1750. 'data',
  1751. ['entity' => $entity, 'options' => $options]
  1752. )
  1753. ));
  1754. $table = TableRegistry::get('users', ['eventManager' => $mock]);
  1755. $entity->isNew(false);
  1756. $table->delete($entity);
  1757. }
  1758. /**
  1759. * Test delete beforeDelete can abort the delete.
  1760. *
  1761. * @return void
  1762. */
  1763. public function testDeleteBeforeDeleteAbort() {
  1764. $entity = new \Cake\ORM\Entity(['id' => 1, 'name' => 'mark']);
  1765. $options = new \ArrayObject(['atomic' => true, 'cascade' => true]);
  1766. $mock = $this->getMock('Cake\Event\EventManager');
  1767. $mock->expects($this->once())
  1768. ->method('dispatch')
  1769. ->will($this->returnCallback(function ($event) {
  1770. $event->stopPropagation();
  1771. }));
  1772. $table = TableRegistry::get('users', ['eventManager' => $mock]);
  1773. $entity->isNew(false);
  1774. $result = $table->delete($entity);
  1775. $this->assertNull($result);
  1776. }
  1777. /**
  1778. * Test delete beforeDelete return result
  1779. *
  1780. * @return void
  1781. */
  1782. public function testDeleteBeforeDeleteReturnResult() {
  1783. $entity = new \Cake\ORM\Entity(['id' => 1, 'name' => 'mark']);
  1784. $options = new \ArrayObject(['atomic' => true, 'cascade' => true]);
  1785. $mock = $this->getMock('Cake\Event\EventManager');
  1786. $mock->expects($this->once())
  1787. ->method('dispatch')
  1788. ->will($this->returnCallback(function ($event) {
  1789. $event->stopPropagation();
  1790. $event->result = 'got stopped';
  1791. }));
  1792. $table = TableRegistry::get('users', ['eventManager' => $mock]);
  1793. $entity->isNew(false);
  1794. $result = $table->delete($entity);
  1795. $this->assertEquals('got stopped', $result);
  1796. }
  1797. /**
  1798. * Test deleting new entities does nothing.
  1799. *
  1800. * @return void
  1801. */
  1802. public function testDeleteIsNew() {
  1803. $entity = new \Cake\ORM\Entity(['id' => 1, 'name' => 'mark']);
  1804. $table = $this->getMock(
  1805. 'Cake\ORM\Table',
  1806. ['query'],
  1807. [['connection' => $this->connection]]
  1808. );
  1809. $table->expects($this->never())
  1810. ->method('query');
  1811. $entity->isNew(true);
  1812. $result = $table->delete($entity);
  1813. $this->assertFalse($result);
  1814. }
  1815. /**
  1816. * test hasField()
  1817. *
  1818. * @return void
  1819. */
  1820. public function testHasField() {
  1821. $table = TableRegistry::get('articles');
  1822. $this->assertFalse($table->hasField('nope'), 'Should not be there.');
  1823. $this->assertTrue($table->hasField('title'), 'Should be there.');
  1824. $this->assertTrue($table->hasField('body'), 'Should be there.');
  1825. }
  1826. /**
  1827. * Tests that there exists a default validator
  1828. *
  1829. * @return void
  1830. */
  1831. public function testValidatorDefault() {
  1832. $table = new Table();
  1833. $validator = $table->validator();
  1834. $this->assertSame($table, $validator->provider('table'));
  1835. $this->assertInstanceOf('\Cake\Validation\Validator', $validator);
  1836. $default = $table->validator('default');
  1837. $this->assertSame($validator, $default);
  1838. }
  1839. /**
  1840. * Tests that it is possible to define custom validator methods
  1841. *
  1842. * @return void
  1843. */
  1844. public function functionTestValidationWithDefiner() {
  1845. $table = $this->getMock('\Cake\ORM\Table', ['validationForOtherStuff']);
  1846. $table->expects($this->once())->method('validationForOtherStuff')
  1847. ->will($this->returnArgument(0));
  1848. $other = $table->validator('forOtherStuff');
  1849. $this->assertInstanceOf('\Cake\Validation\Validator', $other);
  1850. $this->assertNotSame($other, $table->validator());
  1851. $this->assertSame($table, $other->provider('table'));
  1852. }
  1853. /**
  1854. * Tests that it is possible to set a custom validator under a name
  1855. *
  1856. * @return void
  1857. */
  1858. public function testValidatorSetter() {
  1859. $table = new Table;
  1860. $validator = new \Cake\Validation\Validator;
  1861. $table->validator('other', $validator);
  1862. $this->assertSame($validator, $table->validator('other'));
  1863. $this->assertSame($table, $validator->provider('table'));
  1864. }
  1865. /**
  1866. * Tests saving with validation
  1867. *
  1868. * @return void
  1869. */
  1870. public function testSaveWithValidationError() {
  1871. $entity = new \Cake\ORM\Entity([
  1872. 'username' => 'superuser'
  1873. ]);
  1874. $table = TableRegistry::get('users');
  1875. $table->validator()->validatePresence('password');
  1876. $this->assertFalse($table->save($entity));
  1877. $this->assertNotEmpty($entity->errors('password'));
  1878. $this->assertSame($entity, $table->validator()->provider('entity'));
  1879. $this->assertSame($table, $table->validator()->provider('table'));
  1880. }
  1881. /**
  1882. * Tests saving with validation and field list
  1883. *
  1884. * @return void
  1885. */
  1886. public function testSaveWithValidationErrorAndFieldList() {
  1887. $entity = new \Cake\ORM\Entity([
  1888. 'username' => 'superuser'
  1889. ]);
  1890. $table = TableRegistry::get('users');
  1891. $table->validator()->validatePresence('password');
  1892. $this->assertFalse($table->save($entity));
  1893. $this->assertNotEmpty($entity->errors('password'));
  1894. }
  1895. /**
  1896. * Tests using a custom validation object when saving
  1897. *
  1898. * @return void
  1899. */
  1900. public function testSaveWithDifferentValidator() {
  1901. $entity = new \Cake\ORM\Entity([
  1902. 'username' => 'superuser'
  1903. ]);
  1904. $table = TableRegistry::get('users');
  1905. $validator = (new Validator)->validatePresence('password');
  1906. $table->validator('custom', $validator);
  1907. $this->assertFalse($table->save($entity, ['validate' => 'custom']));
  1908. $this->assertNotEmpty($entity->errors('password'));
  1909. $this->assertSame($entity, $table->save($entity), 'default was not used');
  1910. }
  1911. /**
  1912. * Tests saving with successful validation
  1913. *
  1914. * @return void
  1915. */
  1916. public function testSaveWithValidationSuccess() {
  1917. $entity = new \Cake\ORM\Entity([
  1918. 'username' => 'superuser',
  1919. 'password' => 'hey'
  1920. ]);
  1921. $table = TableRegistry::get('users');
  1922. $table->validator()->validatePresence('password');
  1923. $this->assertSame($entity, $table->save($entity));
  1924. $this->assertEmpty($entity->errors('password'));
  1925. }
  1926. /**
  1927. * Tests beforeValidate event is triggered
  1928. *
  1929. * @return void
  1930. */
  1931. public function testBeforeValidate() {
  1932. $entity = new \Cake\ORM\Entity([
  1933. 'username' => 'superuser'
  1934. ]);
  1935. $table = TableRegistry::get('users');
  1936. $table->eventManager()->attach(function ($ev, $en, $opt, $val) use ($entity) {
  1937. $this->assertSame($entity, $en);
  1938. $this->assertTrue($opt['crazy']);
  1939. $this->assertSame($ev->subject()->validator('default'), $val);
  1940. $val->validatePresence('password');
  1941. }, 'Model.beforeValidate');
  1942. $this->assertFalse($table->save($entity, ['crazy' => true]));
  1943. $this->assertNotEmpty($entity->errors('password'));
  1944. }
  1945. /**
  1946. * Tests that beforeValidate can set the validation result
  1947. *
  1948. * @return void
  1949. */
  1950. public function testBeforeValidateSetResult() {
  1951. $entity = new \Cake\ORM\Entity([
  1952. 'username' => 'superuser'
  1953. ]);
  1954. $table = TableRegistry::get('users');
  1955. $table->eventManager()->attach(function ($ev, $en) {
  1956. $en->errors('username', 'Not good');
  1957. return false;
  1958. }, 'Model.beforeValidate');
  1959. $this->assertFalse($table->save($entity));
  1960. $this->assertEquals(['Not good'], $entity->errors('username'));
  1961. }
  1962. /**
  1963. * Tests that afterValidate is triggered and can set a result
  1964. *
  1965. * @return void
  1966. */
  1967. public function testAfterValidate() {
  1968. $entity = new \Cake\ORM\Entity([
  1969. 'username' => 'superuser',
  1970. 'password' => 'hey'
  1971. ]);
  1972. $table = TableRegistry::get('users');
  1973. $table->validator()->validatePresence('password');
  1974. $table->eventManager()->attach(function ($ev, $en, $opt, $val) use ($entity) {
  1975. $this->assertSame($entity, $en);
  1976. $this->assertTrue($opt['crazy']);
  1977. $this->assertSame($ev->subject()->validator('default'), $val);
  1978. $en->errors('username', 'Not good');
  1979. return false;
  1980. }, 'Model.afterValidate');
  1981. $this->assertFalse($table->save($entity, ['crazy' => true]));
  1982. $this->assertEmpty($entity->errors('password'));
  1983. $this->assertEquals(['Not good'], $entity->errors('username'));
  1984. }
  1985. /**
  1986. * Tests using a custom validation object when saving and saving associations
  1987. *
  1988. * @return void
  1989. */
  1990. public function testSaveWithDifferentValidatorAndAssociations() {
  1991. $entity = new \Cake\ORM\Entity([
  1992. 'title' => 'foo',
  1993. 'body' => 'bar',
  1994. 'author' => new \Cake\ORM\Entity([
  1995. 'name' => 'Susan'
  1996. ]),
  1997. 'articles_tags' => [
  1998. new \Cake\ORM\Entity([
  1999. 'tag_id' => 100
  2000. ])
  2001. ]
  2002. ]);
  2003. $table = TableRegistry::get('articles');
  2004. $table->belongsTo('authors');
  2005. $table->hasMany('ArticlesTags');
  2006. $validator = (new Validator)->validatePresence('body');
  2007. $table->validator('custom', $validator);
  2008. $validator2 = (new Validator)->validatePresence('thing');
  2009. $table->authors->validator('default', $validator2);
  2010. $this->assertFalse($table->save($entity, ['validate' => 'custom']), 'default was not used');
  2011. $this->assertNotEmpty($entity->author->errors('thing'));
  2012. $table->ArticlesTags->validator('default', $validator2);
  2013. unset($entity->author);
  2014. $this->assertFalse($table->save($entity, ['validate' => 'custom']), 'default was not used');
  2015. $this->assertNotEmpty($entity->articles_tags[0]->errors('thing'));
  2016. }
  2017. /**
  2018. * Tests that save validates all entities before persisting.
  2019. *
  2020. * @return void
  2021. */
  2022. public function testSaveValidateAllAssociations() {
  2023. $entity = new \Cake\ORM\Entity([
  2024. 'title' => 'foo',
  2025. 'author' => new \Cake\ORM\Entity([
  2026. 'name' => 'Susan'
  2027. ]),
  2028. 'comments' => [
  2029. new \Cake\ORM\Entity([
  2030. 'comment' => 'the worst!'
  2031. ])
  2032. ]
  2033. ]);
  2034. $table = TableRegistry::get('Articles');
  2035. $table->belongsTo('Authors');
  2036. $table->hasMany('Comments');
  2037. $validator = (new Validator)->validatePresence('body');
  2038. $table->validator('default', $validator);
  2039. $authorValidate = (new Validator)->validatePresence('bio');
  2040. $table->Authors->validator('default', $authorValidate);
  2041. $commentValidate = (new Validator)->validatePresence('author');
  2042. $table->Comments->validator('default', $commentValidate);
  2043. $result = $table->save($entity);
  2044. $this->assertFalse($result, 'Should have failed');
  2045. $this->assertNotEmpty($entity->errors('body'), 'Missing article errors');
  2046. $this->assertNotEmpty($entity->author->errors('bio'), 'Missing author errors');
  2047. $this->assertNotEmpty($entity->comments[0]->errors('author'), 'Missing comment errors');
  2048. }
  2049. /**
  2050. * Test magic findByXX method.
  2051. *
  2052. * @return void
  2053. */
  2054. public function testMagicFindDefaultToAll() {
  2055. $table = TableRegistry::get('Users');
  2056. $result = $table->findByUsername('garrett');
  2057. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2058. $expected = new QueryExpression(['username' => 'garrett'], $this->usersTypeMap);
  2059. $this->assertEquals($expected, $result->clause('where'));
  2060. }
  2061. /**
  2062. * Test magic findByXX errors on missing arguments.
  2063. *
  2064. * @expectedException BadMethodCallException
  2065. * @expectedExceptionMessage Not enough arguments to magic finder. Got 0 required 1
  2066. * @return void
  2067. */
  2068. public function testMagicFindError() {
  2069. $table = TableRegistry::get('Users');
  2070. $table->findByUsername();
  2071. }
  2072. /**
  2073. * Test magic findByXX errors on missing arguments.
  2074. *
  2075. * @expectedException BadMethodCallException
  2076. * @expectedExceptionMessage Not enough arguments to magic finder. Got 1 required 2
  2077. * @return void
  2078. */
  2079. public function testMagicFindErrorMissingField() {
  2080. $table = TableRegistry::get('Users');
  2081. $table->findByUsernameAndId('garrett');
  2082. }
  2083. /**
  2084. * Test magic findByXX errors when there is a mix of or & and.
  2085. *
  2086. * @expectedException BadMethodCallException
  2087. * @expectedExceptionMessage Cannot mix "and" & "or" in a magic finder. Use find() instead.
  2088. * @return void
  2089. */
  2090. public function testMagicFindErrorMixOfOperators() {
  2091. $table = TableRegistry::get('Users');
  2092. $table->findByUsernameAndIdOrPassword('garrett', 1, 'sekret');
  2093. }
  2094. /**
  2095. * Test magic findByXX method.
  2096. *
  2097. * @return void
  2098. */
  2099. public function testMagicFindFirstAnd() {
  2100. $table = TableRegistry::get('Users');
  2101. $result = $table->findByUsernameAndId('garrett', 4);
  2102. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2103. $expected = new QueryExpression(['username' => 'garrett', 'id' => 4], $this->usersTypeMap);
  2104. $this->assertEquals($expected, $result->clause('where'));
  2105. }
  2106. /**
  2107. * Test magic findByXX method.
  2108. *
  2109. * @return void
  2110. */
  2111. public function testMagicFindFirstOr() {
  2112. $table = TableRegistry::get('Users');
  2113. $result = $table->findByUsernameOrId('garrett', 4);
  2114. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2115. $expected = new QueryExpression([], $this->usersTypeMap);
  2116. $expected->add([
  2117. 'OR' => [
  2118. 'username' => 'garrett',
  2119. 'id' => 4
  2120. ]]
  2121. );
  2122. $this->assertEquals($expected, $result->clause('where'));
  2123. }
  2124. /**
  2125. * Test magic findAllByXX method.
  2126. *
  2127. * @return void
  2128. */
  2129. public function testMagicFindAll() {
  2130. $table = TableRegistry::get('Articles');
  2131. $result = $table->findAllByAuthorId(1);
  2132. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2133. $this->assertNull($result->clause('limit'));
  2134. $expected = new QueryExpression(['author_id' => 1], $this->articlesTypeMap);
  2135. $this->assertEquals($expected, $result->clause('where'));
  2136. }
  2137. /**
  2138. * Test magic findAllByXX method.
  2139. *
  2140. * @return void
  2141. */
  2142. public function testMagicFindAllAnd() {
  2143. $table = TableRegistry::get('Users');
  2144. $result = $table->findAllByAuthorIdAndPublished(1, 'Y');
  2145. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2146. $this->assertNull($result->clause('limit'));
  2147. $expected = new QueryExpression(
  2148. ['author_id' => 1, 'published' => 'Y'],
  2149. $this->usersTypeMap
  2150. );
  2151. $this->assertEquals($expected, $result->clause('where'));
  2152. }
  2153. /**
  2154. * Test magic findAllByXX method.
  2155. *
  2156. * @return void
  2157. */
  2158. public function testMagicFindAllOr() {
  2159. $table = TableRegistry::get('Users');
  2160. $result = $table->findAllByAuthorIdOrPublished(1, 'Y');
  2161. $this->assertInstanceOf('Cake\ORM\Query', $result);
  2162. $this->assertNull($result->clause('limit'));
  2163. $expected = new QueryExpression();
  2164. $expected->typeMap()->defaults([
  2165. 'Users.id' => 'integer',
  2166. 'id' => 'integer',
  2167. 'Users.username' => 'string',
  2168. 'username' => 'string',
  2169. 'Users.password' => 'string',
  2170. 'password' => 'string',
  2171. 'Users.created' => 'timestamp',
  2172. 'created' => 'timestamp',
  2173. 'Users.updated' => 'timestamp',
  2174. 'updated' => 'timestamp',
  2175. ]);
  2176. $expected->add(
  2177. ['or' => ['author_id' => 1, 'published' => 'Y']]
  2178. );
  2179. $this->assertEquals($expected, $result->clause('where'));
  2180. $this->assertNull($result->clause('order'));
  2181. }
  2182. /**
  2183. * Test the behavior method.
  2184. *
  2185. * @return void
  2186. */
  2187. public function testBehaviorIntrospection() {
  2188. $table = TableRegistry::get('users');
  2189. $table->addBehavior('Timestamp');
  2190. $this->assertTrue($table->hasBehavior('Timestamp'), 'should be true on loaded behavior');
  2191. $this->assertFalse($table->hasBehavior('Tree'), 'should be false on unloaded behavior');
  2192. }
  2193. /**
  2194. * Tests saving belongsTo association
  2195. *
  2196. * @group save
  2197. * @return void
  2198. */
  2199. public function testSaveBelongsTo() {
  2200. $entity = new \Cake\ORM\Entity([
  2201. 'title' => 'A Title',
  2202. 'body' => 'A body'
  2203. ]);
  2204. $entity->author = new \Cake\ORM\Entity([
  2205. 'name' => 'Jose'
  2206. ]);
  2207. $table = TableRegistry::get('articles');
  2208. $table->belongsTo('authors');
  2209. $this->assertSame($entity, $table->save($entity));
  2210. $this->assertFalse($entity->isNew());
  2211. $this->assertFalse($entity->author->isNew());
  2212. $this->assertEquals(5, $entity->author->id);
  2213. $this->assertEquals(5, $entity->get('author_id'));
  2214. }
  2215. /**
  2216. * Tests saving belongsTo association and get a validation error
  2217. *
  2218. * @group save
  2219. * @return void
  2220. */
  2221. public function testsSaveBelongsToWithValidationError() {
  2222. $entity = new \Cake\ORM\Entity([
  2223. 'title' => 'A Title',
  2224. 'body' => 'A body'
  2225. ]);
  2226. $entity->author = new \Cake\ORM\Entity([
  2227. 'name' => 'Jose'
  2228. ]);
  2229. $table = TableRegistry::get('articles');
  2230. $table->belongsTo('authors');
  2231. $table->association('authors')
  2232. ->target()
  2233. ->validator()
  2234. ->add('name', 'num', ['rule' => 'numeric']);
  2235. $this->assertFalse($table->save($entity));
  2236. $this->assertTrue($entity->isNew());
  2237. $this->assertTrue($entity->author->isNew());
  2238. $this->assertNull($entity->get('author_id'));
  2239. $this->assertNotEmpty($entity->author->errors('name'));
  2240. }
  2241. /**
  2242. * Tests saving hasOne association
  2243. *
  2244. * @group save
  2245. * @return void
  2246. */
  2247. public function testSaveHasOne() {
  2248. $entity = new \Cake\ORM\Entity([
  2249. 'name' => 'Jose'
  2250. ]);
  2251. $entity->article = new \Cake\ORM\Entity([
  2252. 'title' => 'A Title',
  2253. 'body' => 'A body'
  2254. ]);
  2255. $table = TableRegistry::get('authors');
  2256. $table->hasOne('articles');
  2257. $this->assertSame($entity, $table->save($entity));
  2258. $this->assertFalse($entity->isNew());
  2259. $this->assertFalse($entity->article->isNew());
  2260. $this->assertEquals(4, $entity->article->id);
  2261. $this->assertEquals(5, $entity->article->get('author_id'));
  2262. $this->assertFalse($entity->article->dirty('author_id'));
  2263. }
  2264. /**
  2265. * Tests saving associations only saves associations
  2266. * if they are entities.
  2267. *
  2268. * @group save
  2269. * @return void
  2270. */
  2271. public function testSaveOnlySaveAssociatedEntities() {
  2272. $entity = new \Cake\ORM\Entity([
  2273. 'name' => 'Jose'
  2274. ]);
  2275. // Not an entity.
  2276. $entity->article = [
  2277. 'title' => 'A Title',
  2278. 'body' => 'A body'
  2279. ];
  2280. $table = TableRegistry::get('authors');
  2281. $table->hasOne('articles');
  2282. $this->assertFalse($table->save($entity));
  2283. $this->assertTrue($entity->isNew());
  2284. $this->assertInternalType('array', $entity->article);
  2285. }
  2286. /**
  2287. * Tests saving hasOne association and returning a validation error will
  2288. * abort the saving process
  2289. *
  2290. * @group save
  2291. * @return void
  2292. */
  2293. public function testSaveHasOneWithValidationError() {
  2294. $entity = new \Cake\ORM\Entity([
  2295. 'name' => 'Jose'
  2296. ]);
  2297. $entity->article = new \Cake\ORM\Entity([
  2298. 'title' => 'A Title',
  2299. 'body' => 'A body'
  2300. ]);
  2301. $table = TableRegistry::get('authors');
  2302. $table->hasOne('articles');
  2303. $table->association('articles')
  2304. ->target()
  2305. ->validator()
  2306. ->add('title', 'num', ['rule' => 'numeric']);
  2307. $this->assertFalse($table->save($entity));
  2308. $this->assertTrue($entity->isNew());
  2309. $this->assertTrue($entity->article->isNew());
  2310. $this->assertNull($entity->article->id);
  2311. $this->assertNull($entity->article->get('author_id'));
  2312. $this->assertFalse($entity->article->dirty('author_id'));
  2313. $this->assertNotEmpty($entity->article->errors('title'));
  2314. }
  2315. /**
  2316. * Tests saving multiple entities in a hasMany association
  2317. *
  2318. * @return void
  2319. */
  2320. public function testSaveHasMany() {
  2321. $entity = new \Cake\ORM\Entity([
  2322. 'name' => 'Jose'
  2323. ]);
  2324. $entity->articles = [
  2325. new \Cake\ORM\Entity([
  2326. 'title' => 'A Title',
  2327. 'body' => 'A body'
  2328. ]),
  2329. new \Cake\ORM\Entity([
  2330. 'title' => 'Another Title',
  2331. 'body' => 'Another body'
  2332. ])
  2333. ];
  2334. $table = TableRegistry::get('authors');
  2335. $table->hasMany('articles');
  2336. $this->assertSame($entity, $table->save($entity));
  2337. $this->assertFalse($entity->isNew());
  2338. $this->assertFalse($entity->articles[0]->isNew());
  2339. $this->assertFalse($entity->articles[1]->isNew());
  2340. $this->assertEquals(4, $entity->articles[0]->id);
  2341. $this->assertEquals(5, $entity->articles[1]->id);
  2342. $this->assertEquals(5, $entity->articles[0]->author_id);
  2343. $this->assertEquals(5, $entity->articles[1]->author_id);
  2344. }
  2345. /**
  2346. * Tests saving multiple entities in a hasMany association and getting and
  2347. * error while saving one of them. It should abort all the save operation
  2348. * when options are set to defaults
  2349. *
  2350. * @return void
  2351. */
  2352. public function testSaveHasManyWithErrorsAtomic() {
  2353. $entity = new \Cake\ORM\Entity([
  2354. 'name' => 'Jose'
  2355. ]);
  2356. $entity->articles = [
  2357. new \Cake\ORM\Entity([
  2358. 'title' => '1',
  2359. 'body' => 'A body'
  2360. ]),
  2361. new \Cake\ORM\Entity([
  2362. 'title' => 'Another Title',
  2363. 'body' => 'Another body'
  2364. ])
  2365. ];
  2366. $table = TableRegistry::get('authors');
  2367. $table->hasMany('articles');
  2368. $table->association('articles')
  2369. ->target()
  2370. ->validator()
  2371. ->add('title', 'num', ['rule' => 'numeric']);
  2372. $this->assertFalse($table->save($entity));
  2373. $this->assertTrue($entity->isNew());
  2374. $this->assertTrue($entity->articles[0]->isNew());
  2375. $this->assertTrue($entity->articles[1]->isNew());
  2376. $this->assertNull($entity->articles[0]->id);
  2377. $this->assertNull($entity->articles[1]->id);
  2378. $this->assertNull($entity->articles[0]->author_id);
  2379. $this->assertNull($entity->articles[1]->author_id);
  2380. $this->assertEmpty($entity->articles[0]->errors());
  2381. $this->assertNotEmpty($entity->articles[1]->errors());
  2382. }
  2383. /**
  2384. * Tests that it is possible to continue saving hasMany associations
  2385. * even if any of the records fail validation when atomic is set
  2386. * to false
  2387. *
  2388. * @return void
  2389. */
  2390. public function testSaveHasManyWithErrorsNonAtomic() {
  2391. $entity = new \Cake\ORM\Entity([
  2392. 'name' => 'Jose'
  2393. ]);
  2394. $entity->articles = [
  2395. new \Cake\ORM\Entity([
  2396. 'title' => 'A title',
  2397. 'body' => 'A body'
  2398. ]),
  2399. new \Cake\ORM\Entity([
  2400. 'title' => '1',
  2401. 'body' => 'Another body'
  2402. ])
  2403. ];
  2404. $table = TableRegistry::get('authors');
  2405. $table->hasMany('articles');
  2406. $table->association('articles')
  2407. ->target()
  2408. ->validator()
  2409. ->add('title', 'num', ['rule' => 'numeric']);
  2410. $result = $table->save($entity, ['atomic' => false]);
  2411. $this->assertFalse($result, 'Validation failed, no save.');
  2412. $this->assertTrue($entity->isNew());
  2413. $this->assertTrue($entity->articles[0]->isNew());
  2414. $this->assertTrue($entity->articles[1]->isNew());
  2415. $this->assertNull($entity->articles[1]->id);
  2416. $this->assertNull($entity->articles[0]->id);
  2417. $this->assertNull($entity->articles[0]->author_id);
  2418. $this->assertNull($entity->articles[1]->author_id);
  2419. }
  2420. /**
  2421. * Tests saving hasOne association and returning a validation error will
  2422. * not abort the saving process if atomic is set to false
  2423. *
  2424. * @group save
  2425. * @return void
  2426. */
  2427. public function testSaveHasOneWithValidationErrorNonAtomic() {
  2428. $entity = new \Cake\ORM\Entity([
  2429. 'name' => 'Jose'
  2430. ]);
  2431. $entity->article = new \Cake\ORM\Entity([
  2432. 'title' => 'A Title',
  2433. 'body' => 'A body'
  2434. ]);
  2435. $table = TableRegistry::get('authors');
  2436. $table->hasOne('articles');
  2437. $table->association('articles')
  2438. ->target()
  2439. ->validator()
  2440. ->add('title', 'num', ['rule' => 'numeric']);
  2441. $result = $table->save($entity, ['atomic' => false]);
  2442. $this->assertFalse($result, 'Validation failed, no save.');
  2443. $this->assertTrue($entity->isNew());
  2444. $this->assertTrue($entity->article->isNew());
  2445. $this->assertNull($entity->article->id);
  2446. $this->assertNull($entity->article->get('author_id'));
  2447. $this->assertFalse($entity->article->dirty('author_id'));
  2448. $this->assertNotEmpty($entity->article->errors('title'));
  2449. }
  2450. /**
  2451. * Tests saving belongsTo association and get a validation error won't stop
  2452. * saving if atomic is set to false
  2453. *
  2454. * @group save
  2455. * @return void
  2456. */
  2457. public function testSaveBelongsToWithValidationErrorNotAtomic() {
  2458. $entity = new \Cake\ORM\Entity([
  2459. 'title' => 'A Title',
  2460. 'body' => 'A body'
  2461. ]);
  2462. $entity->author = new \Cake\ORM\Entity([
  2463. 'name' => 'Jose'
  2464. ]);
  2465. $table = TableRegistry::get('articles');
  2466. $table->belongsTo('authors');
  2467. $table->association('authors')
  2468. ->target()
  2469. ->validator()
  2470. ->add('name', 'num', ['rule' => 'numeric']);
  2471. $result = $table->save($entity, ['atomic' => false]);
  2472. $this->assertFalse($result, 'Validation failed, no save');
  2473. $this->assertTrue($entity->isNew());
  2474. $this->assertTrue($entity->author->isNew());
  2475. $this->assertNull($entity->get('author_id'));
  2476. $this->assertNotEmpty($entity->author->errors('name'));
  2477. }
  2478. /**
  2479. * Tests saving belongsToMany records
  2480. *
  2481. * @group save
  2482. * @return void
  2483. */
  2484. public function testSaveBelongsToMany() {
  2485. $entity = new \Cake\ORM\Entity([
  2486. 'title' => 'A Title',
  2487. 'body' => 'A body'
  2488. ]);
  2489. $entity->tags = [
  2490. new \Cake\ORM\Entity([
  2491. 'name' => 'Something New'
  2492. ]),
  2493. new \Cake\ORM\Entity([
  2494. 'name' => 'Another Something'
  2495. ])
  2496. ];
  2497. $table = TableRegistry::get('articles');
  2498. $table->belongsToMany('tags');
  2499. $this->assertSame($entity, $table->save($entity));
  2500. $this->assertFalse($entity->isNew());
  2501. $this->assertFalse($entity->tags[0]->isNew());
  2502. $this->assertFalse($entity->tags[1]->isNew());
  2503. $this->assertEquals(4, $entity->tags[0]->id);
  2504. $this->assertEquals(5, $entity->tags[1]->id);
  2505. $this->assertEquals(4, $entity->tags[0]->_joinData->article_id);
  2506. $this->assertEquals(4, $entity->tags[1]->_joinData->article_id);
  2507. $this->assertEquals(4, $entity->tags[0]->_joinData->tag_id);
  2508. $this->assertEquals(5, $entity->tags[1]->_joinData->tag_id);
  2509. }
  2510. /**
  2511. * Tests saving belongsToMany records can delete all links.
  2512. *
  2513. * @group save
  2514. * @return void
  2515. */
  2516. public function testSaveBelongsToManyDeleteAllLinks() {
  2517. $table = TableRegistry::get('articles');
  2518. $table->belongsToMany('tags', [
  2519. 'saveStrategy' => 'replace',
  2520. ]);
  2521. $entity = $table->get(1, ['contain' => 'Tags']);
  2522. $this->assertCount(2, $entity->tags, 'Fixture data did not change.');
  2523. $entity->tags = [];
  2524. $result = $table->save($entity);
  2525. $this->assertSame($result, $entity);
  2526. $this->assertSame([], $entity->tags, 'No tags on the entity.');
  2527. $entity = $table->get(1, ['contain' => 'Tags']);
  2528. $this->assertSame([], $entity->tags, 'No tags in the db either.');
  2529. }
  2530. /**
  2531. * Tests saving belongsToMany records can delete some links.
  2532. *
  2533. * @group save
  2534. * @return void
  2535. */
  2536. public function testSaveBelongsToManyDeleteSomeLinks() {
  2537. $table = TableRegistry::get('articles');
  2538. $table->belongsToMany('tags', [
  2539. 'saveStrategy' => 'replace',
  2540. ]);
  2541. $entity = $table->get(1, ['contain' => 'Tags']);
  2542. $this->assertCount(2, $entity->tags, 'Fixture data did not change.');
  2543. $tag = new \Cake\ORM\Entity([
  2544. 'id' => 2,
  2545. ]);
  2546. $entity->tags = [$tag];
  2547. $result = $table->save($entity);
  2548. $this->assertSame($result, $entity);
  2549. $this->assertCount(1, $entity->tags, 'Only one tag left.');
  2550. $this->assertEquals($tag, $entity->tags[0]);
  2551. $entity = $table->get(1, ['contain' => 'Tags']);
  2552. $this->assertCount(1, $entity->tags, 'Only one tag in the db.');
  2553. $this->assertEquals($tag->id, $entity->tags[0]->id);
  2554. }
  2555. /**
  2556. * Tests saving belongsToMany records with a validation error and atomic set
  2557. * to true
  2558. *
  2559. * @group save
  2560. * @return void
  2561. */
  2562. public function testSaveBelongsToManyWithValidationErrorAtomic() {
  2563. $entity = new \Cake\ORM\Entity([
  2564. 'title' => 'A Title',
  2565. 'body' => 'A body'
  2566. ]);
  2567. $entity->tags = [
  2568. new \Cake\ORM\Entity([
  2569. 'name' => '100'
  2570. ]),
  2571. new \Cake\ORM\Entity([
  2572. 'name' => 'Something New'
  2573. ])
  2574. ];
  2575. $table = TableRegistry::get('articles');
  2576. $table->belongsToMany('tags');
  2577. $tags = $table->association('tags')
  2578. ->target()
  2579. ->validator()
  2580. ->add('name', 'num', ['rule' => 'numeric']);
  2581. $this->assertFalse($table->save($entity));
  2582. $this->assertTrue($entity->isNew());
  2583. $this->assertTrue($entity->tags[0]->isNew());
  2584. $this->assertTrue($entity->tags[1]->isNew());
  2585. $this->assertNull($entity->tags[0]->id);
  2586. $this->assertNull($entity->tags[1]->id);
  2587. $this->assertNull($entity->tags[0]->_joinData);
  2588. $this->assertNull($entity->tags[1]->_joinData);
  2589. $this->assertEmpty($entity->tags[0]->errors('name'));
  2590. $this->assertNotEmpty($entity->tags[1]->errors('name'));
  2591. }
  2592. /**
  2593. * Tests saving belongsToMany records with a validation error and atomic set
  2594. * to false
  2595. *
  2596. * @group save
  2597. * @return void
  2598. */
  2599. public function testSaveBelongsToManyWithValidationErrorNonAtomic() {
  2600. $entity = new \Cake\ORM\Entity([
  2601. 'title' => 'A Title',
  2602. 'body' => 'A body'
  2603. ]);
  2604. $entity->tags = [
  2605. new \Cake\ORM\Entity([
  2606. 'name' => 'Something New'
  2607. ]),
  2608. new \Cake\ORM\Entity([
  2609. 'name' => '100'
  2610. ])
  2611. ];
  2612. $table = TableRegistry::get('articles');
  2613. $table->belongsToMany('tags');
  2614. $tags = $table->association('tags')
  2615. ->target()
  2616. ->validator()
  2617. ->add('name', 'num', ['rule' => 'numeric']);
  2618. $result = $table->save($entity, ['atomic' => false]);
  2619. $this->assertFalse($result, 'HABTM validation failed, save aborted');
  2620. $this->assertTrue($entity->isNew());
  2621. $this->assertTrue($entity->tags[0]->isNew());
  2622. $this->assertTrue($entity->tags[1]->isNew());
  2623. $this->assertNull($entity->tags[0]->id);
  2624. $this->assertNull($entity->tags[1]->id);
  2625. $this->assertNull($entity->tags[0]->_joinData);
  2626. $this->assertNull($entity->tags[1]->_joinData);
  2627. }
  2628. /**
  2629. * Tests saving belongsToMany records with a validation error in a joint entity
  2630. *
  2631. * @group save
  2632. * @return void
  2633. */
  2634. public function testSaveBelongsToManyWithValidationErrorInJointEntity() {
  2635. $entity = new \Cake\ORM\Entity([
  2636. 'title' => 'A Title',
  2637. 'body' => 'A body'
  2638. ]);
  2639. $entity->tags = [
  2640. new \Cake\ORM\Entity([
  2641. 'name' => 'Something New'
  2642. ]),
  2643. new \Cake\ORM\Entity([
  2644. 'name' => '100'
  2645. ])
  2646. ];
  2647. $table = TableRegistry::get('articles');
  2648. $table->belongsToMany('tags');
  2649. $table->association('tags')
  2650. ->junction()
  2651. ->validator()
  2652. ->add('article_id', 'num', ['rule' => ['comparison', '>', 4]]);
  2653. $this->assertFalse($table->save($entity));
  2654. $this->assertTrue($entity->isNew());
  2655. $this->assertTrue($entity->tags[0]->isNew());
  2656. $this->assertTrue($entity->tags[1]->isNew());
  2657. $this->assertNull($entity->tags[0]->id);
  2658. $this->assertNull($entity->tags[1]->id);
  2659. $this->assertNull($entity->tags[0]->_joinData);
  2660. $this->assertNull($entity->tags[1]->_joinData);
  2661. }
  2662. /**
  2663. * Tests saving belongsToMany records with a validation error in a joint entity
  2664. * and atomic set to false
  2665. *
  2666. * @group save
  2667. * @return void
  2668. */
  2669. public function testSaveBelongsToManyWithValidationErrorInJointEntityNonAtomic() {
  2670. $entity = new \Cake\ORM\Entity([
  2671. 'title' => 'A Title',
  2672. 'body' => 'A body'
  2673. ]);
  2674. $entity->tags = [
  2675. new \Cake\ORM\Entity([
  2676. 'name' => 'Something New'
  2677. ]),
  2678. new \Cake\ORM\Entity([
  2679. 'name' => 'New one'
  2680. ])
  2681. ];
  2682. $table = TableRegistry::get('articles');
  2683. $table->belongsToMany('tags');
  2684. $table->association('tags')
  2685. ->junction()
  2686. ->validator()
  2687. ->add('tag_id', 'num', ['rule' => ['comparison', '>', 4]]);
  2688. $this->assertSame($entity, $table->save($entity, ['atomic' => false]));
  2689. $this->assertFalse($entity->isNew());
  2690. $this->assertFalse($entity->tags[0]->isNew());
  2691. $this->assertFalse($entity->tags[1]->isNew());
  2692. $this->assertEquals(4, $entity->tags[0]->id);
  2693. $this->assertEquals(5, $entity->tags[1]->id);
  2694. $this->assertTrue($entity->tags[0]->_joinData->isNew());
  2695. $this->assertNotEmpty($entity->tags[0]->_joinData->errors());
  2696. $this->assertEquals(4, $entity->tags[1]->_joinData->article_id);
  2697. $this->assertEquals(5, $entity->tags[1]->_joinData->tag_id);
  2698. }
  2699. /**
  2700. * Tests that saving a persisted and clean entity will is a no-op
  2701. *
  2702. * @group save
  2703. * @return void
  2704. */
  2705. public function testSaveCleanEntity() {
  2706. $table = $this->getMock('\Cake\ORM\Table', ['_processSave']);
  2707. $entity = new \Cake\ORM\Entity(
  2708. ['id' => 'foo'],
  2709. ['markNew' => false, 'markClean' => true]
  2710. );
  2711. $table->expects($this->never())->method('_processSave');
  2712. $this->assertSame($entity, $table->save($entity));
  2713. }
  2714. /**
  2715. * Integration test to show how to append a new tag to an article
  2716. *
  2717. * @group save
  2718. * @return void
  2719. */
  2720. public function testBelongsToManyIntegration() {
  2721. $table = TableRegistry::get('articles');
  2722. $table->belongsToMany('tags');
  2723. $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2724. $tags = $article->tags;
  2725. $this->assertNotEmpty($tags);
  2726. $tags[] = new \TestApp\Model\Entity\Tag(['name' => 'Something New']);
  2727. $article->tags = $tags;
  2728. $this->assertSame($article, $table->save($article));
  2729. $tags = $article->tags;
  2730. $this->assertCount(3, $tags);
  2731. $this->assertFalse($tags[2]->isNew());
  2732. $this->assertEquals(4, $tags[2]->id);
  2733. $this->assertEquals(1, $tags[2]->_joinData->article_id);
  2734. $this->assertEquals(4, $tags[2]->_joinData->tag_id);
  2735. }
  2736. /**
  2737. * Tests that it is possible to do a deep save and control what associations get saved,
  2738. * while having control of the options passed to each level of the save
  2739. *
  2740. * @group save
  2741. * @return void
  2742. */
  2743. public function testSaveDeepAssociationOptions() {
  2744. $articles = $this->getMock(
  2745. '\Cake\ORM\Table',
  2746. ['_insert'],
  2747. [['table' => 'articles', 'connection' => $this->connection]]
  2748. );
  2749. $authors = $this->getMock(
  2750. '\Cake\ORM\Table',
  2751. ['_insert', 'validate'],
  2752. [['table' => 'authors', 'connection' => $this->connection]]
  2753. );
  2754. $supervisors = $this->getMock(
  2755. '\Cake\ORM\Table',
  2756. ['_insert', 'validate'],
  2757. [[
  2758. 'table' => 'authors',
  2759. 'alias' => 'supervisors',
  2760. 'connection' => $this->connection
  2761. ]]
  2762. );
  2763. $tags = $this->getMock(
  2764. '\Cake\ORM\Table',
  2765. ['_insert'],
  2766. [['table' => 'tags', 'connection' => $this->connection]]
  2767. );
  2768. $articles->belongsTo('authors', ['targetTable' => $authors]);
  2769. $authors->hasOne('supervisors', ['targetTable' => $supervisors]);
  2770. $supervisors->belongsToMany('tags', ['targetTable' => $tags]);
  2771. $entity = new \Cake\ORM\Entity([
  2772. 'title' => 'bar',
  2773. 'author' => new \Cake\ORM\Entity([
  2774. 'name' => 'Juan',
  2775. 'supervisor' => new \Cake\ORM\Entity(['name' => 'Marc']),
  2776. 'tags' => [
  2777. new \Cake\ORM\Entity(['name' => 'foo'])
  2778. ]
  2779. ]),
  2780. ]);
  2781. $entity->isNew(true);
  2782. $entity->author->isNew(true);
  2783. $entity->author->supervisor->isNew(true);
  2784. $entity->author->tags[0]->isNew(true);
  2785. $articles->expects($this->once())
  2786. ->method('_insert')
  2787. ->with($entity, ['title' => 'bar'])
  2788. ->will($this->returnValue($entity));
  2789. $authors->expects($this->once())
  2790. ->method('_insert')
  2791. ->with($entity->author, ['name' => 'Juan'])
  2792. ->will($this->returnValue($entity->author));
  2793. $authors->expects($this->once())
  2794. ->method('validate')
  2795. ->with($entity->author)
  2796. ->will($this->returnValue(true));
  2797. $supervisors->expects($this->once())
  2798. ->method('_insert')
  2799. ->with($entity->author->supervisor, ['name' => 'Marc'])
  2800. ->will($this->returnValue($entity->author->supervisor));
  2801. $supervisors->expects($this->never())->method('validate');
  2802. $tags->expects($this->never())->method('_insert');
  2803. $this->assertSame($entity, $articles->save($entity, [
  2804. 'associated' => [
  2805. 'authors' => [
  2806. 'validate' => true
  2807. ],
  2808. 'authors.supervisors' => [
  2809. 'atomic' => false,
  2810. 'validate' => false,
  2811. 'associated' => false
  2812. ]
  2813. ]
  2814. ]));
  2815. }
  2816. /**
  2817. * Integration test for linking entities with belongsToMany
  2818. *
  2819. * @return void
  2820. */
  2821. public function testLinkBelongsToMany() {
  2822. $table = TableRegistry::get('articles');
  2823. $table->belongsToMany('tags');
  2824. $tagsTable = TableRegistry::get('tags');
  2825. $source = ['source' => 'tags'];
  2826. $options = ['markNew' => false];
  2827. $article = new \Cake\ORM\Entity([
  2828. 'id' => 1,
  2829. ], $options);
  2830. $newTag = new \TestApp\Model\Entity\Tag([
  2831. 'name' => 'Foo'
  2832. ], $source);
  2833. $tags[] = new \TestApp\Model\Entity\Tag([
  2834. 'id' => 3
  2835. ], $options + $source);
  2836. $tags[] = $newTag;
  2837. $tagsTable->save($newTag);
  2838. $table->association('tags')->link($article, $tags);
  2839. $this->assertEquals($article->tags, $tags);
  2840. foreach ($tags as $tag) {
  2841. $this->assertFalse($tag->isNew());
  2842. }
  2843. $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2844. $this->assertEquals($article->tags[2]->id, $tags[0]->id);
  2845. $this->assertEquals($article->tags[3], $tags[1]);
  2846. }
  2847. /**
  2848. * Integration test to show how to unlink a single record from a belongsToMany
  2849. *
  2850. * @return void
  2851. */
  2852. public function testUnlinkBelongsToMany() {
  2853. $table = TableRegistry::get('articles');
  2854. $table->belongsToMany('tags');
  2855. $tagsTable = TableRegistry::get('tags');
  2856. $options = ['markNew' => false];
  2857. $article = $table->find('all')
  2858. ->where(['id' => 1])
  2859. ->contain(['tags'])->first();
  2860. $table->association('tags')->unlink($article, [$article->tags[0]]);
  2861. $this->assertCount(1, $article->tags);
  2862. $this->assertEquals(2, $article->tags[0]->get('id'));
  2863. $this->assertFalse($article->dirty('tags'));
  2864. }
  2865. /**
  2866. * Integration test to show how to unlink multiple records from a belongsToMany
  2867. *
  2868. * @return void
  2869. */
  2870. public function testUnlinkBelongsToManyMultiple() {
  2871. $table = TableRegistry::get('articles');
  2872. $table->belongsToMany('tags');
  2873. $tagsTable = TableRegistry::get('tags');
  2874. $options = ['markNew' => false];
  2875. $article = new \Cake\ORM\Entity(['id' => 1], $options);
  2876. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 1], $options);
  2877. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 2], $options);
  2878. $table->association('tags')->unlink($article, $tags);
  2879. $left = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2880. $this->assertEmpty($left->tags);
  2881. }
  2882. /**
  2883. * Integration test to show how to unlink multiple records from a belongsToMany
  2884. * providing some of the joint
  2885. *
  2886. * @return void
  2887. */
  2888. public function testUnlinkBelongsToManyPassingJoint() {
  2889. $table = TableRegistry::get('articles');
  2890. $table->belongsToMany('tags');
  2891. $tagsTable = TableRegistry::get('tags');
  2892. $options = ['markNew' => false];
  2893. $article = new \Cake\ORM\Entity(['id' => 1], $options);
  2894. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 1], $options);
  2895. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 2], $options);
  2896. $tags[1]->_joinData = new \Cake\ORM\Entity([
  2897. 'article_id' => 1,
  2898. 'tag_id' => 2
  2899. ], $options);
  2900. $table->association('tags')->unlink($article, $tags);
  2901. $left = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2902. $this->assertEmpty($left->tags);
  2903. }
  2904. /**
  2905. * Integration test to show how to replace records from a belongsToMany
  2906. *
  2907. * @return void
  2908. */
  2909. public function testReplacelinksBelongsToMany() {
  2910. $table = TableRegistry::get('articles');
  2911. $table->belongsToMany('tags');
  2912. $tagsTable = TableRegistry::get('tags');
  2913. $options = ['markNew' => false];
  2914. $article = new \Cake\ORM\Entity(['id' => 1], $options);
  2915. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 2], $options);
  2916. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 3], $options);
  2917. $tags[] = new \TestApp\Model\Entity\Tag(['name' => 'foo']);
  2918. $table->association('tags')->replaceLinks($article, $tags);
  2919. $this->assertEquals(2, $article->tags[0]->id);
  2920. $this->assertEquals(3, $article->tags[1]->id);
  2921. $this->assertEquals(4, $article->tags[2]->id);
  2922. $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2923. $this->assertCount(3, $article->tags);
  2924. $this->assertEquals(2, $article->tags[0]->id);
  2925. $this->assertEquals(3, $article->tags[1]->id);
  2926. $this->assertEquals(4, $article->tags[2]->id);
  2927. $this->assertEquals('foo', $article->tags[2]->name);
  2928. }
  2929. /**
  2930. * Integration test to show how remove all links from a belongsToMany
  2931. *
  2932. * @return void
  2933. */
  2934. public function testReplacelinksBelongsToManyWithEmpty() {
  2935. $table = TableRegistry::get('articles');
  2936. $table->belongsToMany('tags');
  2937. $tagsTable = TableRegistry::get('tags');
  2938. $options = ['markNew' => false];
  2939. $article = new \Cake\ORM\Entity(['id' => 1], $options);
  2940. $tags = [];
  2941. $table->association('tags')->replaceLinks($article, $tags);
  2942. $this->assertSame($tags, $article->tags);
  2943. $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2944. $this->assertEmpty($article->tags);
  2945. }
  2946. /**
  2947. * Integration test to show how to replace records from a belongsToMany
  2948. * passing the joint property along in the target entity
  2949. *
  2950. * @return void
  2951. */
  2952. public function testReplacelinksBelongsToManyWithJoint() {
  2953. $table = TableRegistry::get('articles');
  2954. $table->belongsToMany('tags');
  2955. $tagsTable = TableRegistry::get('tags');
  2956. $options = ['markNew' => false];
  2957. $article = new \Cake\ORM\Entity(['id' => 1], $options);
  2958. $tags[] = new \TestApp\Model\Entity\Tag([
  2959. 'id' => 2,
  2960. '_joinData' => new \Cake\ORM\Entity([
  2961. 'article_id' => 1,
  2962. 'tag_id' => 2,
  2963. ])
  2964. ], $options);
  2965. $tags[] = new \TestApp\Model\Entity\Tag(['id' => 3], $options);
  2966. $table->association('tags')->replaceLinks($article, $tags);
  2967. $this->assertSame($tags, $article->tags);
  2968. $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
  2969. $this->assertCount(2, $article->tags);
  2970. $this->assertEquals(2, $article->tags[0]->id);
  2971. $this->assertEquals(3, $article->tags[1]->id);
  2972. }
  2973. /**
  2974. * Tests that it is possible to call find with no arguments
  2975. *
  2976. * @return void
  2977. */
  2978. public function testSimplifiedFind() {
  2979. $table = $this->getMock(
  2980. '\Cake\ORM\Table',
  2981. ['callFinder'],
  2982. [[
  2983. 'connection' => $this->connection,
  2984. 'schema' => ['id' => ['type' => 'integer']]
  2985. ]]
  2986. );
  2987. $query = (new \Cake\ORM\Query($this->connection, $table))->select();
  2988. $table->expects($this->once())->method('callFinder')
  2989. ->with('all', $query, []);
  2990. $table->find();
  2991. }
  2992. /**
  2993. * Test that get() will use the primary key for searching and return the first
  2994. * entity found
  2995. *
  2996. * @return void
  2997. */
  2998. public function testGet() {
  2999. $table = $this->getMock(
  3000. '\Cake\ORM\Table',
  3001. ['callFinder', 'query'],
  3002. [[
  3003. 'connection' => $this->connection,
  3004. 'schema' => [
  3005. 'id' => ['type' => 'integer'],
  3006. 'bar' => ['type' => 'integer'],
  3007. '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['bar']]]
  3008. ]
  3009. ]]
  3010. );
  3011. $query = $this->getMock(
  3012. '\Cake\ORM\Query',
  3013. ['addDefaultTypes', 'first', 'where'],
  3014. [$this->connection, $table]
  3015. );
  3016. $entity = new \Cake\ORM\Entity;
  3017. $table->expects($this->once())->method('query')
  3018. ->will($this->returnValue($query));
  3019. $table->expects($this->once())->method('callFinder')
  3020. ->with('all', $query, ['fields' => ['id']])
  3021. ->will($this->returnValue($query));
  3022. $query->expects($this->once())->method('where')
  3023. ->with([$table->alias() . '.bar' => 10])
  3024. ->will($this->returnSelf());
  3025. $query->expects($this->once())->method('first')
  3026. ->will($this->returnValue($entity));
  3027. $result = $table->get(10, ['fields' => ['id']]);
  3028. $this->assertSame($entity, $result);
  3029. }
  3030. /**
  3031. * Tests that get() will throw an exception if the record was not found
  3032. *
  3033. * @expectedException \Cake\ORM\Exception\RecordNotFoundException
  3034. * @expectedExceptionMessage Record "10" not found in table "articles"
  3035. * @return void
  3036. */
  3037. public function testGetNotFoundException() {
  3038. $table = new Table([
  3039. 'name' => 'Articles',
  3040. 'connection' => $this->connection,
  3041. 'table' => 'articles',
  3042. ]);
  3043. $table->get(10);
  3044. }
  3045. /**
  3046. * Test that an exception is raised when there are not enough keys.
  3047. *
  3048. * @expectedException \InvalidArgumentException
  3049. * @expectedExceptionMessage Incorrect number of primary key values. Expected 1 got 0.
  3050. * @return void
  3051. */
  3052. public function testGetExceptionOnIncorrectData() {
  3053. $table = new Table([
  3054. 'name' => 'Articles',
  3055. 'connection' => $this->connection,
  3056. 'table' => 'articles',
  3057. ]);
  3058. $table->get(null);
  3059. }
  3060. /**
  3061. * Tests entityValidator
  3062. *
  3063. * @return void
  3064. */
  3065. public function testEntityValidator() {
  3066. $table = new Table;
  3067. $expected = new \Cake\ORM\EntityValidator($table);
  3068. $table->entityValidator();
  3069. $this->assertEquals($expected, $table->entityValidator());
  3070. }
  3071. /**
  3072. * Tests that validate will call the entity validator with the correct
  3073. * options
  3074. *
  3075. * @return void
  3076. */
  3077. public function testValidateDefaultAssociations() {
  3078. $table = $this->getMock('\Cake\ORM\Table', ['entityValidator']);
  3079. $table->belongsTo('users');
  3080. $table->hasMany('articles');
  3081. $table->schema([]);
  3082. $entityValidator = $this->getMock('\Cake\ORM\EntityValidator', [], [$table]);
  3083. $entity = $table->newEntity([]);
  3084. $table->expects($this->once())->method('entityValidator')
  3085. ->will($this->returnValue($entityValidator));
  3086. $entityValidator->expects($this->once())->method('one')
  3087. ->with($entity, ['associated' => ['users', 'articles']])
  3088. ->will($this->returnValue(true));
  3089. $this->assertTrue($table->validate($entity));
  3090. }
  3091. /**
  3092. * Tests that validate will call the entity validator with the correct
  3093. * options
  3094. *
  3095. * @return void
  3096. */
  3097. public function testValidateWithCustomOptions() {
  3098. $table = $this->getMock('\Cake\ORM\Table', ['entityValidator']);
  3099. $table->schema([]);
  3100. $entityValidator = $this->getMock('\Cake\ORM\EntityValidator', [], [$table]);
  3101. $entity = $table->newEntity([]);
  3102. $options = ['associated' => ['users'], 'validate' => 'foo'];
  3103. $table->expects($this->once())->method('entityValidator')
  3104. ->will($this->returnValue($entityValidator));
  3105. $entityValidator->expects($this->once())->method('one')
  3106. ->with($entity, $options)
  3107. ->will($this->returnValue(false));
  3108. $this->assertFalse($table->validate($entity, $options));
  3109. }
  3110. /**
  3111. * Tests that validateMany will call the entity validator with the correct
  3112. * options
  3113. *
  3114. * @return void
  3115. */
  3116. public function testValidateManyDefaultAssociation() {
  3117. $table = $this->getMock('\Cake\ORM\Table', ['entityValidator']);
  3118. $table->belongsTo('users');
  3119. $table->hasMany('articles');
  3120. $table->schema([]);
  3121. $entityValidator = $this->getMock('\Cake\ORM\EntityValidator', [], [$table]);
  3122. $entities = ['a', 'b'];
  3123. $table->expects($this->once())->method('entityValidator')
  3124. ->will($this->returnValue($entityValidator));
  3125. $entityValidator->expects($this->once())->method('many')
  3126. ->with($entities, ['associated' => ['users', 'articles']])
  3127. ->will($this->returnValue(true));
  3128. $this->assertTrue($table->validateMany($entities));
  3129. }
  3130. /**
  3131. * Tests that validateMany will call the entity validator with the correct
  3132. * options
  3133. *
  3134. * @return void
  3135. */
  3136. public function testValidateManyWithCustomOptions() {
  3137. $table = $this->getMock('\Cake\ORM\Table', ['entityValidator']);
  3138. $table->schema([]);
  3139. $entityValidator = $this->getMock('\Cake\ORM\EntityValidator', [], [$table]);
  3140. $entities = ['a', 'b', 'c'];
  3141. $options = ['associated' => ['users'], 'validate' => 'foo'];
  3142. $table->expects($this->once())->method('entityValidator')
  3143. ->will($this->returnValue($entityValidator));
  3144. $entityValidator->expects($this->once())->method('many')
  3145. ->with($entities, $options)
  3146. ->will($this->returnValue(false));
  3147. $this->assertFalse($table->validateMany($entities, $options));
  3148. }
  3149. /**
  3150. * Tests that patchEntity delegates the task to the marshaller and passed
  3151. * all associations
  3152. *
  3153. * @return void
  3154. */
  3155. public function testPatchEntity() {
  3156. $table = $this->getMock('Cake\ORM\Table', ['marshaller']);
  3157. $marshaller = $this->getMock('Cake\ORM\Marshaller', [], [$table]);
  3158. $table->belongsTo('users');
  3159. $table->hasMany('articles');
  3160. $table->expects($this->once())->method('marshaller')
  3161. ->will($this->returnValue($marshaller));
  3162. $entity = new \Cake\ORM\Entity;
  3163. $data = ['foo' => 'bar'];
  3164. $marshaller->expects($this->once())
  3165. ->method('merge')
  3166. ->with($entity, $data, ['associated' => ['users', 'articles']])
  3167. ->will($this->returnValue($entity));
  3168. $table->patchEntity($entity, $data);
  3169. }
  3170. /**
  3171. * Tests that patchEntities delegates the task to the marshaller and passed
  3172. * all associations
  3173. *
  3174. * @return void
  3175. */
  3176. public function testPatchEntities() {
  3177. $table = $this->getMock('Cake\ORM\Table', ['marshaller']);
  3178. $marshaller = $this->getMock('Cake\ORM\Marshaller', [], [$table]);
  3179. $table->belongsTo('users');
  3180. $table->hasMany('articles');
  3181. $table->expects($this->once())->method('marshaller')
  3182. ->will($this->returnValue($marshaller));
  3183. $entities = [new \Cake\ORM\Entity];
  3184. $data = [['foo' => 'bar']];
  3185. $marshaller->expects($this->once())
  3186. ->method('mergeMany')
  3187. ->with($entities, $data, ['associated' => ['users', 'articles']])
  3188. ->will($this->returnValue($entities));
  3189. $table->patchEntities($entities, $data);
  3190. }
  3191. /**
  3192. * Tests __debugInfo
  3193. *
  3194. * @return void
  3195. */
  3196. public function testDebugInfo() {
  3197. $articles = TableRegistry::get('articles');
  3198. $articles->addBehavior('Timestamp');
  3199. $result = $articles->__debugInfo();
  3200. $expected = [
  3201. 'table' => 'articles',
  3202. 'alias' => 'articles',
  3203. 'entityClass' => 'TestApp\Model\Entity\Article',
  3204. 'associations' => ['authors', 'tags', 'articlestags'],
  3205. 'behaviors' => ['Timestamp'],
  3206. 'defaultConnection' => 'default',
  3207. 'connectionName' => 'test'
  3208. ];
  3209. $this->assertEquals($expected, $result);
  3210. }
  3211. /**
  3212. * Test the findOrCreate method.
  3213. *
  3214. * @return void
  3215. */
  3216. public function testFindOrCreate() {
  3217. $articles = TableRegistry::get('Articles');
  3218. $article = $articles->findOrCreate(['title' => 'Not there'], function ($article) {
  3219. $article->body = 'New body';
  3220. });
  3221. $this->assertFalse($article->isNew());
  3222. $this->assertNotNull($article->id);
  3223. $this->assertEquals('Not there', $article->title);
  3224. $this->assertEquals('New body', $article->body);
  3225. $article = $articles->findOrCreate(['title' => 'Not there']);
  3226. $this->assertFalse($article->isNew());
  3227. $this->assertNotNull($article->id);
  3228. $this->assertEquals('Not there', $article->title);
  3229. $article = $articles->findOrCreate(['title' => 'First Article'], function ($article) {
  3230. $this->fail('Should not be called for existing entities.');
  3231. });
  3232. $this->assertFalse($article->isNew());
  3233. $this->assertNotNull($article->id);
  3234. $this->assertEquals('First Article', $article->title);
  3235. $article = $articles->findOrCreate(
  3236. ['author_id' => 2, 'title' => 'First Article'],
  3237. function ($article) {
  3238. $article->set(['published' => 'N', 'body' => 'New body']);
  3239. }
  3240. );
  3241. $this->assertFalse($article->isNew());
  3242. $this->assertNotNull($article->id);
  3243. $this->assertEquals('First Article', $article->title);
  3244. $this->assertEquals('New body', $article->body);
  3245. $this->assertEquals('N', $article->published);
  3246. $this->assertEquals(2, $article->author_id);
  3247. }
  3248. }