EntityTest.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  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\ORM\Entity;
  17. use Cake\TestSuite\TestCase;
  18. use Cake\Validation\Validator;
  19. use TestApp\Model\Entity\Extending;
  20. use TestApp\Model\Entity\NonExtending;
  21. /**
  22. * Entity test case.
  23. */
  24. class EntityTest extends TestCase
  25. {
  26. /**
  27. * Tests setting a single property in an entity without custom setters
  28. *
  29. * @return void
  30. */
  31. public function testSetOneParamNoSetters()
  32. {
  33. $entity = new Entity;
  34. $this->assertNull($entity->getOriginal('foo'));
  35. $entity->set('foo', 'bar');
  36. $this->assertEquals('bar', $entity->foo);
  37. $this->assertEquals('bar', $entity->getOriginal('foo'));
  38. $entity->set('foo', 'baz');
  39. $this->assertEquals('baz', $entity->foo);
  40. $this->assertEquals('bar', $entity->getOriginal('foo'));
  41. $entity->set('id', 1);
  42. $this->assertSame(1, $entity->id);
  43. $this->assertEquals(1, $entity->getOriginal('id'));
  44. $this->assertEquals('bar', $entity->getOriginal('foo'));
  45. }
  46. /**
  47. * Tests setting multiple properties without custom setters
  48. *
  49. * @return void
  50. */
  51. public function testSetMultiplePropertiesNoSetters()
  52. {
  53. $entity = new Entity;
  54. $entity->accessible('*', true);
  55. $entity->set(['foo' => 'bar', 'id' => 1]);
  56. $this->assertEquals('bar', $entity->foo);
  57. $this->assertSame(1, $entity->id);
  58. $entity->set(['foo' => 'baz', 'id' => 2, 'thing' => 3]);
  59. $this->assertEquals('baz', $entity->foo);
  60. $this->assertSame(2, $entity->id);
  61. $this->assertSame(3, $entity->thing);
  62. $this->assertEquals('bar', $entity->getOriginal('foo'));
  63. $this->assertEquals(1, $entity->getOriginal('id'));
  64. }
  65. /**
  66. * Test extractOriginal()
  67. *
  68. * @return void
  69. */
  70. public function testExtractOriginal()
  71. {
  72. $entity = new Entity([
  73. 'id' => 1,
  74. 'title' => 'original',
  75. 'body' => 'no'
  76. ], ['markNew' => true]);
  77. $entity->set('body', 'updated body');
  78. $result = $entity->extractOriginal(['id', 'title', 'body']);
  79. $expected = [
  80. 'id' => 1,
  81. 'title' => 'original',
  82. 'body' => 'no'
  83. ];
  84. $this->assertEquals($expected, $result);
  85. $result = $entity->extractOriginalChanged(['id', 'title', 'body']);
  86. $expected = [
  87. 'body' => 'no',
  88. ];
  89. $this->assertEquals($expected, $result);
  90. }
  91. /**
  92. * Tests setting a single property using a setter function
  93. *
  94. * @return void
  95. */
  96. public function testSetOneParamWithSetter()
  97. {
  98. $entity = $this->getMock('\Cake\ORM\Entity', ['_setName']);
  99. $entity->expects($this->once())->method('_setName')
  100. ->with('Jones')
  101. ->will($this->returnCallback(function ($name) {
  102. $this->assertEquals('Jones', $name);
  103. return 'Dr. ' . $name;
  104. }));
  105. $entity->set('name', 'Jones');
  106. $this->assertEquals('Dr. Jones', $entity->name);
  107. }
  108. /**
  109. * Tests setting multiple properties using a setter function
  110. *
  111. * @return void
  112. */
  113. public function testMultipleWithSetter()
  114. {
  115. $entity = $this->getMock('\Cake\ORM\Entity', ['_setName', '_setStuff']);
  116. $entity->accessible('*', true);
  117. $entity->expects($this->once())->method('_setName')
  118. ->with('Jones')
  119. ->will($this->returnCallback(function ($name) {
  120. $this->assertEquals('Jones', $name);
  121. return 'Dr. ' . $name;
  122. }));
  123. $entity->expects($this->once())->method('_setStuff')
  124. ->with(['a', 'b'])
  125. ->will($this->returnCallback(function ($stuff) {
  126. $this->assertEquals(['a', 'b'], $stuff);
  127. return ['c', 'd'];
  128. }));
  129. $entity->set(['name' => 'Jones', 'stuff' => ['a', 'b']]);
  130. $this->assertEquals('Dr. Jones', $entity->name);
  131. $this->assertEquals(['c', 'd'], $entity->stuff);
  132. }
  133. /**
  134. * Tests that it is possible to bypass the setters
  135. *
  136. * @return void
  137. */
  138. public function testBypassSetters()
  139. {
  140. $entity = $this->getMock('\Cake\ORM\Entity', ['_setName', '_setStuff']);
  141. $entity->accessible('*', true);
  142. $entity->expects($this->never())->method('_setName');
  143. $entity->expects($this->never())->method('_setStuff');
  144. $entity->set('name', 'Jones', ['setter' => false]);
  145. $this->assertEquals('Jones', $entity->name);
  146. $entity->set('stuff', 'Thing', ['setter' => false]);
  147. $this->assertEquals('Thing', $entity->stuff);
  148. $entity->set(['name' => 'foo', 'stuff' => 'bar'], ['setter' => false]);
  149. $this->assertEquals('bar', $entity->stuff);
  150. }
  151. /**
  152. * Tests that the constructor will set initial properties
  153. *
  154. * @return void
  155. */
  156. public function testConstructor()
  157. {
  158. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  159. ->setMethods(['set'])
  160. ->disableOriginalConstructor()
  161. ->getMock();
  162. $entity->expects($this->at(0))
  163. ->method('set')
  164. ->with(['a' => 'b', 'c' => 'd'], ['setter' => true, 'guard' => false]);
  165. $entity->expects($this->at(1))
  166. ->method('set')
  167. ->with(['foo' => 'bar'], ['setter' => false, 'guard' => false]);
  168. $entity->__construct(['a' => 'b', 'c' => 'd']);
  169. $entity->__construct(['foo' => 'bar'], ['useSetters' => false]);
  170. }
  171. /**
  172. * Tests that the constructor will set initial properties and pass the guard
  173. * option along
  174. *
  175. * @return void
  176. */
  177. public function testConstructorWithGuard()
  178. {
  179. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  180. ->setMethods(['set'])
  181. ->disableOriginalConstructor()
  182. ->getMock();
  183. $entity->expects($this->once())
  184. ->method('set')
  185. ->with(['foo' => 'bar'], ['setter' => true, 'guard' => true]);
  186. $entity->__construct(['foo' => 'bar'], ['guard' => true]);
  187. }
  188. /**
  189. * Tests getting properties with no custom getters
  190. *
  191. * @return void
  192. */
  193. public function testGetNoGetters()
  194. {
  195. $entity = new Entity(['id' => 1, 'foo' => 'bar']);
  196. $this->assertSame(1, $entity->get('id'));
  197. $this->assertSame('bar', $entity->get('foo'));
  198. }
  199. /**
  200. * Tests get with custom getter
  201. *
  202. * @return void
  203. */
  204. public function testGetCustomGetters()
  205. {
  206. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  207. $entity->expects($this->once())->method('_getName')
  208. ->with('Jones')
  209. ->will($this->returnCallback(function ($name) {
  210. return 'Dr. ' . $name;
  211. }));
  212. $entity->set('name', 'Jones');
  213. $this->assertEquals('Dr. Jones', $entity->get('name'));
  214. $this->assertEquals('Dr. Jones', $entity->get('name'));
  215. }
  216. /**
  217. * Tests get with custom getter
  218. *
  219. * @return void
  220. */
  221. public function testGetCustomGettersAfterSet()
  222. {
  223. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  224. $entity->expects($this->exactly(2))->method('_getName')
  225. ->will($this->returnCallback(function ($name) {
  226. return 'Dr. ' . $name;
  227. }));
  228. $entity->set('name', 'Jones');
  229. $this->assertEquals('Dr. Jones', $entity->get('name'));
  230. $this->assertEquals('Dr. Jones', $entity->get('name'));
  231. $entity->set('name', 'Mark');
  232. $this->assertEquals('Dr. Mark', $entity->get('name'));
  233. $this->assertEquals('Dr. Mark', $entity->get('name'));
  234. }
  235. /**
  236. * Tests that the get cache is cleared by unsetProperty.
  237. *
  238. * @return void
  239. */
  240. public function testGetCacheClearedByUnset()
  241. {
  242. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  243. $entity->expects($this->any())->method('_getName')
  244. ->will($this->returnCallback(function ($name) {
  245. return 'Dr. ' . $name;
  246. }));
  247. $entity->set('name', 'Jones');
  248. $this->assertEquals('Dr. Jones', $entity->get('name'));
  249. $entity->unsetProperty('name');
  250. $this->assertEquals('Dr. ', $entity->get('name'));
  251. }
  252. /**
  253. * Tests that the get cache is cleared by setting any property.
  254. * This is because virtual properties can often rely on other
  255. * properties in the entity.
  256. *
  257. * @return void
  258. */
  259. public function testGetCacheClearedBySet()
  260. {
  261. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  262. $entity->last_name = 'Smith';
  263. $entity->name = 'John';
  264. $entity->expects($this->any())->method('_getName')
  265. ->will($this->returnCallback(function ($name) use ($entity) {
  266. return 'Dr. ' . $name . ' ' . $entity->last_name;
  267. }));
  268. $this->assertEquals('Dr. John Smith', $entity->get('name'));
  269. $entity->last_name = 'Jones';
  270. $this->assertEquals('Dr. John Jones', $entity->get('name'));
  271. }
  272. /**
  273. * Test magic property setting with no custom setter
  274. *
  275. * @return void
  276. */
  277. public function testMagicSet()
  278. {
  279. $entity = new Entity;
  280. $entity->name = 'Jones';
  281. $this->assertEquals('Jones', $entity->name);
  282. $entity->name = 'George';
  283. $this->assertEquals('George', $entity->name);
  284. }
  285. /**
  286. * Tests magic set with custom setter function
  287. *
  288. * @return void
  289. */
  290. public function testMagicSetWithSetter()
  291. {
  292. $entity = $this->getMock('\Cake\ORM\Entity', ['_setName']);
  293. $entity->expects($this->once())->method('_setName')
  294. ->with('Jones')
  295. ->will($this->returnCallback(function ($name) {
  296. $this->assertEquals('Jones', $name);
  297. return 'Dr. ' . $name;
  298. }));
  299. $entity->name = 'Jones';
  300. $this->assertEquals('Dr. Jones', $entity->name);
  301. }
  302. /**
  303. * Tests the magic getter with a custom getter function
  304. *
  305. * @return void
  306. */
  307. public function testMagicGetWithGetter()
  308. {
  309. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  310. $entity->expects($this->once())->method('_getName')
  311. ->with('Jones')
  312. ->will($this->returnCallback(function ($name) {
  313. $this->assertSame('Jones', $name);
  314. return 'Dr. ' . $name;
  315. }));
  316. $entity->set('name', 'Jones');
  317. $this->assertEquals('Dr. Jones', $entity->name);
  318. }
  319. /**
  320. * Test indirectly modifying internal properties
  321. *
  322. * @return void
  323. */
  324. public function testIndirectModification()
  325. {
  326. $entity = new Entity;
  327. $entity->things = ['a', 'b'];
  328. $entity->things[] = 'c';
  329. $this->assertEquals(['a', 'b', 'c'], $entity->things);
  330. }
  331. /**
  332. * Tests has() method
  333. *
  334. * @return void
  335. */
  336. public function testHas()
  337. {
  338. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  339. $this->assertTrue($entity->has('id'));
  340. $this->assertTrue($entity->has('name'));
  341. $this->assertFalse($entity->has('foo'));
  342. $this->assertFalse($entity->has('last_name'));
  343. $this->assertTrue($entity->has(['id']));
  344. $this->assertTrue($entity->has(['id', 'name']));
  345. $this->assertFalse($entity->has(['id', 'foo']));
  346. $this->assertFalse($entity->has(['id', 'nope']));
  347. $entity = $this->getMock('\Cake\ORM\Entity', ['_getThings']);
  348. $entity->expects($this->once())->method('_getThings')
  349. ->will($this->returnValue(0));
  350. $this->assertTrue($entity->has('things'));
  351. }
  352. /**
  353. * Tests unsetProperty one property at a time
  354. *
  355. * @return void
  356. */
  357. public function testUnset()
  358. {
  359. $entity = new Entity(['id' => 1, 'name' => 'bar']);
  360. $entity->unsetProperty('id');
  361. $this->assertFalse($entity->has('id'));
  362. $this->assertTrue($entity->has('name'));
  363. $entity->unsetProperty('name');
  364. $this->assertFalse($entity->has('id'));
  365. }
  366. /**
  367. * Unsetting a property should not mark it as dirty.
  368. *
  369. * @return void
  370. */
  371. public function testUnsetMakesClean()
  372. {
  373. $entity = new Entity(['id' => 1, 'name' => 'bar']);
  374. $this->assertTrue($entity->dirty('name'));
  375. $entity->unsetProperty('name');
  376. $this->assertFalse($entity->dirty('name'), 'Removed properties are not dirty.');
  377. }
  378. /**
  379. * Tests unsetProperty whith multiple properties
  380. *
  381. * @return void
  382. */
  383. public function testUnsetMultiple()
  384. {
  385. $entity = new Entity(['id' => 1, 'name' => 'bar', 'thing' => 2]);
  386. $entity->unsetProperty(['id', 'thing']);
  387. $this->assertFalse($entity->has('id'));
  388. $this->assertTrue($entity->has('name'));
  389. $this->assertFalse($entity->has('thing'));
  390. }
  391. /**
  392. * Tests the magic __isset() method
  393. *
  394. * @return void
  395. */
  396. public function testMagicIsset()
  397. {
  398. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  399. $this->assertTrue(isset($entity->id));
  400. $this->assertTrue(isset($entity->name));
  401. $this->assertFalse(isset($entity->foo));
  402. $this->assertFalse(isset($entity->thing));
  403. }
  404. /**
  405. * Tests the magic __unset() method
  406. *
  407. * @return void
  408. */
  409. public function testMagicUnset()
  410. {
  411. $entity = $this->getMock('\Cake\ORM\Entity', ['unsetProperty']);
  412. $entity->expects($this->at(0))
  413. ->method('unsetProperty')
  414. ->with('foo');
  415. unset($entity->foo);
  416. }
  417. /**
  418. * Tests isset with array access
  419. *
  420. * @return void
  421. */
  422. public function testIssetArrayAccess()
  423. {
  424. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  425. $this->assertTrue(isset($entity['id']));
  426. $this->assertTrue(isset($entity['name']));
  427. $this->assertFalse(isset($entity['foo']));
  428. $this->assertFalse(isset($entity['thing']));
  429. }
  430. /**
  431. * Tests get property with array access
  432. *
  433. * @return void
  434. */
  435. public function testGetArrayAccess()
  436. {
  437. $entity = $this->getMock('\Cake\ORM\Entity', ['get']);
  438. $entity->expects($this->at(0))
  439. ->method('get')
  440. ->with('foo')
  441. ->will($this->returnValue('worked'));
  442. $entity->expects($this->at(1))
  443. ->method('get')
  444. ->with('bar')
  445. ->will($this->returnValue('worked too'));
  446. $this->assertEquals('worked', $entity['foo']);
  447. $this->assertEquals('worked too', $entity['bar']);
  448. }
  449. /**
  450. * Tests set with array access
  451. *
  452. * @return void
  453. */
  454. public function testSetArrayAccess()
  455. {
  456. $entity = $this->getMock('\Cake\ORM\Entity', ['set']);
  457. $entity->accessible('*', true);
  458. $entity->expects($this->at(0))
  459. ->method('set')
  460. ->with('foo', 1)
  461. ->will($this->returnSelf());
  462. $entity->expects($this->at(1))
  463. ->method('set')
  464. ->with('bar', 2)
  465. ->will($this->returnSelf());
  466. $entity['foo'] = 1;
  467. $entity['bar'] = 2;
  468. }
  469. /**
  470. * Tests unset with array access
  471. *
  472. * @return void
  473. */
  474. public function testUnsetArrayAccess()
  475. {
  476. $entity = $this->getMock('\Cake\ORM\Entity', ['unsetProperty']);
  477. $entity->expects($this->at(0))
  478. ->method('unsetProperty')
  479. ->with('foo');
  480. unset($entity['foo']);
  481. }
  482. /**
  483. * Tests that the method cache will only report the methods for the called class,
  484. * this is, calling methods defined in another entity will not cause a fatal error
  485. * when trying to call directly an inexistent method in another class
  486. *
  487. * @return void
  488. */
  489. public function testMethodCache()
  490. {
  491. $entity = $this->getMock('\Cake\ORM\Entity', ['_setFoo', '_getBar']);
  492. $entity2 = $this->getMock('\Cake\ORM\Entity', ['_setBar']);
  493. $entity->expects($this->once())->method('_setFoo');
  494. $entity->expects($this->once())->method('_getBar');
  495. $entity2->expects($this->once())->method('_setBar');
  496. $entity->set('foo', 1);
  497. $entity->get('bar');
  498. $entity2->set('bar', 1);
  499. }
  500. /**
  501. * Tests that long properties in the entity are inflected correctly
  502. *
  503. * @return void
  504. */
  505. public function testSetGetLongProperyNames()
  506. {
  507. $entity = $this->getMock('\Cake\ORM\Entity', ['_getVeryLongProperty', '_setVeryLongProperty']);
  508. $entity->expects($this->once())->method('_getVeryLongProperty');
  509. $entity->expects($this->once())->method('_setVeryLongProperty');
  510. $entity->get('very_long_property');
  511. $entity->set('very_long_property', 1);
  512. }
  513. /**
  514. * Tests serializing an entity as json
  515. *
  516. * @return void
  517. */
  518. public function testJsonSerialize()
  519. {
  520. $data = ['name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  521. $entity = new Entity($data);
  522. $this->assertEquals(json_encode($data), json_encode($entity));
  523. }
  524. /**
  525. * Tests the extract method
  526. *
  527. * @return void
  528. */
  529. public function testExtract()
  530. {
  531. $entity = new Entity([
  532. 'id' => 1,
  533. 'title' => 'Foo',
  534. 'author_id' => 3
  535. ]);
  536. $expected = ['author_id' => 3, 'title' => 'Foo', ];
  537. $this->assertEquals($expected, $entity->extract(['author_id', 'title']));
  538. $expected = ['id' => 1];
  539. $this->assertEquals($expected, $entity->extract(['id']));
  540. $expected = [];
  541. $this->assertEquals($expected, $entity->extract([]));
  542. $expected = ['id' => 1, 'crazyness' => null];
  543. $this->assertEquals($expected, $entity->extract(['id', 'crazyness']));
  544. }
  545. /**
  546. * Tests dirty() method on a newly created object
  547. *
  548. * @return void
  549. */
  550. public function testDirty()
  551. {
  552. $entity = new Entity([
  553. 'id' => 1,
  554. 'title' => 'Foo',
  555. 'author_id' => 3
  556. ]);
  557. $this->assertTrue($entity->dirty('id'));
  558. $this->assertTrue($entity->dirty('title'));
  559. $this->assertTrue($entity->dirty('author_id'));
  560. $this->assertTrue($entity->dirty());
  561. $entity->dirty('id', false);
  562. $this->assertFalse($entity->dirty('id'));
  563. $this->assertTrue($entity->dirty('title'));
  564. $entity->dirty('title', false);
  565. $this->assertFalse($entity->dirty('title'));
  566. $this->assertTrue($entity->dirty());
  567. $entity->dirty('author_id', false);
  568. $this->assertFalse($entity->dirty());
  569. }
  570. /**
  571. * Tests dirty() when altering properties values and adding new ones
  572. *
  573. * @return void
  574. */
  575. public function testDirtyChangingProperties()
  576. {
  577. $entity = new Entity([
  578. 'title' => 'Foo',
  579. ]);
  580. $entity->dirty('title', false);
  581. $this->assertFalse($entity->dirty('title'));
  582. $entity->set('title', 'Foo');
  583. $this->assertTrue($entity->dirty('title'));
  584. $entity->set('title', 'Foo');
  585. $this->assertTrue($entity->dirty('title'));
  586. $entity->set('something', 'else');
  587. $this->assertTrue($entity->dirty('something'));
  588. }
  589. /**
  590. * Tests extract only dirty properties
  591. *
  592. * @return void
  593. */
  594. public function testExtractDirty()
  595. {
  596. $entity = new Entity([
  597. 'id' => 1,
  598. 'title' => 'Foo',
  599. 'author_id' => 3
  600. ]);
  601. $entity->dirty('id', false);
  602. $entity->dirty('title', false);
  603. $expected = ['author_id' => 3];
  604. $result = $entity->extract(['id', 'title', 'author_id'], true);
  605. $this->assertEquals($expected, $result);
  606. }
  607. /**
  608. * Tests the clean method
  609. *
  610. * @return void
  611. */
  612. public function testClean()
  613. {
  614. $entity = new Entity([
  615. 'id' => 1,
  616. 'title' => 'Foo',
  617. 'author_id' => 3
  618. ]);
  619. $this->assertTrue($entity->dirty('id'));
  620. $this->assertTrue($entity->dirty('title'));
  621. $this->assertTrue($entity->dirty('author_id'));
  622. $entity->clean();
  623. $this->assertFalse($entity->dirty('id'));
  624. $this->assertFalse($entity->dirty('title'));
  625. $this->assertFalse($entity->dirty('author_id'));
  626. }
  627. /**
  628. * Tests the isNew method
  629. *
  630. * @return void
  631. */
  632. public function testIsNew()
  633. {
  634. $data = [
  635. 'id' => 1,
  636. 'title' => 'Foo',
  637. 'author_id' => 3
  638. ];
  639. $entity = new Entity($data);
  640. $this->assertTrue($entity->isNew());
  641. $entity->isNew(true);
  642. $this->assertTrue($entity->isNew());
  643. $entity->isNew('derpy');
  644. $this->assertTrue($entity->isNew());
  645. $entity->isNew(false);
  646. $this->assertFalse($entity->isNew());
  647. }
  648. /**
  649. * Tests the constructor when passing the markClean option
  650. *
  651. * @return void
  652. */
  653. public function testConstructorWithClean()
  654. {
  655. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  656. ->setMethods(['clean'])
  657. ->disableOriginalConstructor()
  658. ->getMock();
  659. $entity->expects($this->never())->method('clean');
  660. $entity->__construct(['a' => 'b', 'c' => 'd']);
  661. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  662. ->setMethods(['clean'])
  663. ->disableOriginalConstructor()
  664. ->getMock();
  665. $entity->expects($this->once())->method('clean');
  666. $entity->__construct(['a' => 'b', 'c' => 'd'], ['markClean' => true]);
  667. }
  668. /**
  669. * Tests the constructor when passing the markClean option
  670. *
  671. * @return void
  672. */
  673. public function testConstructorWithMarkNew()
  674. {
  675. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  676. ->setMethods(['isNew'])
  677. ->disableOriginalConstructor()
  678. ->getMock();
  679. $entity->expects($this->never())->method('clean');
  680. $entity->__construct(['a' => 'b', 'c' => 'd']);
  681. $entity = $this->getMockBuilder('\Cake\ORM\Entity')
  682. ->setMethods(['isNew'])
  683. ->disableOriginalConstructor()
  684. ->getMock();
  685. $entity->expects($this->once())->method('isNew');
  686. $entity->__construct(['a' => 'b', 'c' => 'd'], ['markNew' => true]);
  687. }
  688. /**
  689. * Test toArray method.
  690. *
  691. * @return void
  692. */
  693. public function testToArray()
  694. {
  695. $data = ['name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  696. $entity = new Entity($data);
  697. $this->assertEquals($data, $entity->toArray());
  698. }
  699. /**
  700. * Test toArray recursive.
  701. *
  702. * @return void
  703. */
  704. public function testToArrayRecursive()
  705. {
  706. $data = ['id' => 1, 'name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  707. $user = new Extending($data);
  708. $comments = [
  709. new NonExtending(['user_id' => 1, 'body' => 'Comment 1']),
  710. new NonExtending(['user_id' => 1, 'body' => 'Comment 2']),
  711. ];
  712. $user->comments = $comments;
  713. $user->profile = new Entity(['email' => 'mark@example.com']);
  714. $expected = [
  715. 'id' => 1,
  716. 'name' => 'James',
  717. 'age' => 20,
  718. 'phones' => ['123', '457'],
  719. 'profile' => ['email' => 'mark@example.com'],
  720. 'comments' => [
  721. ['user_id' => 1, 'body' => 'Comment 1'],
  722. ['user_id' => 1, 'body' => 'Comment 2'],
  723. ]
  724. ];
  725. $this->assertEquals($expected, $user->toArray());
  726. }
  727. /**
  728. * Test that get accessors are called when converting to arrays.
  729. *
  730. * @return void
  731. */
  732. public function testToArrayWithAccessor()
  733. {
  734. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  735. $entity->accessible('*', true);
  736. $entity->set(['name' => 'Mark', 'email' => 'mark@example.com']);
  737. $entity->expects($this->any())
  738. ->method('_getName')
  739. ->will($this->returnValue('Jose'));
  740. $expected = ['name' => 'Jose', 'email' => 'mark@example.com'];
  741. $this->assertEquals($expected, $entity->toArray());
  742. }
  743. /**
  744. * Test that toArray respects hidden properties.
  745. *
  746. * @return void
  747. */
  748. public function testToArrayHiddenProperties()
  749. {
  750. $data = ['secret' => 'sauce', 'name' => 'mark', 'id' => 1];
  751. $entity = new Entity($data);
  752. $entity->hiddenProperties(['secret']);
  753. $this->assertEquals(['name' => 'mark', 'id' => 1], $entity->toArray());
  754. }
  755. /**
  756. * Test toArray includes 'virtual' properties.
  757. *
  758. * @return void
  759. */
  760. public function testToArrayVirtualProperties()
  761. {
  762. $entity = $this->getMock('\Cake\ORM\Entity', ['_getName']);
  763. $entity->accessible('*', true);
  764. $entity->expects($this->any())
  765. ->method('_getName')
  766. ->will($this->returnValue('Jose'));
  767. $entity->set(['email' => 'mark@example.com']);
  768. $entity->virtualProperties(['name']);
  769. $expected = ['name' => 'Jose', 'email' => 'mark@example.com'];
  770. $this->assertEquals($expected, $entity->toArray());
  771. $this->assertEquals(['name'], $entity->virtualProperties());
  772. $entity->hiddenProperties(['name']);
  773. $expected = ['email' => 'mark@example.com'];
  774. $this->assertEquals($expected, $entity->toArray());
  775. $this->assertEquals(['name'], $entity->hiddenProperties());
  776. }
  777. /**
  778. * Tests the errors method
  779. *
  780. * @return void
  781. */
  782. public function testErrors()
  783. {
  784. $entity = new Entity;
  785. $this->assertEmpty($entity->errors());
  786. $this->assertSame($entity, $entity->errors('foo', 'bar'));
  787. $this->assertEquals(['bar'], $entity->errors('foo'));
  788. $this->assertEquals([], $entity->errors('boo'));
  789. $entity['boo'] = [
  790. 'someting' => 'stupid',
  791. 'and' => false
  792. ];
  793. $this->assertEquals([], $entity->errors('boo'));
  794. $entity->errors('foo', 'other error');
  795. $this->assertEquals(['bar', 'other error'], $entity->errors('foo'));
  796. $entity->errors('bar', ['something', 'bad']);
  797. $this->assertEquals(['something', 'bad'], $entity->errors('bar'));
  798. $expected = ['foo' => ['bar', 'other error'], 'bar' => ['something', 'bad']];
  799. $this->assertEquals($expected, $entity->errors());
  800. $errors = ['foo' => ['something'], 'bar' => 'else', 'baz' => ['error']];
  801. $this->assertSame($entity, $entity->errors($errors, null, true));
  802. $errors['bar'] = ['else'];
  803. $this->assertEquals($errors, $entity->errors());
  804. }
  805. /**
  806. * Tests that it is possible to get errors for nested entities
  807. *
  808. * @return void
  809. */
  810. public function testErrorsDeep()
  811. {
  812. $user = new Entity();
  813. $owner = new NonExtending();
  814. $author = new Extending([
  815. 'foo' => 'bar',
  816. 'thing' => 'baz',
  817. 'user' => $user,
  818. 'owner' => $owner
  819. ]);
  820. $author->errors('thing', ['this is a mistake']);
  821. $user->errors(['a' => ['error1'], 'b' => ['error2']]);
  822. $owner->errors(['c' => ['error3'], 'd' => ['error4']]);
  823. $expected = ['a' => ['error1'], 'b' => ['error2']];
  824. $this->assertEquals($expected, $author->errors('user'));
  825. $expected = ['c' => ['error3'], 'd' => ['error4']];
  826. $this->assertEquals($expected, $author->errors('owner'));
  827. $author->set('multiple', [$user, $owner]);
  828. $expected = [
  829. ['a' => ['error1'], 'b' => ['error2']],
  830. ['c' => ['error3'], 'd' => ['error4']]
  831. ];
  832. $this->assertEquals($expected, $author->errors('multiple'));
  833. $expected = [
  834. 'thing' => $author->errors('thing'),
  835. 'user' => $author->errors('user'),
  836. 'owner' => $author->errors('owner'),
  837. 'multiple' => $author->errors('multiple')
  838. ];
  839. $this->assertEquals($expected, $author->errors());
  840. }
  841. /**
  842. * Test that errors can be read with a path.
  843. *
  844. * @return void
  845. */
  846. public function testErrorPathReading()
  847. {
  848. $assoc = new Entity();
  849. $assoc2 = new NonExtending();
  850. $entity = new Extending([
  851. 'field' => 'value',
  852. 'one' => $assoc,
  853. 'many' => [$assoc2]
  854. ]);
  855. $entity->errors('wrong', 'Bad stuff');
  856. $assoc->errors('nope', 'Terrible things');
  857. $assoc2->errors('nope', 'Terrible things');
  858. $this->assertEquals(['Bad stuff'], $entity->errors('wrong'));
  859. $this->assertEquals(['Terrible things'], $entity->errors('many.0.nope'));
  860. $this->assertEquals(['Terrible things'], $entity->errors('one.nope'));
  861. $this->assertEquals(['nope' => ['Terrible things']], $entity->errors('one'));
  862. $this->assertEquals([0 => ['nope' => ['Terrible things']]], $entity->errors('many'));
  863. $this->assertEquals(['nope' => ['Terrible things']], $entity->errors('many.0'));
  864. $this->assertEquals([], $entity->errors('many.0.mistake'));
  865. $this->assertEquals([], $entity->errors('one.mistake'));
  866. $this->assertEquals([], $entity->errors('one.1.mistake'));
  867. $this->assertEquals([], $entity->errors('many.1.nope'));
  868. }
  869. /**
  870. * Tests that changing the value of a property will remove errors
  871. * stored for it
  872. *
  873. * @return void
  874. */
  875. public function testDirtyRemovesError()
  876. {
  877. $entity = new Entity(['a' => 'b']);
  878. $entity->errors('a', 'is not good');
  879. $entity->set('a', 'c');
  880. $this->assertEmpty($entity->errors('a'));
  881. $entity->errors('a', 'is not good');
  882. $entity->dirty('a', true);
  883. $this->assertEmpty($entity->errors('a'));
  884. }
  885. /**
  886. * Tests that marking an entity as clean will remove errors too
  887. *
  888. * @return void
  889. */
  890. public function testCleanRemovesErrors()
  891. {
  892. $entity = new Entity(['a' => 'b']);
  893. $entity->errors('a', 'is not good');
  894. $entity->clean();
  895. $this->assertEmpty($entity->errors());
  896. }
  897. /**
  898. * Tests accessible() method as a getter and setter
  899. *
  900. * @return void
  901. */
  902. public function testAccessible()
  903. {
  904. $entity = new Entity;
  905. $entity->accessible('*', false);
  906. $this->assertFalse($entity->accessible('foo'));
  907. $this->assertFalse($entity->accessible('bar'));
  908. $this->assertSame($entity, $entity->accessible('foo', true));
  909. $this->assertTrue($entity->accessible('foo'));
  910. $this->assertFalse($entity->accessible('bar'));
  911. $this->assertSame($entity, $entity->accessible('bar', true));
  912. $this->assertTrue($entity->accessible('foo'));
  913. $this->assertTrue($entity->accessible('bar'));
  914. $this->assertSame($entity, $entity->accessible('foo', false));
  915. $this->assertFalse($entity->accessible('foo'));
  916. $this->assertTrue($entity->accessible('bar'));
  917. $this->assertSame($entity, $entity->accessible('bar', false));
  918. $this->assertFalse($entity->accessible('foo'));
  919. $this->assertFalse($entity->accessible('bar'));
  920. }
  921. /**
  922. * Tests that an array can be used to set
  923. *
  924. * @return void
  925. */
  926. public function testAccessibleAsArray()
  927. {
  928. $entity = new Entity;
  929. $entity->accessible(['foo', 'bar', 'baz'], true);
  930. $this->assertTrue($entity->accessible('foo'));
  931. $this->assertTrue($entity->accessible('bar'));
  932. $this->assertTrue($entity->accessible('baz'));
  933. $entity->accessible('foo', false);
  934. $this->assertFalse($entity->accessible('foo'));
  935. $this->assertTrue($entity->accessible('bar'));
  936. $this->assertTrue($entity->accessible('baz'));
  937. $entity->accessible(['foo', 'bar', 'baz'], false);
  938. $this->assertFalse($entity->accessible('foo'));
  939. $this->assertFalse($entity->accessible('bar'));
  940. $this->assertFalse($entity->accessible('baz'));
  941. }
  942. /**
  943. * Tests that a wildcard can be used for setting accesible properties
  944. *
  945. * @return void
  946. */
  947. public function testAccessibleWildcard()
  948. {
  949. $entity = new Entity;
  950. $entity->accessible(['foo', 'bar', 'baz'], true);
  951. $this->assertTrue($entity->accessible('foo'));
  952. $this->assertTrue($entity->accessible('bar'));
  953. $this->assertTrue($entity->accessible('baz'));
  954. $entity->accessible('*', false);
  955. $this->assertFalse($entity->accessible('foo'));
  956. $this->assertFalse($entity->accessible('bar'));
  957. $this->assertFalse($entity->accessible('baz'));
  958. $this->assertFalse($entity->accessible('newOne'));
  959. $entity->accessible('*', true);
  960. $this->assertTrue($entity->accessible('foo'));
  961. $this->assertTrue($entity->accessible('bar'));
  962. $this->assertTrue($entity->accessible('baz'));
  963. $this->assertTrue($entity->accessible('newOne2'));
  964. }
  965. /**
  966. * Tests that only accessible properties can be set
  967. *
  968. * @return void
  969. */
  970. public function testSetWithAccessible()
  971. {
  972. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  973. $options = ['guard' => true];
  974. $entity->accessible('*', false);
  975. $entity->accessible('foo', true);
  976. $entity->set('bar', 3, $options);
  977. $entity->set('foo', 4, $options);
  978. $this->assertEquals(2, $entity->get('bar'));
  979. $this->assertEquals(4, $entity->get('foo'));
  980. $entity->accessible('bar', true);
  981. $entity->set('bar', 3, $options);
  982. $this->assertEquals(3, $entity->get('bar'));
  983. }
  984. /**
  985. * Tests that only accessible properties can be set
  986. *
  987. * @return void
  988. */
  989. public function testSetWithAccessibleWithArray()
  990. {
  991. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  992. $options = ['guard' => true];
  993. $entity->accessible('*', false);
  994. $entity->accessible('foo', true);
  995. $entity->set(['bar' => 3, 'foo' => 4], $options);
  996. $this->assertEquals(2, $entity->get('bar'));
  997. $this->assertEquals(4, $entity->get('foo'));
  998. $entity->accessible('bar', true);
  999. $entity->set(['bar' => 3, 'foo' => 5], $options);
  1000. $this->assertEquals(3, $entity->get('bar'));
  1001. $this->assertEquals(5, $entity->get('foo'));
  1002. }
  1003. /**
  1004. * Test that accessible() and single property setting works.
  1005. *
  1006. * @return void
  1007. */
  1008. public function testSetWithAccessibleSingleProperty()
  1009. {
  1010. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1011. $entity->accessible('*', false);
  1012. $entity->accessible('title', true);
  1013. $entity->set(['title' => 'test', 'body' => 'Nope']);
  1014. $this->assertEquals('test', $entity->title);
  1015. $this->assertNull($entity->body);
  1016. $entity->body = 'Yep';
  1017. $this->assertEquals('Yep', $entity->body, 'Single set should bypass guards.');
  1018. $entity->set('body', 'Yes');
  1019. $this->assertEquals('Yes', $entity->body, 'Single set should bypass guards.');
  1020. }
  1021. /**
  1022. * Tests the entity's __toString method
  1023. *
  1024. * @return void
  1025. */
  1026. public function testToString()
  1027. {
  1028. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1029. $this->assertEquals(json_encode($entity, JSON_PRETTY_PRINT), (string)$entity);
  1030. }
  1031. /**
  1032. * Tests __debugInfo
  1033. *
  1034. * @return void
  1035. */
  1036. public function testDebugInfo()
  1037. {
  1038. $entity = new Entity(['foo' => 'bar'], ['markClean' => true]);
  1039. $entity->accessible('name', true);
  1040. $entity->virtualProperties(['baz']);
  1041. $entity->dirty('foo', true);
  1042. $entity->errors('foo', ['An error']);
  1043. $entity->source('foos');
  1044. $result = $entity->__debugInfo();
  1045. $expected = [
  1046. 'new' => true,
  1047. 'accessible' => ['*' => true, 'name' => true],
  1048. 'properties' => ['foo' => 'bar'],
  1049. 'dirty' => ['foo' => true],
  1050. 'original' => [],
  1051. 'virtual' => ['baz'],
  1052. 'errors' => ['foo' => ['An error']],
  1053. 'repository' => 'foos'
  1054. ];
  1055. $this->assertSame($expected, $result);
  1056. }
  1057. /**
  1058. * Tests the source method
  1059. *
  1060. * @return void
  1061. */
  1062. public function testSource()
  1063. {
  1064. $entity = new Entity;
  1065. $this->assertNull($entity->source());
  1066. $entity->source('foos');
  1067. $this->assertEquals('foos', $entity->source());
  1068. }
  1069. /**
  1070. * Provides empty values
  1071. *
  1072. * @return void
  1073. */
  1074. public function emptyNamesProvider()
  1075. {
  1076. return [[''], [null], [false]];
  1077. }
  1078. /**
  1079. * Tests that trying to get an empty propery name throws exception
  1080. *
  1081. * @dataProvider emptyNamesProvider
  1082. * @expectedException \InvalidArgumentException
  1083. * @return void
  1084. */
  1085. public function testEmptyProperties($property)
  1086. {
  1087. $entity = new Entity();
  1088. $entity->get($property);
  1089. }
  1090. /**
  1091. * Tests that setitng an empty property name does nothing
  1092. *
  1093. * @expectedException \InvalidArgumentException
  1094. * @dataProvider emptyNamesProvider
  1095. * @return void
  1096. */
  1097. public function testSetEmptyPropertyName($property)
  1098. {
  1099. $entity = new Entity();
  1100. $entity->set($property, 'bar');
  1101. }
  1102. /**
  1103. * Provides empty values
  1104. *
  1105. * @return void
  1106. */
  1107. public function testIsDirtyFromClone()
  1108. {
  1109. $entity = new Entity(
  1110. ['a' => 1, 'b' => 2],
  1111. ['markNew' => false, 'markClean' => true]
  1112. );
  1113. $this->assertFalse($entity->isNew());
  1114. $this->assertFalse($entity->dirty());
  1115. $cloned = clone $entity;
  1116. $cloned->isNew(true);
  1117. $this->assertTrue($cloned->dirty());
  1118. $this->assertTrue($cloned->dirty('a'));
  1119. $this->assertTrue($cloned->dirty('b'));
  1120. }
  1121. }