TableTest.php 128 KB

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