MarshallerTest.php 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM;
  16. use Cake\Database\Expression\IdentifierExpression;
  17. use Cake\I18n\Time;
  18. use Cake\ORM\Entity;
  19. use Cake\ORM\Marshaller;
  20. use Cake\ORM\Table;
  21. use Cake\ORM\TableRegistry;
  22. use Cake\TestSuite\TestCase;
  23. use Cake\Validation\Validator;
  24. /**
  25. * Test entity for mass assignment.
  26. */
  27. class OpenEntity extends Entity
  28. {
  29. protected $_accessible = [
  30. '*' => true,
  31. ];
  32. }
  33. /**
  34. * Test entity for mass assignment.
  35. */
  36. class Tag extends Entity
  37. {
  38. protected $_accessible = [
  39. 'tag' => true,
  40. ];
  41. }
  42. /**
  43. * Test entity for mass assignment.
  44. */
  45. class ProtectedArticle extends Entity
  46. {
  47. protected $_accessible = [
  48. 'title' => true,
  49. 'body' => true
  50. ];
  51. }
  52. /**
  53. * Test stub for greedy find operations.
  54. */
  55. class GreedyCommentsTable extends Table
  56. {
  57. /**
  58. * initialize hook
  59. *
  60. * @param $config Config data.
  61. * @return void
  62. */
  63. public function initialize(array $config)
  64. {
  65. $this->table('comments');
  66. $this->alias('Comments');
  67. }
  68. /**
  69. * Overload find to cause issues.
  70. *
  71. * @param string $type Find type
  72. * @param array $options find options
  73. * @return object
  74. */
  75. public function find($type = 'all', $options = [])
  76. {
  77. if (empty($options['conditions'])) {
  78. $options['conditions'] = [];
  79. }
  80. $options['conditions'] = array_merge($options['conditions'], ['Comments.published' => 'Y']);
  81. return parent::find($type, $options);
  82. }
  83. }
  84. /**
  85. * Marshaller test case
  86. */
  87. class MarshallerTest extends TestCase
  88. {
  89. public $fixtures = [
  90. 'core.articles',
  91. 'core.articles_tags',
  92. 'core.comments',
  93. 'core.special_tags',
  94. 'core.tags',
  95. 'core.users'
  96. ];
  97. /**
  98. * setup
  99. *
  100. * @return void
  101. */
  102. public function setUp()
  103. {
  104. parent::setUp();
  105. $articles = TableRegistry::get('Articles');
  106. $articles->belongsTo('Users', [
  107. 'foreignKey' => 'author_id'
  108. ]);
  109. $articles->hasMany('Comments');
  110. $articles->belongsToMany('Tags');
  111. $comments = TableRegistry::get('Comments');
  112. $users = TableRegistry::get('Users');
  113. $tags = TableRegistry::get('Tags');
  114. $articleTags = TableRegistry::get('ArticlesTags');
  115. $comments->belongsTo('Articles');
  116. $comments->belongsTo('Users');
  117. $articles->entityClass(__NAMESPACE__ . '\OpenEntity');
  118. $comments->entityClass(__NAMESPACE__ . '\OpenEntity');
  119. $users->entityClass(__NAMESPACE__ . '\OpenEntity');
  120. $tags->entityClass(__NAMESPACE__ . '\OpenEntity');
  121. $articleTags->entityClass(__NAMESPACE__ . '\OpenEntity');
  122. $this->articles = $articles;
  123. $this->comments = $comments;
  124. $this->users = $users;
  125. $this->tags = $tags;
  126. }
  127. /**
  128. * Teardown
  129. *
  130. * @return void
  131. */
  132. public function tearDown()
  133. {
  134. parent::tearDown();
  135. TableRegistry::clear();
  136. unset($this->articles, $this->comments, $this->users);
  137. }
  138. /**
  139. * Test one() in a simple use.
  140. *
  141. * @return void
  142. */
  143. public function testOneSimple()
  144. {
  145. $data = [
  146. 'title' => 'My title',
  147. 'body' => 'My content',
  148. 'author_id' => 1,
  149. 'not_in_schema' => true
  150. ];
  151. $marshall = new Marshaller($this->articles);
  152. $result = $marshall->one($data, []);
  153. $this->assertInstanceOf('Cake\ORM\Entity', $result);
  154. $this->assertEquals($data, $result->toArray());
  155. $this->assertTrue($result->dirty(), 'Should be a dirty entity.');
  156. $this->assertTrue($result->isNew(), 'Should be new');
  157. $this->assertEquals('Articles', $result->source());
  158. }
  159. /**
  160. * Test that marshalling an entity with '' for pk values results
  161. * in no pk value being set.
  162. *
  163. * @return void
  164. */
  165. public function testOneEmptyStringPrimaryKey()
  166. {
  167. $data = [
  168. 'id' => '',
  169. 'username' => 'superuser',
  170. 'password' => 'root',
  171. 'created' => new Time('2013-10-10 00:00'),
  172. 'updated' => new Time('2013-10-10 00:00')
  173. ];
  174. $marshall = new Marshaller($this->articles);
  175. $result = $marshall->one($data, []);
  176. $this->assertFalse($result->dirty('id'));
  177. $this->assertNull($result->id);
  178. }
  179. /**
  180. * Test marshalling datetime/date field.
  181. *
  182. * @return void
  183. */
  184. public function testOneWithDatetimeField()
  185. {
  186. $data = [
  187. 'comment' => 'My Comment text',
  188. 'created' => [
  189. 'year' => '2014',
  190. 'month' => '2',
  191. 'day' => 14
  192. ]
  193. ];
  194. $marshall = new Marshaller($this->comments);
  195. $result = $marshall->one($data, []);
  196. $this->assertEquals(new Time('2014-02-14 00:00:00'), $result->created);
  197. $data['created'] = [
  198. 'year' => '2014',
  199. 'month' => '2',
  200. 'day' => 14,
  201. 'hour' => 9,
  202. 'minute' => 25,
  203. 'meridian' => 'pm'
  204. ];
  205. $result = $marshall->one($data, []);
  206. $this->assertEquals(new Time('2014-02-14 21:25:00'), $result->created);
  207. $data['created'] = [
  208. 'year' => '2014',
  209. 'month' => '2',
  210. 'day' => 14,
  211. 'hour' => 9,
  212. 'minute' => 25,
  213. ];
  214. $result = $marshall->one($data, []);
  215. $this->assertEquals(new Time('2014-02-14 09:25:00'), $result->created);
  216. $data['created'] = '2014-02-14 09:25:00';
  217. $result = $marshall->one($data, []);
  218. $this->assertEquals(new Time('2014-02-14 09:25:00'), $result->created);
  219. $data['created'] = 1392387900;
  220. $result = $marshall->one($data, []);
  221. $this->assertEquals($data['created'], $result->created->getTimestamp());
  222. }
  223. /**
  224. * Ensure that marshalling casts reasonably.
  225. *
  226. * @return void
  227. */
  228. public function testOneOnlyCastMatchingData()
  229. {
  230. $data = [
  231. 'title' => 'My title',
  232. 'body' => 'My content',
  233. 'author_id' => 'derp',
  234. 'created' => 'fale'
  235. ];
  236. $this->articles->entityClass(__NAMESPACE__ . '\OpenEntity');
  237. $marshall = new Marshaller($this->articles);
  238. $result = $marshall->one($data, []);
  239. $this->assertSame($data['title'], $result->title);
  240. $this->assertNull($result->author_id, 'No cast on bad data.');
  241. $this->assertSame($data['created'], $result->created, 'No cast on bad data.');
  242. }
  243. /**
  244. * Test one() follows mass-assignment rules.
  245. *
  246. * @return void
  247. */
  248. public function testOneAccessibleProperties()
  249. {
  250. $data = [
  251. 'title' => 'My title',
  252. 'body' => 'My content',
  253. 'author_id' => 1,
  254. 'not_in_schema' => true
  255. ];
  256. $this->articles->entityClass(__NAMESPACE__ . '\ProtectedArticle');
  257. $marshall = new Marshaller($this->articles);
  258. $result = $marshall->one($data, []);
  259. $this->assertInstanceOf(__NAMESPACE__ . '\ProtectedArticle', $result);
  260. $this->assertNull($result->author_id);
  261. $this->assertNull($result->not_in_schema);
  262. }
  263. /**
  264. * Test one() supports accessibleFields option
  265. *
  266. * @return void
  267. */
  268. public function testOneAccessibleFieldsOption()
  269. {
  270. $data = [
  271. 'title' => 'My title',
  272. 'body' => 'My content',
  273. 'author_id' => 1,
  274. 'not_in_schema' => true
  275. ];
  276. $this->articles->entityClass(__NAMESPACE__ . '\ProtectedArticle');
  277. $marshall = new Marshaller($this->articles);
  278. $result = $marshall->one($data, ['accessibleFields' => ['body' => false]]);
  279. $this->assertNull($result->body);
  280. $result = $marshall->one($data, ['accessibleFields' => ['author_id' => true]]);
  281. $this->assertEquals($data['author_id'], $result->author_id);
  282. $this->assertNull($result->not_in_schema);
  283. $result = $marshall->one($data, ['accessibleFields' => ['*' => true]]);
  284. $this->assertEquals($data['author_id'], $result->author_id);
  285. $this->assertTrue($result->not_in_schema);
  286. }
  287. /**
  288. * Test one() supports accessibleFields option for associations
  289. *
  290. * @return void
  291. */
  292. public function testOneAccessibleFieldsOptionForAssociations()
  293. {
  294. $data = [
  295. 'title' => 'My title',
  296. 'body' => 'My content',
  297. 'user' => [
  298. 'id' => 1,
  299. 'username' => 'mark',
  300. ]
  301. ];
  302. $this->articles->entityClass(__NAMESPACE__ . '\ProtectedArticle');
  303. $this->users->entityClass(__NAMESPACE__ . '\ProtectedArticle');
  304. $marshall = new Marshaller($this->articles);
  305. $result = $marshall->one($data, [
  306. 'associated' => [
  307. 'Users' => ['accessibleFields' => ['id' => true]]
  308. ],
  309. 'accessibleFields' => ['body' => false, 'user' => true]
  310. ]);
  311. $this->assertNull($result->body);
  312. $this->assertNull($result->user->username);
  313. $this->assertEquals(1, $result->user->id);
  314. }
  315. /**
  316. * test one() with a wrapping model name.
  317. *
  318. * @return void
  319. */
  320. public function testOneWithAdditionalName()
  321. {
  322. $data = [
  323. 'Articles' => [
  324. 'title' => 'My title',
  325. 'body' => 'My content',
  326. 'author_id' => 1,
  327. 'not_in_schema' => true,
  328. 'user' => [
  329. 'username' => 'mark',
  330. ]
  331. ]
  332. ];
  333. $marshall = new Marshaller($this->articles);
  334. $result = $marshall->one($data, ['associated' => ['Users']]);
  335. $this->assertInstanceOf('Cake\ORM\Entity', $result);
  336. $this->assertTrue($result->dirty(), 'Should be a dirty entity.');
  337. $this->assertTrue($result->isNew(), 'Should be new');
  338. $this->assertEquals($data['Articles']['title'], $result->title);
  339. $this->assertEquals($data['Articles']['user']['username'], $result->user->username);
  340. }
  341. /**
  342. * test one() with association data.
  343. *
  344. * @return void
  345. */
  346. public function testOneAssociationsSingle()
  347. {
  348. $data = [
  349. 'title' => 'My title',
  350. 'body' => 'My content',
  351. 'author_id' => 1,
  352. 'comments' => [
  353. ['comment' => 'First post', 'user_id' => 2],
  354. ['comment' => 'Second post', 'user_id' => 2],
  355. ],
  356. 'user' => [
  357. 'username' => 'mark',
  358. 'password' => 'secret'
  359. ]
  360. ];
  361. $marshall = new Marshaller($this->articles);
  362. $result = $marshall->one($data, ['associated' => ['Users']]);
  363. $this->assertEquals($data['title'], $result->title);
  364. $this->assertEquals($data['body'], $result->body);
  365. $this->assertEquals($data['author_id'], $result->author_id);
  366. $this->assertInternalType('array', $result->comments);
  367. $this->assertEquals($data['comments'], $result->comments);
  368. $this->assertInstanceOf('Cake\ORM\Entity', $result->user);
  369. $this->assertEquals($data['user']['username'], $result->user->username);
  370. $this->assertEquals($data['user']['password'], $result->user->password);
  371. }
  372. /**
  373. * test one() with association data.
  374. *
  375. * @return void
  376. */
  377. public function testOneAssociationsMany()
  378. {
  379. $data = [
  380. 'title' => 'My title',
  381. 'body' => 'My content',
  382. 'author_id' => 1,
  383. 'comments' => [
  384. ['comment' => 'First post', 'user_id' => 2],
  385. ['comment' => 'Second post', 'user_id' => 2],
  386. ],
  387. 'user' => [
  388. 'username' => 'mark',
  389. 'password' => 'secret'
  390. ]
  391. ];
  392. $marshall = new Marshaller($this->articles);
  393. $result = $marshall->one($data, ['associated' => ['Comments']]);
  394. $this->assertEquals($data['title'], $result->title);
  395. $this->assertEquals($data['body'], $result->body);
  396. $this->assertEquals($data['author_id'], $result->author_id);
  397. $this->assertInternalType('array', $result->comments);
  398. $this->assertCount(2, $result->comments);
  399. $this->assertInstanceOf('Cake\ORM\Entity', $result->comments[0]);
  400. $this->assertInstanceOf('Cake\ORM\Entity', $result->comments[1]);
  401. $this->assertEquals($data['comments'][0]['comment'], $result->comments[0]->comment);
  402. $this->assertInternalType('array', $result->user);
  403. $this->assertEquals($data['user'], $result->user);
  404. }
  405. /**
  406. * Test building the _joinData entity for belongstomany associations.
  407. *
  408. * @return void
  409. */
  410. public function testOneBelongsToManyJoinData()
  411. {
  412. $data = [
  413. 'title' => 'My title',
  414. 'body' => 'My content',
  415. 'author_id' => 1,
  416. 'tags' => [
  417. ['tag' => 'news', '_joinData' => ['active' => 1]],
  418. ['tag' => 'cakephp', '_joinData' => ['active' => 0]],
  419. ],
  420. ];
  421. $marshall = new Marshaller($this->articles);
  422. $result = $marshall->one($data, [
  423. 'associated' => ['Tags']
  424. ]);
  425. $this->assertEquals($data['title'], $result->title);
  426. $this->assertEquals($data['body'], $result->body);
  427. $this->assertInternalType('array', $result->tags);
  428. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  429. $this->assertEquals($data['tags'][0]['tag'], $result->tags[0]->tag);
  430. $this->assertInstanceOf(
  431. 'Cake\ORM\Entity',
  432. $result->tags[0]->_joinData,
  433. '_joinData should be an entity.'
  434. );
  435. $this->assertEquals(
  436. $data['tags'][0]['_joinData']['active'],
  437. $result->tags[0]->_joinData->active,
  438. '_joinData should be an entity.'
  439. );
  440. }
  441. /**
  442. * Test that the onlyIds option restricts to only accepting ids for belongs to many associations.
  443. *
  444. * @return void
  445. */
  446. public function testOneBelongsToManyOnlyIdsRejectArray()
  447. {
  448. $data = [
  449. 'title' => 'My title',
  450. 'body' => 'My content',
  451. 'author_id' => 1,
  452. 'tags' => [
  453. ['tag' => 'news'],
  454. ['tag' => 'cakephp'],
  455. ],
  456. ];
  457. $marshall = new Marshaller($this->articles);
  458. $result = $marshall->one($data, [
  459. 'associated' => ['Tags' => ['onlyIds' => true]]
  460. ]);
  461. $this->assertEmpty($result->tags, 'Only ids should be marshalled.');
  462. }
  463. /**
  464. * Test that the onlyIds option restricts to only accepting ids for belongs to many associations.
  465. *
  466. * @return void
  467. */
  468. public function testOneBelongsToManyOnlyIdsWithIds()
  469. {
  470. $data = [
  471. 'title' => 'My title',
  472. 'body' => 'My content',
  473. 'author_id' => 1,
  474. 'tags' => [
  475. '_ids' => [1, 2],
  476. ['tag' => 'news'],
  477. ],
  478. ];
  479. $marshall = new Marshaller($this->articles);
  480. $result = $marshall->one($data, [
  481. 'associated' => ['Tags' => ['onlyIds' => true]]
  482. ]);
  483. $this->assertCount(2, $result->tags, 'Ids should be marshalled.');
  484. }
  485. /**
  486. * Test marshalling nested associations on the _joinData structure.
  487. *
  488. * @return void
  489. */
  490. public function testOneBelongsToManyJoinDataAssociated()
  491. {
  492. $data = [
  493. 'title' => 'My title',
  494. 'body' => 'My content',
  495. 'author_id' => 1,
  496. 'tags' => [
  497. [
  498. 'tag' => 'news',
  499. '_joinData' => [
  500. 'active' => 1,
  501. 'user' => ['username' => 'Bill'],
  502. ]
  503. ],
  504. [
  505. 'tag' => 'cakephp',
  506. '_joinData' => [
  507. 'active' => 0,
  508. 'user' => ['username' => 'Mark'],
  509. ]
  510. ],
  511. ],
  512. ];
  513. $articlesTags = TableRegistry::get('ArticlesTags');
  514. $articlesTags->belongsTo('Users');
  515. $marshall = new Marshaller($this->articles);
  516. $result = $marshall->one($data, ['associated' => ['Tags._joinData.Users']]);
  517. $this->assertInstanceOf(
  518. 'Cake\ORM\Entity',
  519. $result->tags[0]->_joinData->user,
  520. 'joinData should contain a user entity.'
  521. );
  522. $this->assertEquals('Bill', $result->tags[0]->_joinData->user->username);
  523. $this->assertInstanceOf(
  524. 'Cake\ORM\Entity',
  525. $result->tags[1]->_joinData->user,
  526. 'joinData should contain a user entity.'
  527. );
  528. $this->assertEquals('Mark', $result->tags[1]->_joinData->user->username);
  529. }
  530. /**
  531. * Test one() with with id and _joinData.
  532. *
  533. * @return void
  534. */
  535. public function testOneBelongsToManyJoinDataAssociatedWithIds()
  536. {
  537. $data = [
  538. 'title' => 'My title',
  539. 'body' => 'My content',
  540. 'author_id' => 1,
  541. 'tags' => [
  542. 3 => [
  543. 'id' => 1,
  544. '_joinData' => [
  545. 'active' => 1,
  546. 'user' => ['username' => 'MyLux'],
  547. ]
  548. ],
  549. 5 => [
  550. 'id' => 2,
  551. '_joinData' => [
  552. 'active' => 0,
  553. 'user' => ['username' => 'IronFall'],
  554. ]
  555. ],
  556. ],
  557. ];
  558. $articlesTags = TableRegistry::get('ArticlesTags');
  559. $tags = TableRegistry::get('Tags');
  560. $t1 = $tags->find('all')->where(['id' => 1])->first();
  561. $t2 = $tags->find('all')->where(['id' => 2])->first();
  562. $articlesTags->belongsTo('Users');
  563. $marshall = new Marshaller($this->articles);
  564. $result = $marshall->one($data, ['associated' => ['Tags._joinData.Users']]);
  565. $this->assertInstanceOf(
  566. 'Cake\ORM\Entity',
  567. $result->tags[0]
  568. );
  569. $this->assertInstanceOf(
  570. 'Cake\ORM\Entity',
  571. $result->tags[1]
  572. );
  573. $this->assertInstanceOf(
  574. 'Cake\ORM\Entity',
  575. $result->tags[0]->_joinData->user
  576. );
  577. $this->assertInstanceOf(
  578. 'Cake\ORM\Entity',
  579. $result->tags[1]->_joinData->user
  580. );
  581. $this->assertFalse($result->tags[0]->isNew(), 'Should not be new, as id is in db.');
  582. $this->assertFalse($result->tags[1]->isNew(), 'Should not be new, as id is in db.');
  583. $this->assertEquals($t1->tag, $result->tags[0]->tag);
  584. $this->assertEquals($t2->tag, $result->tags[1]->tag);
  585. $this->assertEquals($data['tags'][3]['_joinData']['user']['username'], $result->tags[0]->_joinData->user->username);
  586. $this->assertEquals($data['tags'][5]['_joinData']['user']['username'], $result->tags[1]->_joinData->user->username);
  587. }
  588. /**
  589. * Test belongsToMany association with mixed data and _joinData
  590. *
  591. * @return void
  592. */
  593. public function testOneBelongsToManyWithMixedJoinData()
  594. {
  595. $data = [
  596. 'title' => 'My title',
  597. 'body' => 'My content',
  598. 'author_id' => 1,
  599. 'tags' => [
  600. [
  601. 'id' => 1,
  602. '_joinData' => [
  603. 'active' => 0,
  604. ]
  605. ],
  606. [
  607. 'name' => 'tag5',
  608. '_joinData' => [
  609. 'active' => 1,
  610. ]
  611. ]
  612. ]
  613. ];
  614. $marshall = new Marshaller($this->articles);
  615. $result = $marshall->one($data, ['associated' => ['Tags._joinData']]);
  616. $this->assertEquals($data['tags'][0]['id'], $result->tags[0]->id);
  617. $this->assertEquals($data['tags'][1]['name'], $result->tags[1]->name);
  618. $this->assertEquals(0, $result->tags[0]->_joinData->active);
  619. $this->assertEquals(1, $result->tags[1]->_joinData->active);
  620. }
  621. public function testOneBelongsToManyWithNestedAssociations()
  622. {
  623. $this->tags->belongsToMany('Articles');
  624. $data = [
  625. 'name' => 'new tag',
  626. 'articles' => [
  627. // This nested article exists, and we want to update it.
  628. [
  629. 'id' => 1,
  630. 'title' => 'New tagged article',
  631. 'body' => 'New tagged article',
  632. 'user' => [
  633. 'id' => 1,
  634. 'username' => 'newuser'
  635. ],
  636. 'comments' => [
  637. ['comment' => 'New comment', 'user_id' => 1],
  638. ['comment' => 'Second comment', 'user_id' => 1],
  639. ]
  640. ]
  641. ]
  642. ];
  643. $marshaller = new Marshaller($this->tags);
  644. $tag = $marshaller->one($data, ['associated' => ['Articles.Users', 'Articles.Comments']]);
  645. $this->assertNotEmpty($tag->articles);
  646. $this->assertCount(1, $tag->articles);
  647. $this->assertInstanceOf('Cake\ORM\Entity', $tag->articles[0]);
  648. $this->assertSame('New tagged article', $tag->articles[0]->title);
  649. $this->assertFalse($tag->articles[0]->isNew());
  650. $this->assertNotEmpty($tag->articles[0]->user);
  651. $this->assertInstanceOf('Cake\ORM\Entity', $tag->articles[0]->user);
  652. $this->assertSame('newuser', $tag->articles[0]->user->username);
  653. $this->assertTrue($tag->articles[0]->user->isNew());
  654. $this->assertNotEmpty($tag->articles[0]->comments);
  655. $this->assertCount(2, $tag->articles[0]->comments);
  656. $this->assertInstanceOf('Cake\ORM\Entity', $tag->articles[0]->comments[0]);
  657. $this->assertTrue($tag->articles[0]->comments[0]->isNew());
  658. $this->assertTrue($tag->articles[0]->comments[1]->isNew());
  659. }
  660. /**
  661. * Test belongsToMany association with mixed data and _joinData
  662. *
  663. * @return void
  664. */
  665. public function testBelongsToManyAddingNewExisting()
  666. {
  667. $this->tags->entityClass(__NAMESPACE__ . '\Tag');
  668. $data = [
  669. 'title' => 'My title',
  670. 'body' => 'My content',
  671. 'author_id' => 1,
  672. 'tags' => [
  673. [
  674. 'id' => 1,
  675. '_joinData' => [
  676. 'active' => 0,
  677. ]
  678. ],
  679. ]
  680. ];
  681. $marshall = new Marshaller($this->articles);
  682. $result = $marshall->one($data, ['associated' => ['Tags._joinData']]);
  683. $data = [
  684. 'title' => 'New Title',
  685. 'tags' => [
  686. [
  687. 'id' => 1,
  688. '_joinData' => [
  689. 'active' => 0,
  690. ]
  691. ],
  692. [
  693. 'id' => 2,
  694. '_joinData' => [
  695. 'active' => 1,
  696. ]
  697. ]
  698. ]
  699. ];
  700. $result = $marshall->merge($result, $data, ['associated' => ['Tags._joinData']]);
  701. $this->assertEquals($data['title'], $result->title);
  702. $this->assertEquals($data['tags'][0]['id'], $result->tags[0]->id);
  703. $this->assertEquals($data['tags'][1]['id'], $result->tags[1]->id);
  704. $this->assertNotEmpty($result->tags[0]->_joinData);
  705. $this->assertNotEmpty($result->tags[1]->_joinData);
  706. $this->assertEquals(0, $result->tags[0]->_joinData->active);
  707. $this->assertEquals(1, $result->tags[1]->_joinData->active);
  708. }
  709. /**
  710. * Test belongsToMany association with mixed data and _joinData
  711. *
  712. * @return void
  713. */
  714. public function testBelongsToManyWithMixedJoinDataOutOfOrder()
  715. {
  716. $data = [
  717. 'title' => 'My title',
  718. 'body' => 'My content',
  719. 'author_id' => 1,
  720. 'tags' => [
  721. [
  722. 'name' => 'tag5',
  723. '_joinData' => [
  724. 'active' => 1,
  725. ]
  726. ],
  727. [
  728. 'id' => 1,
  729. '_joinData' => [
  730. 'active' => 0,
  731. ]
  732. ],
  733. [
  734. 'name' => 'tag3',
  735. '_joinData' => [
  736. 'active' => 1,
  737. ]
  738. ],
  739. ]
  740. ];
  741. $marshall = new Marshaller($this->articles);
  742. $result = $marshall->one($data, ['associated' => ['Tags._joinData']]);
  743. $this->assertEquals($data['tags'][0]['name'], $result->tags[0]->name);
  744. $this->assertEquals($data['tags'][1]['id'], $result->tags[1]->id);
  745. $this->assertEquals($data['tags'][2]['name'], $result->tags[2]->name);
  746. $this->assertEquals(1, $result->tags[0]->_joinData->active);
  747. $this->assertEquals(0, $result->tags[1]->_joinData->active);
  748. $this->assertEquals(1, $result->tags[2]->_joinData->active);
  749. }
  750. /**
  751. * Test belongsToMany association with scalars
  752. *
  753. * @return void
  754. */
  755. public function testBelongsToManyInvalidData()
  756. {
  757. $data = [
  758. 'title' => 'My title',
  759. 'body' => 'My content',
  760. 'author_id' => 1,
  761. 'tags' => [
  762. 'id' => 1
  763. ]
  764. ];
  765. $article = $this->articles->newEntity($data, [
  766. 'associated' => ['Tags']
  767. ]);
  768. $this->assertEmpty($article->tags, 'No entity should be created');
  769. $data['tags'] = 1;
  770. $article = $this->articles->newEntity($data, [
  771. 'associated' => ['Tags']
  772. ]);
  773. $this->assertEmpty($article->tags, 'No entity should be created');
  774. }
  775. /**
  776. * Test belongsToMany association with mixed data array
  777. *
  778. * @return void
  779. */
  780. public function testBelongsToManyWithMixedData()
  781. {
  782. $data = [
  783. 'title' => 'My title',
  784. 'body' => 'My content',
  785. 'author_id' => 1,
  786. 'tags' => [
  787. [
  788. 'name' => 'tag4'
  789. ],
  790. [
  791. 'name' => 'tag5'
  792. ],
  793. [
  794. 'id' => 1
  795. ]
  796. ]
  797. ];
  798. $tags = TableRegistry::get('Tags');
  799. $marshaller = new Marshaller($this->articles);
  800. $article = $marshaller->one($data, ['associated' => ['Tags']]);
  801. $this->assertEquals($data['tags'][0]['name'], $article->tags[0]->name);
  802. $this->assertEquals($data['tags'][1]['name'], $article->tags[1]->name);
  803. $this->assertEquals($article->tags[2], $tags->get(1));
  804. $this->assertEquals($article->tags[0]->isNew(), true);
  805. $this->assertEquals($article->tags[1]->isNew(), true);
  806. $this->assertEquals($article->tags[2]->isNew(), false);
  807. $tagCount = $tags->find()->count();
  808. $this->articles->save($article);
  809. $this->assertEquals($tagCount + 2, $tags->find()->count());
  810. }
  811. /**
  812. * Test HasMany association with _ids attribute
  813. *
  814. * @return void
  815. */
  816. public function testOneHasManyWithIds()
  817. {
  818. $data = [
  819. 'title' => 'article',
  820. 'body' => 'some content',
  821. 'comments' => [
  822. '_ids' => [1, 2]
  823. ]
  824. ];
  825. $marshaller = new Marshaller($this->articles);
  826. $article = $marshaller->one($data, ['associated' => ['Comments']]);
  827. $this->assertEquals($article->comments[0], $this->comments->get(1));
  828. $this->assertEquals($article->comments[1], $this->comments->get(2));
  829. }
  830. /**
  831. * Test that the onlyIds option restricts to only accepting ids for hasmany associations.
  832. *
  833. * @return void
  834. */
  835. public function testOneHasManyOnlyIdsRejectArray()
  836. {
  837. $data = [
  838. 'title' => 'article',
  839. 'body' => 'some content',
  840. 'comments' => [
  841. ['comment' => 'first comment'],
  842. ['comment' => 'second comment'],
  843. ]
  844. ];
  845. $marshaller = new Marshaller($this->articles);
  846. $article = $marshaller->one($data, [
  847. 'associated' => ['Comments' => ['onlyIds' => true]]
  848. ]);
  849. $this->assertEmpty($article->comments);
  850. }
  851. /**
  852. * Test that the onlyIds option restricts to only accepting ids for hasmany associations.
  853. *
  854. * @return void
  855. */
  856. public function testOneHasManyOnlyIdsWithIds()
  857. {
  858. $data = [
  859. 'title' => 'article',
  860. 'body' => 'some content',
  861. 'comments' => [
  862. '_ids' => [1, 2],
  863. ['comment' => 'first comment'],
  864. ]
  865. ];
  866. $marshaller = new Marshaller($this->articles);
  867. $article = $marshaller->one($data, [
  868. 'associated' => ['Comments' => ['onlyIds' => true]]
  869. ]);
  870. $this->assertCount(2, $article->comments);
  871. }
  872. /**
  873. * Test HasMany association with invalid data
  874. *
  875. * @return void
  876. */
  877. public function testOneHasManyInvalidData()
  878. {
  879. $data = [
  880. 'title' => 'new title',
  881. 'body' => 'some content',
  882. 'comments' => [
  883. 'id' => 1
  884. ]
  885. ];
  886. $marshaller = new Marshaller($this->articles);
  887. $article = $marshaller->one($data, ['associated' => ['Comments']]);
  888. $this->assertEmpty($article->comments);
  889. $data['comments'] = 1;
  890. $article = $marshaller->one($data, ['associated' => ['Comments']]);
  891. $this->assertEmpty($article->comments);
  892. }
  893. /**
  894. * Test one() with deeper associations.
  895. *
  896. * @return void
  897. */
  898. public function testOneDeepAssociations()
  899. {
  900. $data = [
  901. 'comment' => 'First post',
  902. 'user_id' => 2,
  903. 'article' => [
  904. 'title' => 'Article title',
  905. 'body' => 'Article body',
  906. 'user' => [
  907. 'username' => 'mark',
  908. 'password' => 'secret'
  909. ],
  910. ]
  911. ];
  912. $marshall = new Marshaller($this->comments);
  913. $result = $marshall->one($data, ['associated' => ['Articles.Users']]);
  914. $this->assertEquals(
  915. $data['article']['title'],
  916. $result->article->title
  917. );
  918. $this->assertEquals(
  919. $data['article']['user']['username'],
  920. $result->article->user->username
  921. );
  922. }
  923. /**
  924. * Test many() with a simple set of data.
  925. *
  926. * @return void
  927. */
  928. public function testManySimple()
  929. {
  930. $data = [
  931. ['comment' => 'First post', 'user_id' => 2],
  932. ['comment' => 'Second post', 'user_id' => 2],
  933. ];
  934. $marshall = new Marshaller($this->comments);
  935. $result = $marshall->many($data);
  936. $this->assertCount(2, $result);
  937. $this->assertInstanceOf('Cake\ORM\Entity', $result[0]);
  938. $this->assertInstanceOf('Cake\ORM\Entity', $result[1]);
  939. $this->assertEquals($data[0]['comment'], $result[0]->comment);
  940. $this->assertEquals($data[1]['comment'], $result[1]->comment);
  941. }
  942. /**
  943. * Test many() with some invalid data
  944. *
  945. * @return void
  946. */
  947. public function testManyInvalidData()
  948. {
  949. $data = [
  950. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2],
  951. ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 1],
  952. '_csrfToken' => 'abc123',
  953. ];
  954. $marshall = new Marshaller($this->comments);
  955. $result = $marshall->many($data);
  956. $this->assertCount(2, $result);
  957. }
  958. /**
  959. * test many() with nested associations.
  960. *
  961. * @return void
  962. */
  963. public function testManyAssociations()
  964. {
  965. $data = [
  966. [
  967. 'comment' => 'First post',
  968. 'user_id' => 2,
  969. 'user' => [
  970. 'username' => 'mark',
  971. ],
  972. ],
  973. [
  974. 'comment' => 'Second post',
  975. 'user_id' => 2,
  976. 'user' => [
  977. 'username' => 'jose',
  978. ],
  979. ],
  980. ];
  981. $marshall = new Marshaller($this->comments);
  982. $result = $marshall->many($data, ['associated' => ['Users']]);
  983. $this->assertCount(2, $result);
  984. $this->assertInstanceOf('Cake\ORM\Entity', $result[0]);
  985. $this->assertInstanceOf('Cake\ORM\Entity', $result[1]);
  986. $this->assertEquals(
  987. $data[0]['user']['username'],
  988. $result[0]->user->username
  989. );
  990. $this->assertEquals(
  991. $data[1]['user']['username'],
  992. $result[1]->user->username
  993. );
  994. }
  995. /**
  996. * Test generating a list of entities from a list of ids.
  997. *
  998. * @return void
  999. */
  1000. public function testOneGenerateBelongsToManyEntitiesFromIds()
  1001. {
  1002. $data = [
  1003. 'title' => 'Haz tags',
  1004. 'body' => 'Some content here',
  1005. 'tags' => ['_ids' => '']
  1006. ];
  1007. $marshall = new Marshaller($this->articles);
  1008. $result = $marshall->one($data, ['associated' => ['Tags']]);
  1009. $this->assertCount(0, $result->tags);
  1010. $data = [
  1011. 'title' => 'Haz tags',
  1012. 'body' => 'Some content here',
  1013. 'tags' => ['_ids' => false]
  1014. ];
  1015. $result = $marshall->one($data, ['associated' => ['Tags']]);
  1016. $this->assertCount(0, $result->tags);
  1017. $data = [
  1018. 'title' => 'Haz tags',
  1019. 'body' => 'Some content here',
  1020. 'tags' => ['_ids' => null]
  1021. ];
  1022. $result = $marshall->one($data, ['associated' => ['Tags']]);
  1023. $this->assertCount(0, $result->tags);
  1024. $data = [
  1025. 'title' => 'Haz tags',
  1026. 'body' => 'Some content here',
  1027. 'tags' => ['_ids' => []]
  1028. ];
  1029. $result = $marshall->one($data, ['associated' => ['Tags']]);
  1030. $this->assertCount(0, $result->tags);
  1031. $data = [
  1032. 'title' => 'Haz tags',
  1033. 'body' => 'Some content here',
  1034. 'tags' => ['_ids' => [1, 2, 3]]
  1035. ];
  1036. $marshall = new Marshaller($this->articles);
  1037. $result = $marshall->one($data, ['associated' => ['Tags']]);
  1038. $this->assertCount(3, $result->tags);
  1039. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1040. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  1041. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[2]);
  1042. }
  1043. /**
  1044. * Test merge() in a simple use.
  1045. *
  1046. * @return void
  1047. */
  1048. public function testMergeSimple()
  1049. {
  1050. $data = [
  1051. 'title' => 'My title',
  1052. 'author_id' => 1,
  1053. 'not_in_schema' => true
  1054. ];
  1055. $marshall = new Marshaller($this->articles);
  1056. $entity = new Entity([
  1057. 'title' => 'Foo',
  1058. 'body' => 'My Content'
  1059. ]);
  1060. $entity->accessible('*', true);
  1061. $entity->isNew(false);
  1062. $entity->clean();
  1063. $result = $marshall->merge($entity, $data, []);
  1064. $this->assertSame($entity, $result);
  1065. $this->assertEquals($data + ['body' => 'My Content'], $result->toArray());
  1066. $this->assertTrue($result->dirty(), 'Should be a dirty entity.');
  1067. $this->assertFalse($result->isNew(), 'Should not change the entity state');
  1068. }
  1069. /**
  1070. * Test merge() with accessibleFields options
  1071. *
  1072. * @return void
  1073. */
  1074. public function testMergeAccessibleFields()
  1075. {
  1076. $data = [
  1077. 'title' => 'My title',
  1078. 'body' => 'New content',
  1079. 'author_id' => 1,
  1080. 'not_in_schema' => true
  1081. ];
  1082. $marshall = new Marshaller($this->articles);
  1083. $entity = new Entity([
  1084. 'title' => 'Foo',
  1085. 'body' => 'My Content'
  1086. ]);
  1087. $entity->accessible('*', false);
  1088. $entity->isNew(false);
  1089. $entity->clean();
  1090. $result = $marshall->merge($entity, $data, ['accessibleFields' => ['body' => true]]);
  1091. $this->assertSame($entity, $result);
  1092. $this->assertEquals(['title' => 'Foo', 'body' => 'New content'], $result->toArray());
  1093. $this->assertTrue($entity->accessible('body'));
  1094. }
  1095. /**
  1096. * Provides empty values.
  1097. *
  1098. * @return array
  1099. */
  1100. public function emptyProvider()
  1101. {
  1102. return [
  1103. [0],
  1104. ['0'],
  1105. ];
  1106. }
  1107. /**
  1108. * Test merging empty values into an entity.
  1109. *
  1110. * @dataProvider emptyProvider
  1111. * @return void
  1112. */
  1113. public function testMergeFalseyValues($value)
  1114. {
  1115. $marshall = new Marshaller($this->articles);
  1116. $entity = new Entity();
  1117. $entity->accessible('*', true);
  1118. $entity = $marshall->merge($entity, ['author_id' => $value]);
  1119. $this->assertTrue($entity->dirty('author_id'), 'Field should be dirty');
  1120. $this->assertSame(0, $entity->get('author_id'), 'Value should be zero');
  1121. }
  1122. /**
  1123. * Tests that merge respects the entity accessible methods
  1124. *
  1125. * @return void
  1126. */
  1127. public function testMergeWhitelist()
  1128. {
  1129. $data = [
  1130. 'title' => 'My title',
  1131. 'author_id' => 1,
  1132. 'not_in_schema' => true
  1133. ];
  1134. $marshall = new Marshaller($this->articles);
  1135. $entity = new Entity([
  1136. 'title' => 'Foo',
  1137. 'body' => 'My Content'
  1138. ]);
  1139. $entity->accessible('*', false);
  1140. $entity->accessible('author_id', true);
  1141. $entity->isNew(false);
  1142. $result = $marshall->merge($entity, $data, []);
  1143. $expected = [
  1144. 'title' => 'Foo',
  1145. 'body' => 'My Content',
  1146. 'author_id' => 1
  1147. ];
  1148. $this->assertEquals($expected, $result->toArray());
  1149. }
  1150. /**
  1151. * Test merge when fieldList contains an association.
  1152. *
  1153. * @return void
  1154. */
  1155. public function testMergeWithSingleAssociationAndFieldLists()
  1156. {
  1157. $user = new Entity([
  1158. 'username' => 'user',
  1159. ]);
  1160. $article = new Entity([
  1161. 'title' => 'title for post',
  1162. 'body' => 'body',
  1163. 'user' => $user,
  1164. ]);
  1165. $user->accessible('*', true);
  1166. $article->accessible('*', true);
  1167. $data = [
  1168. 'title' => 'Chelsea',
  1169. 'user' => [
  1170. 'username' => 'dee'
  1171. ]
  1172. ];
  1173. $marshall = new Marshaller($this->articles);
  1174. $marshall->merge($article, $data, [
  1175. 'fieldList' => ['title', 'user'],
  1176. 'associated' => ['Users' => []]
  1177. ]);
  1178. $this->assertSame($user, $article->user);
  1179. }
  1180. /**
  1181. * Tests that fields with the same value are not marked as dirty
  1182. *
  1183. * @return void
  1184. */
  1185. public function testMergeDirty()
  1186. {
  1187. $marshall = new Marshaller($this->articles);
  1188. $entity = new Entity([
  1189. 'title' => 'Foo',
  1190. 'author_id' => 1
  1191. ]);
  1192. $data = [
  1193. 'title' => 'Foo',
  1194. 'author_id' => 1,
  1195. 'crazy' => true
  1196. ];
  1197. $entity->accessible('*', true);
  1198. $entity->clean();
  1199. $result = $marshall->merge($entity, $data, []);
  1200. $expected = [
  1201. 'title' => 'Foo',
  1202. 'author_id' => 1,
  1203. 'crazy' => true
  1204. ];
  1205. $this->assertEquals($expected, $result->toArray());
  1206. $this->assertFalse($entity->dirty('title'));
  1207. $this->assertFalse($entity->dirty('author_id'));
  1208. $this->assertTrue($entity->dirty('crazy'));
  1209. }
  1210. /**
  1211. * Tests merging data into an associated entity
  1212. *
  1213. * @return void
  1214. */
  1215. public function testMergeWithSingleAssociation()
  1216. {
  1217. $user = new Entity([
  1218. 'username' => 'mark',
  1219. 'password' => 'secret'
  1220. ]);
  1221. $entity = new Entity([
  1222. 'title' => 'My Title',
  1223. 'user' => $user
  1224. ]);
  1225. $user->accessible('*', true);
  1226. $entity->accessible('*', true);
  1227. $data = [
  1228. 'body' => 'My Content',
  1229. 'user' => [
  1230. 'password' => 'not a secret'
  1231. ]
  1232. ];
  1233. $marshall = new Marshaller($this->articles);
  1234. $marshall->merge($entity, $data, ['associated' => ['Users']]);
  1235. $this->assertEquals('My Content', $entity->body);
  1236. $this->assertSame($user, $entity->user);
  1237. $this->assertEquals('mark', $entity->user->username);
  1238. $this->assertEquals('not a secret', $entity->user->password);
  1239. $this->assertTrue($entity->dirty('user'));
  1240. }
  1241. /**
  1242. * Tests that new associated entities can be created when merging data into
  1243. * a parent entity
  1244. *
  1245. * @return void
  1246. */
  1247. public function testMergeCreateAssociation()
  1248. {
  1249. $entity = new Entity([
  1250. 'title' => 'My Title'
  1251. ]);
  1252. $entity->accessible('*', true);
  1253. $data = [
  1254. 'body' => 'My Content',
  1255. 'user' => [
  1256. 'username' => 'mark',
  1257. 'password' => 'not a secret'
  1258. ]
  1259. ];
  1260. $marshall = new Marshaller($this->articles);
  1261. $marshall->merge($entity, $data, ['associated' => ['Users']]);
  1262. $this->assertEquals('My Content', $entity->body);
  1263. $this->assertInstanceOf('Cake\ORM\Entity', $entity->user);
  1264. $this->assertEquals('mark', $entity->user->username);
  1265. $this->assertEquals('not a secret', $entity->user->password);
  1266. $this->assertTrue($entity->dirty('user'));
  1267. $this->assertTrue($entity->user->isNew());
  1268. }
  1269. /**
  1270. * Tests merging one to many associations
  1271. *
  1272. * @return void
  1273. */
  1274. public function testMergeMultipleAssociations()
  1275. {
  1276. $user = new Entity(['username' => 'mark', 'password' => 'secret']);
  1277. $comment1 = new Entity(['id' => 1, 'comment' => 'A comment']);
  1278. $comment2 = new Entity(['id' => 2, 'comment' => 'Another comment']);
  1279. $entity = new Entity([
  1280. 'title' => 'My Title',
  1281. 'user' => $user,
  1282. 'comments' => [$comment1, $comment2]
  1283. ]);
  1284. $user->accessible('*', true);
  1285. $comment1->accessible('*', true);
  1286. $comment2->accessible('*', true);
  1287. $entity->accessible('*', true);
  1288. $data = [
  1289. 'title' => 'Another title',
  1290. 'user' => ['password' => 'not so secret'],
  1291. 'comments' => [
  1292. ['comment' => 'Extra comment 1'],
  1293. ['id' => 2, 'comment' => 'Altered comment 2'],
  1294. ['id' => 1, 'comment' => 'Altered comment 1'],
  1295. ['id' => 3, 'comment' => 'Extra comment 3'],
  1296. ['id' => 4, 'comment' => 'Extra comment 4'],
  1297. ['comment' => 'Extra comment 2']
  1298. ]
  1299. ];
  1300. $marshall = new Marshaller($this->articles);
  1301. $result = $marshall->merge($entity, $data, ['associated' => ['Users', 'Comments']]);
  1302. $this->assertSame($entity, $result);
  1303. $this->assertSame($user, $result->user);
  1304. $this->assertEquals('not so secret', $entity->user->password);
  1305. $this->assertSame($comment1, $entity->comments[0]);
  1306. $this->assertSame($comment2, $entity->comments[1]);
  1307. $this->assertEquals('Altered comment 1', $entity->comments[0]->comment);
  1308. $this->assertEquals('Altered comment 2', $entity->comments[1]->comment);
  1309. $thirdComment = $this->articles->Comments
  1310. ->find()
  1311. ->where(['id' => 3])
  1312. ->hydrate(false)
  1313. ->first();
  1314. $this->assertEquals(
  1315. ['comment' => 'Extra comment 3'] + $thirdComment,
  1316. $entity->comments[2]->toArray()
  1317. );
  1318. $forthComment = $this->articles->Comments
  1319. ->find()
  1320. ->where(['id' => 4])
  1321. ->hydrate(false)
  1322. ->first();
  1323. $this->assertEquals(
  1324. ['comment' => 'Extra comment 4'] + $forthComment,
  1325. $entity->comments[3]->toArray()
  1326. );
  1327. $this->assertEquals(
  1328. ['comment' => 'Extra comment 1'],
  1329. $entity->comments[4]->toArray()
  1330. );
  1331. $this->assertEquals(
  1332. ['comment' => 'Extra comment 2'],
  1333. $entity->comments[5]->toArray()
  1334. );
  1335. }
  1336. /**
  1337. * Tests that merging data to an entity containing belongsToMany and _ids
  1338. * will just overwrite the data
  1339. *
  1340. * @return void
  1341. */
  1342. public function testMergeBelongsToManyEntitiesFromIds()
  1343. {
  1344. $entity = new Entity([
  1345. 'title' => 'Haz tags',
  1346. 'body' => 'Some content here',
  1347. 'tags' => [
  1348. new Entity(['id' => 1, 'name' => 'Cake']),
  1349. new Entity(['id' => 2, 'name' => 'PHP'])
  1350. ]
  1351. ]);
  1352. $data = [
  1353. 'title' => 'Haz moar tags',
  1354. 'tags' => ['_ids' => [1, 2, 3]]
  1355. ];
  1356. $entity->accessible('*', true);
  1357. $marshall = new Marshaller($this->articles);
  1358. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1359. $this->assertCount(3, $result->tags);
  1360. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1361. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  1362. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[2]);
  1363. }
  1364. /**
  1365. * Tests that merging data to an entity containing belongsToMany and _ids
  1366. * will not generate conflicting queries when associations are automatically selected
  1367. *
  1368. * @return void
  1369. */
  1370. public function testMergeFromIdsWithAutoAssociation()
  1371. {
  1372. $entity = new Entity([
  1373. 'title' => 'Haz tags',
  1374. 'body' => 'Some content here',
  1375. 'tags' => [
  1376. new Entity(['id' => 1, 'name' => 'Cake']),
  1377. new Entity(['id' => 2, 'name' => 'PHP'])
  1378. ]
  1379. ]);
  1380. $data = [
  1381. 'title' => 'Haz moar tags',
  1382. 'tags' => ['_ids' => [1, 2, 3]]
  1383. ];
  1384. $entity->accessible('*', true);
  1385. // Adding a forced join to have another table with the same column names
  1386. $this->articles->Tags->eventManager()->attach(function ($e, $query) {
  1387. $left = new IdentifierExpression('Tags.id');
  1388. $right = new IdentifierExpression('a.id');
  1389. $query->leftJoin(['a' => 'tags'], $query->newExpr()->eq($left, $right));
  1390. }, 'Model.beforeFind');
  1391. $marshall = new Marshaller($this->articles);
  1392. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1393. $this->assertCount(3, $result->tags);
  1394. }
  1395. /**
  1396. * Tests that merging data to an entity containing belongsToMany and _ids
  1397. * with additional association conditions works.
  1398. *
  1399. * @return void
  1400. */
  1401. public function testMergeBelongsToManyFromIdsWithConditions()
  1402. {
  1403. $this->articles->belongsToMany('Tags', [
  1404. 'conditions' => ['ArticleTags.article_id' => 1]
  1405. ]);
  1406. $entity = new Entity([
  1407. 'title' => 'No tags',
  1408. 'body' => 'Some content here',
  1409. 'tags' => []
  1410. ]);
  1411. $data = [
  1412. 'title' => 'Haz moar tags',
  1413. 'tags' => ['_ids' => [1, 2, 3]]
  1414. ];
  1415. $entity->accessible('*', true);
  1416. $marshall = new Marshaller($this->articles);
  1417. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1418. $this->assertCount(3, $result->tags);
  1419. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1420. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  1421. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[2]);
  1422. }
  1423. /**
  1424. * Tests that merging data to an entity containing belongsToMany as an array
  1425. * with additional association conditions works.
  1426. *
  1427. * @return void
  1428. */
  1429. public function testMergeBelongsToManyFromArrayWithConditions()
  1430. {
  1431. $this->articles->belongsToMany('Tags', [
  1432. 'conditions' => ['ArticleTags.article_id' => 1]
  1433. ]);
  1434. $this->articles->Tags->eventManager()
  1435. ->on('Model.beforeFind', function ($event, $query) use (&$called) {
  1436. $called = true;
  1437. return $query->where(['Tags.id >=' => 1]);
  1438. });
  1439. $entity = new Entity([
  1440. 'title' => 'No tags',
  1441. 'body' => 'Some content here',
  1442. 'tags' => []
  1443. ]);
  1444. $data = [
  1445. 'title' => 'Haz moar tags',
  1446. 'tags' => [
  1447. ['id' => 1],
  1448. ['id' => 2]
  1449. ]
  1450. ];
  1451. $entity->accessible('*', true);
  1452. $marshall = new Marshaller($this->articles);
  1453. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1454. $this->assertCount(2, $result->tags);
  1455. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1456. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  1457. $this->assertTrue($called);
  1458. }
  1459. /**
  1460. * Tests that merging data to an entity containing belongsToMany and _ids
  1461. * will ignore empty values.
  1462. *
  1463. * @return void
  1464. */
  1465. public function testMergeBelongsToManyEntitiesFromIdsEmptyValue()
  1466. {
  1467. $entity = new Entity([
  1468. 'title' => 'Haz tags',
  1469. 'body' => 'Some content here',
  1470. 'tags' => [
  1471. new Entity(['id' => 1, 'name' => 'Cake']),
  1472. new Entity(['id' => 2, 'name' => 'PHP'])
  1473. ]
  1474. ]);
  1475. $data = [
  1476. 'title' => 'Haz moar tags',
  1477. 'tags' => ['_ids' => '']
  1478. ];
  1479. $entity->accessible('*', true);
  1480. $marshall = new Marshaller($this->articles);
  1481. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1482. $this->assertCount(0, $result->tags);
  1483. $data = [
  1484. 'title' => 'Haz moar tags',
  1485. 'tags' => ['_ids' => false]
  1486. ];
  1487. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1488. $this->assertCount(0, $result->tags);
  1489. $data = [
  1490. 'title' => 'Haz moar tags',
  1491. 'tags' => ['_ids' => null]
  1492. ];
  1493. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1494. $this->assertCount(0, $result->tags);
  1495. }
  1496. /**
  1497. * Test that the ids option restricts to only accepting ids for belongs to many associations.
  1498. *
  1499. * @return void
  1500. */
  1501. public function testMergeBelongsToManyOnlyIdsRejectArray()
  1502. {
  1503. $entity = new Entity([
  1504. 'title' => 'Haz tags',
  1505. 'body' => 'Some content here',
  1506. 'tags' => [
  1507. new Entity(['id' => 1, 'name' => 'Cake']),
  1508. new Entity(['id' => 2, 'name' => 'PHP'])
  1509. ]
  1510. ]);
  1511. $data = [
  1512. 'title' => 'Haz moar tags',
  1513. 'tags' => [
  1514. ['name' => 'new'],
  1515. ['name' => 'awesome']
  1516. ]
  1517. ];
  1518. $entity->accessible('*', true);
  1519. $marshall = new Marshaller($this->articles);
  1520. $result = $marshall->merge($entity, $data, [
  1521. 'associated' => ['Tags' => ['onlyIds' => true]]
  1522. ]);
  1523. $this->assertCount(0, $result->tags);
  1524. }
  1525. /**
  1526. * Test that the ids option restricts to only accepting ids for belongs to many associations.
  1527. *
  1528. * @return void
  1529. */
  1530. public function testMergeBelongsToManyOnlyIdsWithIds()
  1531. {
  1532. $entity = new Entity([
  1533. 'title' => 'Haz tags',
  1534. 'body' => 'Some content here',
  1535. 'tags' => [
  1536. new Entity(['id' => 1, 'name' => 'Cake']),
  1537. new Entity(['id' => 2, 'name' => 'PHP'])
  1538. ]
  1539. ]);
  1540. $data = [
  1541. 'title' => 'Haz moar tags',
  1542. 'tags' => [
  1543. '_ids' => [3]
  1544. ]
  1545. ];
  1546. $entity->accessible('*', true);
  1547. $marshall = new Marshaller($this->articles);
  1548. $result = $marshall->merge($entity, $data, [
  1549. 'associated' => ['Tags' => ['ids' => true]]
  1550. ]);
  1551. $this->assertCount(1, $result->tags);
  1552. $this->assertEquals('tag3', $result->tags[0]->name);
  1553. }
  1554. /**
  1555. * Test that invalid _joinData (scalar data) is not marshalled.
  1556. *
  1557. * @return void
  1558. */
  1559. public function testMergeBelongsToManyJoinDataScalar()
  1560. {
  1561. TableRegistry::clear();
  1562. $articles = TableRegistry::get('Articles');
  1563. $articles->belongsToMany('Tags', [
  1564. 'through' => 'SpecialTags'
  1565. ]);
  1566. $entity = $articles->get(1, ['contain' => 'Tags']);
  1567. $data = [
  1568. 'title' => 'Haz data',
  1569. 'tags' => [
  1570. ['id' => 3, 'tag' => 'Cake', '_joinData' => 'Invalid'],
  1571. ]
  1572. ];
  1573. $marshall = new Marshaller($articles);
  1574. $result = $marshall->merge($entity, $data, ['associated' => 'Tags._joinData']);
  1575. $articles->save($entity, ['associated' => ['Tags._joinData']]);
  1576. $this->assertFalse($entity->tags[0]->dirty('_joinData'));
  1577. $this->assertEmpty($entity->tags[0]->_joinData);
  1578. }
  1579. /**
  1580. * Test merging the _joinData entity for belongstomany associations when * is not
  1581. * accessible.
  1582. *
  1583. * @return void
  1584. */
  1585. public function testMergeBelongsToManyJoinDataNotAccessible()
  1586. {
  1587. TableRegistry::clear();
  1588. $articles = TableRegistry::get('Articles');
  1589. $articles->belongsToMany('Tags', [
  1590. 'through' => 'SpecialTags'
  1591. ]);
  1592. $entity = $articles->get(1, ['contain' => 'Tags']);
  1593. $data = [
  1594. 'title' => 'Haz data',
  1595. 'tags' => [
  1596. ['id' => 3, 'tag' => 'Cake', '_joinData' => ['highlighted' => '1', 'author_id' => '99']],
  1597. ]
  1598. ];
  1599. // Make only specific fields accessible, but not _joinData.
  1600. $entity->tags[0]->accessible('*', false);
  1601. $entity->tags[0]->accessible(['article_id', 'tag_id'], true);
  1602. $marshall = new Marshaller($articles);
  1603. $result = $marshall->merge($entity, $data, ['associated' => 'Tags._joinData']);
  1604. $this->assertTrue($entity->tags[0]->dirty('_joinData'));
  1605. $this->assertTrue($result->tags[0]->_joinData->dirty('author_id'), 'Field not modified');
  1606. $this->assertTrue($result->tags[0]->_joinData->dirty('highlighted'), 'Field not modified');
  1607. $this->assertSame(99, $result->tags[0]->_joinData->author_id);
  1608. $this->assertTrue($result->tags[0]->_joinData->highlighted);
  1609. }
  1610. /**
  1611. * Test that _joinData is marshalled consistently with both
  1612. * new and existing records
  1613. *
  1614. * @return void
  1615. */
  1616. public function testMergeBelongsToManyHandleJoinDataConsistently()
  1617. {
  1618. TableRegistry::clear();
  1619. $articles = TableRegistry::get('Articles');
  1620. $articles->belongsToMany('Tags', [
  1621. 'through' => 'SpecialTags'
  1622. ]);
  1623. $entity = $articles->get(1);
  1624. $data = [
  1625. 'title' => 'Haz data',
  1626. 'tags' => [
  1627. ['id' => 3, 'tag' => 'Cake', '_joinData' => ['highlighted' => true]],
  1628. ]
  1629. ];
  1630. $marshall = new Marshaller($articles);
  1631. $result = $marshall->merge($entity, $data, ['associated' => 'Tags']);
  1632. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]->_joinData);
  1633. $this->assertTrue($result->tags[0]->_joinData->highlighted);
  1634. // Also ensure merge() overwrites existing data.
  1635. $entity = $articles->get(1, ['contain' => 'Tags']);
  1636. $data = [
  1637. 'title' => 'Haz data',
  1638. 'tags' => [
  1639. ['id' => 3, 'tag' => 'Cake', '_joinData' => ['highlighted' => true]],
  1640. ]
  1641. ];
  1642. $marshall = new Marshaller($articles);
  1643. $result = $marshall->merge($entity, $data, ['associated' => 'Tags']);
  1644. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]->_joinData);
  1645. $this->assertTrue($result->tags[0]->_joinData->highlighted);
  1646. }
  1647. /**
  1648. * Test merging belongsToMany data doesn't create 'new' entities.
  1649. *
  1650. * @return void
  1651. */
  1652. public function testMergeBelongsToManyJoinDataAssociatedWithIds()
  1653. {
  1654. $data = [
  1655. 'title' => 'My title',
  1656. 'tags' => [
  1657. [
  1658. 'id' => 1,
  1659. '_joinData' => [
  1660. 'active' => 1,
  1661. 'user' => ['username' => 'MyLux'],
  1662. ]
  1663. ],
  1664. [
  1665. 'id' => 2,
  1666. '_joinData' => [
  1667. 'active' => 0,
  1668. 'user' => ['username' => 'IronFall'],
  1669. ]
  1670. ],
  1671. ],
  1672. ];
  1673. $articlesTags = TableRegistry::get('ArticlesTags');
  1674. $articlesTags->belongsTo('Users');
  1675. $marshall = new Marshaller($this->articles);
  1676. $article = $this->articles->get(1, ['associated' => 'Tags']);
  1677. $result = $marshall->merge($article, $data, ['associated' => ['Tags._joinData.Users']]);
  1678. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1679. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  1680. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]->_joinData->user);
  1681. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]->_joinData->user);
  1682. $this->assertFalse($result->tags[0]->isNew(), 'Should not be new, as id is in db.');
  1683. $this->assertFalse($result->tags[1]->isNew(), 'Should not be new, as id is in db.');
  1684. $this->assertEquals(1, $result->tags[0]->id);
  1685. $this->assertEquals(2, $result->tags[1]->id);
  1686. $this->assertEquals(1, $result->tags[0]->_joinData->active);
  1687. $this->assertEquals(0, $result->tags[1]->_joinData->active);
  1688. $this->assertEquals(
  1689. $data['tags'][0]['_joinData']['user']['username'],
  1690. $result->tags[0]->_joinData->user->username
  1691. );
  1692. $this->assertEquals(
  1693. $data['tags'][1]['_joinData']['user']['username'],
  1694. $result->tags[1]->_joinData->user->username
  1695. );
  1696. }
  1697. /**
  1698. * Test merging the _joinData entity for belongstomany associations.
  1699. *
  1700. * @return void
  1701. */
  1702. public function testMergeBelongsToManyJoinData()
  1703. {
  1704. $data = [
  1705. 'title' => 'My title',
  1706. 'body' => 'My content',
  1707. 'author_id' => 1,
  1708. 'tags' => [
  1709. [
  1710. 'id' => 1,
  1711. 'tag' => 'news',
  1712. '_joinData' => [
  1713. 'active' => 0
  1714. ]
  1715. ],
  1716. [
  1717. 'id' => 2,
  1718. 'tag' => 'cakephp',
  1719. '_joinData' => [
  1720. 'active' => 0
  1721. ]
  1722. ],
  1723. ],
  1724. ];
  1725. $options = ['associated' => ['Tags._joinData']];
  1726. $marshall = new Marshaller($this->articles);
  1727. $entity = $marshall->one($data, $options);
  1728. $entity->accessible('*', true);
  1729. $data = [
  1730. 'title' => 'Haz data',
  1731. 'tags' => [
  1732. ['id' => 1, 'tag' => 'Cake', '_joinData' => ['foo' => 'bar']],
  1733. ['tag' => 'new tag', '_joinData' => ['active' => 1, 'foo' => 'baz']]
  1734. ]
  1735. ];
  1736. $tag1 = $entity->tags[0];
  1737. $result = $marshall->merge($entity, $data, $options);
  1738. $this->assertEquals($data['title'], $result->title);
  1739. $this->assertEquals('My content', $result->body);
  1740. $this->assertSame($tag1, $entity->tags[0]);
  1741. $this->assertSame($tag1->_joinData, $entity->tags[0]->_joinData);
  1742. $this->assertSame(
  1743. ['active' => 0, 'foo' => 'bar'],
  1744. $entity->tags[0]->_joinData->toArray()
  1745. );
  1746. $this->assertSame(
  1747. ['active' => 1, 'foo' => 'baz'],
  1748. $entity->tags[1]->_joinData->toArray()
  1749. );
  1750. $this->assertEquals('new tag', $entity->tags[1]->tag);
  1751. $this->assertTrue($entity->tags[0]->dirty('_joinData'));
  1752. $this->assertTrue($entity->tags[1]->dirty('_joinData'));
  1753. }
  1754. /**
  1755. * Test merging associations inside _joinData
  1756. *
  1757. * @return void
  1758. */
  1759. public function testMergeJoinDataAssociations()
  1760. {
  1761. $data = [
  1762. 'title' => 'My title',
  1763. 'body' => 'My content',
  1764. 'author_id' => 1,
  1765. 'tags' => [
  1766. [
  1767. 'id' => 1,
  1768. 'tag' => 'news',
  1769. '_joinData' => [
  1770. 'active' => 0,
  1771. 'user' => ['username' => 'Bill']
  1772. ]
  1773. ],
  1774. [
  1775. 'id' => 2,
  1776. 'tag' => 'cakephp',
  1777. '_joinData' => [
  1778. 'active' => 0
  1779. ]
  1780. ],
  1781. ]
  1782. ];
  1783. $articlesTags = TableRegistry::get('ArticlesTags');
  1784. $articlesTags->belongsTo('Users');
  1785. $options = ['associated' => ['Tags._joinData.Users']];
  1786. $marshall = new Marshaller($this->articles);
  1787. $entity = $marshall->one($data, $options);
  1788. $entity->accessible('*', true);
  1789. $data = [
  1790. 'title' => 'Haz data',
  1791. 'tags' => [
  1792. [
  1793. 'id' => 1,
  1794. 'tag' => 'news',
  1795. '_joinData' => [
  1796. 'foo' => 'bar',
  1797. 'user' => ['password' => 'secret']
  1798. ]
  1799. ],
  1800. [
  1801. 'id' => 2,
  1802. '_joinData' => [
  1803. 'active' => 1,
  1804. 'foo' => 'baz',
  1805. 'user' => ['username' => 'ber']
  1806. ]
  1807. ]
  1808. ]
  1809. ];
  1810. $tag1 = $entity->tags[0];
  1811. $result = $marshall->merge($entity, $data, $options);
  1812. $this->assertEquals($data['title'], $result->title);
  1813. $this->assertEquals('My content', $result->body);
  1814. $this->assertSame($tag1, $entity->tags[0]);
  1815. $this->assertSame($tag1->_joinData, $entity->tags[0]->_joinData);
  1816. $this->assertEquals('Bill', $entity->tags[0]->_joinData->user->username);
  1817. $this->assertEquals('secret', $entity->tags[0]->_joinData->user->password);
  1818. $this->assertEquals('ber', $entity->tags[1]->_joinData->user->username);
  1819. }
  1820. /**
  1821. * Tests that merging belongsToMany association doesn't erase _joinData
  1822. * on existing objects.
  1823. *
  1824. * @return void
  1825. */
  1826. public function testMergeBelongsToManyIdsRetainJoinData()
  1827. {
  1828. $this->articles->belongsToMany('Tags');
  1829. $entity = $this->articles->get(1, ['contain' => ['Tags']]);
  1830. $entity->accessible('*', true);
  1831. $original = $entity->tags[0]->_joinData;
  1832. $this->assertInstanceOf('Cake\ORM\Entity', $entity->tags[0]->_joinData);
  1833. $data = [
  1834. 'title' => 'Haz moar tags',
  1835. 'tags' => [
  1836. ['id' => 1],
  1837. ]
  1838. ];
  1839. $marshall = new Marshaller($this->articles);
  1840. $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
  1841. $this->assertCount(1, $result->tags);
  1842. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  1843. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]->_joinData);
  1844. $this->assertSame($original, $result->tags[0]->_joinData, 'Should be same object');
  1845. }
  1846. /**
  1847. * Test mergeMany() with a simple set of data.
  1848. *
  1849. * @return void
  1850. */
  1851. public function testMergeManySimple()
  1852. {
  1853. $entities = [
  1854. new OpenEntity(['id' => 1, 'comment' => 'First post', 'user_id' => 2]),
  1855. new OpenEntity(['id' => 2, 'comment' => 'Second post', 'user_id' => 2])
  1856. ];
  1857. $entities[0]->clean();
  1858. $entities[1]->clean();
  1859. $data = [
  1860. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2],
  1861. ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 1]
  1862. ];
  1863. $marshall = new Marshaller($this->comments);
  1864. $result = $marshall->mergeMany($entities, $data);
  1865. $this->assertSame($entities[0], $result[0]);
  1866. $this->assertSame($entities[1], $result[1]);
  1867. $this->assertEquals('Changed 1', $result[0]->comment);
  1868. $this->assertEquals(1, $result[0]->user_id);
  1869. $this->assertEquals('Changed 2', $result[1]->comment);
  1870. $this->assertTrue($result[0]->dirty('user_id'));
  1871. $this->assertFalse($result[1]->dirty('user_id'));
  1872. }
  1873. /**
  1874. * Test mergeMany() with some invalid data
  1875. *
  1876. * @return void
  1877. */
  1878. public function testMergeManyInvalidData()
  1879. {
  1880. $entities = [
  1881. new OpenEntity(['id' => 1, 'comment' => 'First post', 'user_id' => 2]),
  1882. new OpenEntity(['id' => 2, 'comment' => 'Second post', 'user_id' => 2])
  1883. ];
  1884. $entities[0]->clean();
  1885. $entities[1]->clean();
  1886. $data = [
  1887. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2],
  1888. ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 1],
  1889. '_csrfToken' => 'abc123',
  1890. ];
  1891. $marshall = new Marshaller($this->comments);
  1892. $result = $marshall->mergeMany($entities, $data);
  1893. $this->assertSame($entities[0], $result[0]);
  1894. $this->assertSame($entities[1], $result[1]);
  1895. }
  1896. /**
  1897. * Tests that only records found in the data array are returned, those that cannot
  1898. * be matched are discarded
  1899. *
  1900. * @return void
  1901. */
  1902. public function testMergeManyWithAppend()
  1903. {
  1904. $entities = [
  1905. new OpenEntity(['comment' => 'First post', 'user_id' => 2]),
  1906. new OpenEntity(['id' => 2, 'comment' => 'Second post', 'user_id' => 2])
  1907. ];
  1908. $entities[0]->clean();
  1909. $entities[1]->clean();
  1910. $data = [
  1911. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2],
  1912. ['id' => 1, 'comment' => 'Comment 1', 'user_id' => 1]
  1913. ];
  1914. $marshall = new Marshaller($this->comments);
  1915. $result = $marshall->mergeMany($entities, $data);
  1916. $this->assertCount(2, $result);
  1917. $this->assertNotSame($entities[0], $result[0]);
  1918. $this->assertSame($entities[1], $result[0]);
  1919. $this->assertEquals('Changed 2', $result[0]->comment);
  1920. $this->assertEquals('Comment 1', $result[1]->comment);
  1921. }
  1922. /**
  1923. * Test that mergeMany() handles composite key associations properly.
  1924. *
  1925. * The articles_tags table has a composite primary key, and should be
  1926. * handled correctly.
  1927. *
  1928. * @return void
  1929. */
  1930. public function testMergeManyCompositeKey()
  1931. {
  1932. $articlesTags = TableRegistry::get('ArticlesTags');
  1933. $entities = [
  1934. new OpenEntity(['article_id' => 1, 'tag_id' => 2]),
  1935. new OpenEntity(['article_id' => 1, 'tag_id' => 1]),
  1936. ];
  1937. $entities[0]->clean();
  1938. $entities[1]->clean();
  1939. $data = [
  1940. ['article_id' => 1, 'tag_id' => 1],
  1941. ['article_id' => 1, 'tag_id' => 2]
  1942. ];
  1943. $marshall = new Marshaller($articlesTags);
  1944. $result = $marshall->mergeMany($entities, $data);
  1945. $this->assertCount(2, $result, 'Should have two records');
  1946. $this->assertSame($entities[0], $result[0], 'Should retain object');
  1947. $this->assertSame($entities[1], $result[1], 'Should retain object');
  1948. }
  1949. /**
  1950. * Test mergeMany() when the exist check returns nothing.
  1951. *
  1952. * @return void
  1953. */
  1954. public function testMergeManyExistQueryFails()
  1955. {
  1956. $entities = [
  1957. new Entity(['id' => 1, 'comment' => 'First post', 'user_id' => 2]),
  1958. new Entity(['id' => 2, 'comment' => 'Second post', 'user_id' => 2])
  1959. ];
  1960. $entities[0]->clean();
  1961. $entities[1]->clean();
  1962. $data = [
  1963. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2],
  1964. ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 1],
  1965. ['id' => 3, 'comment' => 'New 1'],
  1966. ];
  1967. $comments = TableRegistry::get('GreedyComments', [
  1968. 'className' => __NAMESPACE__ . '\\GreedyCommentsTable'
  1969. ]);
  1970. $marshall = new Marshaller($comments);
  1971. $result = $marshall->mergeMany($entities, $data);
  1972. $this->assertCount(3, $result);
  1973. $this->assertEquals('Changed 1', $result[0]->comment);
  1974. $this->assertEquals(1, $result[0]->user_id);
  1975. $this->assertEquals('Changed 2', $result[1]->comment);
  1976. $this->assertEquals('New 1', $result[2]->comment);
  1977. }
  1978. /**
  1979. * Tests merge with data types that need to be marshalled
  1980. *
  1981. * @return void
  1982. */
  1983. public function testMergeComplexType()
  1984. {
  1985. $entity = new Entity(
  1986. ['comment' => 'My Comment text'],
  1987. ['markNew' => false, 'markClean' => true]
  1988. );
  1989. $data = [
  1990. 'created' => [
  1991. 'year' => '2014',
  1992. 'month' => '2',
  1993. 'day' => 14
  1994. ]
  1995. ];
  1996. $marshall = new Marshaller($this->comments);
  1997. $result = $marshall->merge($entity, $data);
  1998. $this->assertInstanceOf('DateTime', $entity->created);
  1999. $this->assertEquals('2014-02-14', $entity->created->format('Y-m-d'));
  2000. }
  2001. /**
  2002. * Tests that it is possible to pass a fieldList option to the marshaller
  2003. *
  2004. * @return void
  2005. */
  2006. public function testOneWithFieldList()
  2007. {
  2008. $data = [
  2009. 'title' => 'My title',
  2010. 'body' => 'My content',
  2011. 'author_id' => null
  2012. ];
  2013. $marshall = new Marshaller($this->articles);
  2014. $result = $marshall->one($data, ['fieldList' => ['title', 'author_id']]);
  2015. $this->assertInstanceOf('Cake\ORM\Entity', $result);
  2016. unset($data['body']);
  2017. $this->assertEquals($data, $result->toArray());
  2018. }
  2019. /**
  2020. * Tests that it is possible to pass a fieldList option to the merge method
  2021. *
  2022. * @return void
  2023. */
  2024. public function testMergeWithFieldList()
  2025. {
  2026. $data = [
  2027. 'title' => 'My title',
  2028. 'body' => null,
  2029. 'author_id' => 1
  2030. ];
  2031. $marshall = new Marshaller($this->articles);
  2032. $entity = new Entity([
  2033. 'title' => 'Foo',
  2034. 'body' => 'My content',
  2035. 'author_id' => 2
  2036. ]);
  2037. $entity->accessible('*', false);
  2038. $entity->isNew(false);
  2039. $entity->clean();
  2040. $result = $marshall->merge($entity, $data, ['fieldList' => ['title', 'body']]);
  2041. $expected = [
  2042. 'title' => 'My title',
  2043. 'body' => null,
  2044. 'author_id' => 2
  2045. ];
  2046. $this->assertSame($entity, $result);
  2047. $this->assertEquals($expected, $result->toArray());
  2048. $this->assertFalse($entity->accessible('*'));
  2049. }
  2050. /**
  2051. * Test that many() also receives a fieldList option
  2052. *
  2053. * @return void
  2054. */
  2055. public function testManyFieldList()
  2056. {
  2057. $data = [
  2058. ['comment' => 'First post', 'user_id' => 2, 'foo' => 'bar'],
  2059. ['comment' => 'Second post', 'user_id' => 2, 'foo' => 'bar'],
  2060. ];
  2061. $marshall = new Marshaller($this->comments);
  2062. $result = $marshall->many($data, ['fieldList' => ['comment', 'user_id']]);
  2063. $this->assertCount(2, $result);
  2064. unset($data[0]['foo'], $data[1]['foo']);
  2065. $this->assertEquals($data[0], $result[0]->toArray());
  2066. $this->assertEquals($data[1], $result[1]->toArray());
  2067. }
  2068. /**
  2069. * Test that many() also receives a fieldList option
  2070. *
  2071. * @return void
  2072. */
  2073. public function testMergeManyFieldList()
  2074. {
  2075. $entities = [
  2076. new OpenEntity(['id' => 1, 'comment' => 'First post', 'user_id' => 2]),
  2077. new OpenEntity(['id' => 2, 'comment' => 'Second post', 'user_id' => 2])
  2078. ];
  2079. $entities[0]->clean();
  2080. $entities[1]->clean();
  2081. $data = [
  2082. ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 10],
  2083. ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 20]
  2084. ];
  2085. $marshall = new Marshaller($this->comments);
  2086. $result = $marshall->mergeMany($entities, $data, ['fieldList' => ['id', 'comment']]);
  2087. $this->assertSame($entities[0], $result[0]);
  2088. $this->assertSame($entities[1], $result[1]);
  2089. $expected = ['id' => 2, 'comment' => 'Changed 2', 'user_id' => 2];
  2090. $this->assertEquals($expected, $entities[1]->toArray());
  2091. $expected = ['id' => 1, 'comment' => 'Changed 1', 'user_id' => 2];
  2092. $this->assertEquals($expected, $entities[0]->toArray());
  2093. }
  2094. /**
  2095. * test marshalling association data while passing a fieldList
  2096. *
  2097. * @return void
  2098. */
  2099. public function testAssociatoinsFieldList()
  2100. {
  2101. $data = [
  2102. 'title' => 'My title',
  2103. 'body' => 'My content',
  2104. 'author_id' => 1,
  2105. 'user' => [
  2106. 'username' => 'mark',
  2107. 'password' => 'secret',
  2108. 'foo' => 'bar'
  2109. ]
  2110. ];
  2111. $marshall = new Marshaller($this->articles);
  2112. $result = $marshall->one($data, [
  2113. 'fieldList' => ['title', 'body', 'user'],
  2114. 'associated' => [
  2115. 'Users' => ['fieldList' => ['username', 'foo']]
  2116. ]
  2117. ]);
  2118. $this->assertEquals($data['title'], $result->title);
  2119. $this->assertEquals($data['body'], $result->body);
  2120. $this->assertNull($result->author_id);
  2121. $this->assertInstanceOf('Cake\ORM\Entity', $result->user);
  2122. $this->assertEquals($data['user']['username'], $result->user->username);
  2123. $this->assertNull($result->user->password);
  2124. }
  2125. /**
  2126. * Tests merging associated data with a fieldList
  2127. *
  2128. * @return void
  2129. */
  2130. public function testMergeAssociationWithfieldList()
  2131. {
  2132. $user = new Entity([
  2133. 'username' => 'mark',
  2134. 'password' => 'secret'
  2135. ]);
  2136. $entity = new Entity([
  2137. 'tile' => 'My Title',
  2138. 'user' => $user
  2139. ]);
  2140. $user->accessible('*', true);
  2141. $entity->accessible('*', true);
  2142. $data = [
  2143. 'body' => 'My Content',
  2144. 'something' => 'else',
  2145. 'user' => [
  2146. 'password' => 'not a secret',
  2147. 'extra' => 'data'
  2148. ]
  2149. ];
  2150. $marshall = new Marshaller($this->articles);
  2151. $marshall->merge($entity, $data, [
  2152. 'fieldList' => ['something'],
  2153. 'associated' => ['Users' => ['fieldList' => ['extra']]]
  2154. ]);
  2155. $this->assertNull($entity->body);
  2156. $this->assertEquals('else', $entity->something);
  2157. $this->assertSame($user, $entity->user);
  2158. $this->assertEquals('mark', $entity->user->username);
  2159. $this->assertEquals('secret', $entity->user->password);
  2160. $this->assertEquals('data', $entity->user->extra);
  2161. $this->assertTrue($entity->dirty('user'));
  2162. }
  2163. /**
  2164. * Test marshalling nested associations on the _joinData structure
  2165. * while having a fieldList
  2166. *
  2167. * @return void
  2168. */
  2169. public function testJoinDataWhiteList()
  2170. {
  2171. $data = [
  2172. 'title' => 'My title',
  2173. 'body' => 'My content',
  2174. 'author_id' => 1,
  2175. 'tags' => [
  2176. [
  2177. 'tag' => 'news',
  2178. '_joinData' => [
  2179. 'active' => 1,
  2180. 'crazy' => 'data',
  2181. 'user' => ['username' => 'Bill'],
  2182. ]
  2183. ],
  2184. [
  2185. 'tag' => 'cakephp',
  2186. '_joinData' => [
  2187. 'active' => 0,
  2188. 'crazy' => 'stuff',
  2189. 'user' => ['username' => 'Mark'],
  2190. ]
  2191. ],
  2192. ],
  2193. ];
  2194. $articlesTags = TableRegistry::get('ArticlesTags');
  2195. $articlesTags->belongsTo('Users');
  2196. $marshall = new Marshaller($this->articles);
  2197. $result = $marshall->one($data, [
  2198. 'associated' => [
  2199. 'Tags._joinData' => ['fieldList' => ['active', 'user']],
  2200. 'Tags._joinData.Users'
  2201. ]
  2202. ]);
  2203. $this->assertInstanceOf(
  2204. 'Cake\ORM\Entity',
  2205. $result->tags[0]->_joinData->user,
  2206. 'joinData should contain a user entity.'
  2207. );
  2208. $this->assertEquals('Bill', $result->tags[0]->_joinData->user->username);
  2209. $this->assertInstanceOf(
  2210. 'Cake\ORM\Entity',
  2211. $result->tags[1]->_joinData->user,
  2212. 'joinData should contain a user entity.'
  2213. );
  2214. $this->assertEquals('Mark', $result->tags[1]->_joinData->user->username);
  2215. $this->assertNull($result->tags[0]->_joinData->crazy);
  2216. $this->assertNull($result->tags[1]->_joinData->crazy);
  2217. }
  2218. /**
  2219. * Test merging the _joinData entity for belongstomany associations
  2220. * while passing a whitelist
  2221. *
  2222. * @return void
  2223. */
  2224. public function testMergeJoinDataWithFieldList()
  2225. {
  2226. $data = [
  2227. 'title' => 'My title',
  2228. 'body' => 'My content',
  2229. 'author_id' => 1,
  2230. 'tags' => [
  2231. [
  2232. 'id' => 1,
  2233. 'tag' => 'news',
  2234. '_joinData' => [
  2235. 'active' => 0
  2236. ]
  2237. ],
  2238. [
  2239. 'id' => 2,
  2240. 'tag' => 'cakephp',
  2241. '_joinData' => [
  2242. 'active' => 0
  2243. ]
  2244. ],
  2245. ],
  2246. ];
  2247. $options = ['associated' => ['Tags' => ['associated' => ['_joinData']]]];
  2248. $marshall = new Marshaller($this->articles);
  2249. $entity = $marshall->one($data, $options);
  2250. $entity->accessible('*', true);
  2251. $data = [
  2252. 'title' => 'Haz data',
  2253. 'tags' => [
  2254. ['id' => 1, 'tag' => 'Cake', '_joinData' => ['foo' => 'bar', 'crazy' => 'something']],
  2255. ['tag' => 'new tag', '_joinData' => ['active' => 1, 'foo' => 'baz']]
  2256. ]
  2257. ];
  2258. $tag1 = $entity->tags[0];
  2259. $result = $marshall->merge($entity, $data, [
  2260. 'associated' => ['Tags._joinData' => ['fieldList' => ['foo']]]
  2261. ]);
  2262. $this->assertEquals($data['title'], $result->title);
  2263. $this->assertEquals('My content', $result->body);
  2264. $this->assertSame($tag1, $entity->tags[0]);
  2265. $this->assertSame($tag1->_joinData, $entity->tags[0]->_joinData);
  2266. $this->assertSame(
  2267. ['active' => 0, 'foo' => 'bar'],
  2268. $entity->tags[0]->_joinData->toArray()
  2269. );
  2270. $this->assertSame(
  2271. ['foo' => 'baz'],
  2272. $entity->tags[1]->_joinData->toArray()
  2273. );
  2274. $this->assertEquals('new tag', $entity->tags[1]->tag);
  2275. $this->assertTrue($entity->tags[0]->dirty('_joinData'));
  2276. $this->assertTrue($entity->tags[1]->dirty('_joinData'));
  2277. }
  2278. /**
  2279. * Tests marshalling with validation errors
  2280. *
  2281. * @return void
  2282. */
  2283. public function testValidationFail()
  2284. {
  2285. $data = [
  2286. 'title' => 'Thing',
  2287. 'body' => 'hey'
  2288. ];
  2289. $this->articles->validator()->requirePresence('thing');
  2290. $marshall = new Marshaller($this->articles);
  2291. $entity = $marshall->one($data);
  2292. $this->assertNotEmpty($entity->errors('thing'));
  2293. }
  2294. /**
  2295. * Test that invalid validate options raise exceptions
  2296. *
  2297. * @expectedException \RuntimeException
  2298. * @return void
  2299. */
  2300. public function testValidateInvalidType()
  2301. {
  2302. $data = ['title' => 'foo'];
  2303. $marshaller = new Marshaller($this->articles);
  2304. $marshaller->one($data, [
  2305. 'validate' => ['derp'],
  2306. ]);
  2307. }
  2308. /**
  2309. * Tests that associations are validated and custom validators can be used
  2310. *
  2311. * @return void
  2312. */
  2313. public function testValidateWithAssociationsAndCustomValidator()
  2314. {
  2315. $data = [
  2316. 'title' => 'foo',
  2317. 'body' => 'bar',
  2318. 'user' => [
  2319. 'name' => 'Susan'
  2320. ],
  2321. 'comments' => [
  2322. [
  2323. 'comment' => 'foo'
  2324. ]
  2325. ]
  2326. ];
  2327. $validator = (new Validator)->add('body', 'numeric', ['rule' => 'numeric']);
  2328. $this->articles->validator('custom', $validator);
  2329. $validator2 = (new Validator)->requirePresence('thing');
  2330. $this->articles->Users->validator('customThing', $validator2);
  2331. $this->articles->Comments->validator('default', $validator2);
  2332. $entity = (new Marshaller($this->articles))->one($data, [
  2333. 'validate' => 'custom',
  2334. 'associated' => ['Users', 'Comments']
  2335. ]);
  2336. $this->assertNotEmpty($entity->errors('body'), 'custom was not used');
  2337. $this->assertNull($entity->body);
  2338. $this->assertEmpty($entity->user->errors('thing'));
  2339. $this->assertNotEmpty($entity->comments[0]->errors('thing'));
  2340. $entity = (new Marshaller($this->articles))->one($data, [
  2341. 'validate' => 'custom',
  2342. 'associated' => ['Users' => ['validate' => 'customThing'], 'Comments']
  2343. ]);
  2344. $this->assertNotEmpty($entity->errors('body'));
  2345. $this->assertNull($entity->body);
  2346. $this->assertNotEmpty($entity->user->errors('thing'), 'customThing was not used');
  2347. $this->assertNotEmpty($entity->comments[0]->errors('thing'));
  2348. }
  2349. /**
  2350. * Tests that validation can be bypassed
  2351. *
  2352. * @return void
  2353. */
  2354. public function testSkipValidation()
  2355. {
  2356. $data = [
  2357. 'title' => 'foo',
  2358. 'body' => 'bar',
  2359. 'user' => [
  2360. 'name' => 'Susan'
  2361. ],
  2362. ];
  2363. $validator = (new Validator)->requirePresence('thing');
  2364. $this->articles->validator('default', $validator);
  2365. $this->articles->Users->validator('default', $validator);
  2366. $entity = (new Marshaller($this->articles))->one($data, [
  2367. 'validate' => false,
  2368. 'associated' => ['Users']
  2369. ]);
  2370. $this->assertEmpty($entity->errors('thing'));
  2371. $this->assertNotEmpty($entity->user->errors('thing'));
  2372. $entity = (new Marshaller($this->articles))->one($data, [
  2373. 'associated' => ['Users' => ['validate' => false]]
  2374. ]);
  2375. $this->assertNotEmpty($entity->errors('thing'));
  2376. $this->assertEmpty($entity->user->errors('thing'));
  2377. }
  2378. /**
  2379. * Tests that it is possible to pass a validator directly in the options
  2380. *
  2381. * @return void
  2382. */
  2383. public function testPassingCustomValidator()
  2384. {
  2385. $data = [
  2386. 'title' => 'Thing',
  2387. 'body' => 'hey'
  2388. ];
  2389. $validator = clone $this->articles->validator();
  2390. $validator->requirePresence('thing');
  2391. $marshall = new Marshaller($this->articles);
  2392. $entity = $marshall->one($data, ['validate' => $validator]);
  2393. $this->assertNotEmpty($entity->errors('thing'));
  2394. }
  2395. /**
  2396. * Tests that invalid property is being filled when data cannot be patched into an entity.
  2397. *
  2398. * @return void
  2399. */
  2400. public function testValidationWithInvalidFilled()
  2401. {
  2402. $data = [
  2403. 'title' => 'foo',
  2404. 'number' => 'bar',
  2405. ];
  2406. $validator = (new Validator)->add('number', 'numeric', ['rule' => 'numeric']);
  2407. $marshall = new Marshaller($this->articles);
  2408. $entity = $marshall->one($data, ['validate' => $validator]);
  2409. $this->assertNotEmpty($entity->errors('number'));
  2410. $this->assertNull($entity->number);
  2411. $this->assertSame(['number' => 'bar'], $entity->invalid());
  2412. }
  2413. /**
  2414. * Test merge with validation error
  2415. *
  2416. * @return void
  2417. */
  2418. public function testMergeWithValidation()
  2419. {
  2420. $data = [
  2421. 'title' => 'My title',
  2422. 'author_id' => 'foo',
  2423. ];
  2424. $marshall = new Marshaller($this->articles);
  2425. $entity = new Entity([
  2426. 'id' => 1,
  2427. 'title' => 'Foo',
  2428. 'body' => 'My Content',
  2429. 'author_id' => 1
  2430. ]);
  2431. $this->assertEmpty($entity->invalid());
  2432. $entity->accessible('*', true);
  2433. $entity->isNew(false);
  2434. $entity->clean();
  2435. $this->articles->validator()
  2436. ->requirePresence('thing', 'update')
  2437. ->requirePresence('id', 'update')
  2438. ->add('author_id', 'numeric', ['rule' => 'numeric'])
  2439. ->add('id', 'numeric', ['rule' => 'numeric', 'on' => 'update']);
  2440. $expected = clone $entity;
  2441. $result = $marshall->merge($expected, $data, []);
  2442. $this->assertSame($expected, $result);
  2443. $this->assertSame(1, $result->author_id);
  2444. $this->assertNotEmpty($result->errors('thing'));
  2445. $this->assertEmpty($result->errors('id'));
  2446. $this->articles->validator()->requirePresence('thing', 'create');
  2447. $result = $marshall->merge($entity, $data, []);
  2448. $this->assertEmpty($result->errors('thing'));
  2449. $this->assertSame(['author_id' => 'foo'], $result->invalid());
  2450. }
  2451. /**
  2452. * Test merge with validation and create or update validation rules
  2453. *
  2454. * @return void
  2455. */
  2456. public function testMergeWithCreate()
  2457. {
  2458. $data = [
  2459. 'title' => 'My title',
  2460. 'author_id' => 'foo',
  2461. ];
  2462. $marshall = new Marshaller($this->articles);
  2463. $entity = new Entity([
  2464. 'title' => 'Foo',
  2465. 'body' => 'My Content',
  2466. 'author_id' => 1
  2467. ]);
  2468. $entity->accessible('*', true);
  2469. $entity->isNew(true);
  2470. $entity->clean();
  2471. $this->articles->validator()
  2472. ->requirePresence('thing', 'update')
  2473. ->add('author_id', 'numeric', ['rule' => 'numeric', 'on' => 'update']);
  2474. $expected = clone $entity;
  2475. $result = $marshall->merge($expected, $data, []);
  2476. $this->assertEmpty($result->errors('author_id'));
  2477. $this->assertEmpty($result->errors('thing'));
  2478. $entity->clean();
  2479. $entity->isNew(false);
  2480. $result = $marshall->merge($entity, $data, []);
  2481. $this->assertNotEmpty($result->errors('author_id'));
  2482. $this->assertNotEmpty($result->errors('thing'));
  2483. }
  2484. /**
  2485. * Test Model.beforeMarshal event.
  2486. *
  2487. * @return void
  2488. */
  2489. public function testBeforeMarshalEvent()
  2490. {
  2491. $data = [
  2492. 'title' => 'My title',
  2493. 'body' => 'My content',
  2494. 'user' => [
  2495. 'name' => 'Robert',
  2496. 'username' => 'rob'
  2497. ]
  2498. ];
  2499. $marshall = new Marshaller($this->articles);
  2500. $this->articles->eventManager()->attach(function ($e, $data, $options) {
  2501. $data['title'] = 'Modified title';
  2502. $data['user']['username'] = 'robert';
  2503. $options['associated'] = ['Users'];
  2504. }, 'Model.beforeMarshal');
  2505. $entity = $marshall->one($data);
  2506. $this->assertEquals('Modified title', $entity->title);
  2507. $this->assertEquals('My content', $entity->body);
  2508. $this->assertEquals('Robert', $entity->user->name);
  2509. $this->assertEquals('robert', $entity->user->username);
  2510. }
  2511. /**
  2512. * Test Model.beforeMarshal event on associated tables.
  2513. *
  2514. * @return void
  2515. */
  2516. public function testBeforeMarshalEventOnAssociations()
  2517. {
  2518. $data = [
  2519. 'title' => 'My title',
  2520. 'body' => 'My content',
  2521. 'author_id' => 1,
  2522. 'user' => [
  2523. 'username' => 'mark',
  2524. 'password' => 'secret'
  2525. ],
  2526. 'comments' => [
  2527. ['comment' => 'First post', 'user_id' => 2],
  2528. ['comment' => 'Second post', 'user_id' => 2],
  2529. ],
  2530. 'tags' => [
  2531. ['tag' => 'news', '_joinData' => ['active' => 1]],
  2532. ['tag' => 'cakephp', '_joinData' => ['active' => 0]],
  2533. ],
  2534. ];
  2535. $marshall = new Marshaller($this->articles);
  2536. $this->articles->users->eventManager()->attach(function ($e, $data) {
  2537. $data['secret'] = 'h45h3d';
  2538. }, 'Model.beforeMarshal');
  2539. $this->articles->comments->eventManager()->attach(function ($e, $data) {
  2540. $data['comment'] .= ' (modified)';
  2541. }, 'Model.beforeMarshal');
  2542. $this->articles->tags->eventManager()->attach(function ($e, $data) {
  2543. $data['tag'] .= ' (modified)';
  2544. }, 'Model.beforeMarshal');
  2545. $this->articles->tags->junction()->eventManager()->attach(function ($e, $data) {
  2546. $data['modified_by'] = 1;
  2547. }, 'Model.beforeMarshal');
  2548. $entity = $marshall->one($data, [
  2549. 'associated' => ['Users', 'Comments', 'Tags']
  2550. ]);
  2551. $this->assertEquals('h45h3d', $entity->user->secret);
  2552. $this->assertEquals('First post (modified)', $entity->comments[0]->comment);
  2553. $this->assertEquals('Second post (modified)', $entity->comments[1]->comment);
  2554. $this->assertEquals('news (modified)', $entity->tags[0]->tag);
  2555. $this->assertEquals('cakephp (modified)', $entity->tags[1]->tag);
  2556. $this->assertEquals(1, $entity->tags[0]->_joinData->modified_by);
  2557. $this->assertEquals(1, $entity->tags[1]->_joinData->modified_by);
  2558. }
  2559. /**
  2560. * Tests that patching an association resulting in no changes, will
  2561. * not mark the parent entity as dirty
  2562. *
  2563. * @return void
  2564. */
  2565. public function testAssociationNoChanges()
  2566. {
  2567. $options = ['markClean' => true, 'isNew' => false];
  2568. $entity = new Entity([
  2569. 'title' => 'My Title',
  2570. 'user' => new Entity([
  2571. 'username' => 'mark',
  2572. 'password' => 'not a secret'
  2573. ], $options)
  2574. ], $options);
  2575. $data = [
  2576. 'body' => 'My Content',
  2577. 'user' => [
  2578. 'username' => 'mark',
  2579. 'password' => 'not a secret'
  2580. ]
  2581. ];
  2582. $marshall = new Marshaller($this->articles);
  2583. $marshall->merge($entity, $data, ['associated' => ['Users']]);
  2584. $this->assertEquals('My Content', $entity->body);
  2585. $this->assertInstanceOf('Cake\ORM\Entity', $entity->user);
  2586. $this->assertEquals('mark', $entity->user->username);
  2587. $this->assertEquals('not a secret', $entity->user->password);
  2588. $this->assertFalse($entity->dirty('user'));
  2589. $this->assertTrue($entity->user->isNew());
  2590. }
  2591. }