MarshallerTest.php 85 KB

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