EntityTest.php 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\ORM;
  17. use Cake\ORM\Entity;
  18. use Cake\TestSuite\TestCase;
  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->assertSame('bar', $entity->foo);
  37. $this->assertSame('bar', $entity->getOriginal('foo'));
  38. $entity->set('foo', 'baz');
  39. $this->assertSame('baz', $entity->foo);
  40. $this->assertSame('bar', $entity->getOriginal('foo'));
  41. $entity->set('id', 1);
  42. $this->assertSame(1, $entity->id);
  43. $this->assertSame(1, $entity->getOriginal('id'));
  44. $this->assertSame('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->setAccess('*', true);
  55. $entity->set(['foo' => 'bar', 'id' => 1]);
  56. $this->assertSame('bar', $entity->foo);
  57. $this->assertSame(1, $entity->id);
  58. $entity->set(['foo' => 'baz', 'id' => 2, 'thing' => 3]);
  59. $this->assertSame('baz', $entity->foo);
  60. $this->assertSame(2, $entity->id);
  61. $this->assertSame(3, $entity->thing);
  62. $this->assertSame('bar', $entity->getOriginal('foo'));
  63. $this->assertSame(1, $entity->getOriginal('id'));
  64. $entity->set(['foo', 'bar']);
  65. $this->assertSame('foo', $entity->get('0'));
  66. $this->assertSame('bar', $entity->get('1'));
  67. $entity->set(['sample']);
  68. $this->assertSame('sample', $entity->get('0'));
  69. }
  70. /**
  71. * Test that getOriginal() retains falsey values.
  72. *
  73. * @return void
  74. */
  75. public function testGetOriginal()
  76. {
  77. $entity = new Entity(
  78. ['false' => false, 'null' => null, 'zero' => 0, 'empty' => ''],
  79. ['markNew' => true]
  80. );
  81. $this->assertNull($entity->getOriginal('null'));
  82. $this->assertFalse($entity->getOriginal('false'));
  83. $this->assertSame(0, $entity->getOriginal('zero'));
  84. $this->assertSame('', $entity->getOriginal('empty'));
  85. $entity->set(['false' => 'y', 'null' => 'y', 'zero' => 'y', 'empty' => '']);
  86. $this->assertNull($entity->getOriginal('null'));
  87. $this->assertFalse($entity->getOriginal('false'));
  88. $this->assertSame(0, $entity->getOriginal('zero'));
  89. $this->assertSame('', $entity->getOriginal('empty'));
  90. }
  91. /**
  92. * Test extractOriginal()
  93. *
  94. * @return void
  95. */
  96. public function testExtractOriginal()
  97. {
  98. $entity = new Entity([
  99. 'id' => 1,
  100. 'title' => 'original',
  101. 'body' => 'no',
  102. 'null' => null,
  103. ], ['markNew' => true]);
  104. $entity->set('body', 'updated body');
  105. $result = $entity->extractOriginal(['id', 'title', 'body', 'null']);
  106. $expected = [
  107. 'id' => 1,
  108. 'title' => 'original',
  109. 'body' => 'no',
  110. 'null' => null,
  111. ];
  112. $this->assertEquals($expected, $result);
  113. $result = $entity->extractOriginalChanged(['id', 'title', 'body', 'null']);
  114. $expected = [
  115. 'body' => 'no',
  116. ];
  117. $this->assertEquals($expected, $result);
  118. $entity->set('null', 'not null');
  119. $result = $entity->extractOriginalChanged(['id', 'title', 'body', 'null']);
  120. $expected = [
  121. 'null' => null,
  122. 'body' => 'no',
  123. ];
  124. $this->assertEquals($expected, $result);
  125. }
  126. /**
  127. * Test that all original values are returned properly
  128. *
  129. * @return void
  130. */
  131. public function testExtractOriginalValues()
  132. {
  133. $entity = new Entity([
  134. 'id' => 1,
  135. 'title' => 'original',
  136. 'body' => 'no',
  137. 'null' => null,
  138. ], ['markNew' => true]);
  139. $entity->set('body', 'updated body');
  140. $result = $entity->getOriginalValues();
  141. $expected = [
  142. 'id' => 1,
  143. 'title' => 'original',
  144. 'body' => 'no',
  145. 'null' => null,
  146. ];
  147. $this->assertEquals($expected, $result);
  148. }
  149. /**
  150. * Tests setting a single property using a setter function
  151. *
  152. * @return void
  153. */
  154. public function testSetOneParamWithSetter()
  155. {
  156. $entity = $this->getMockBuilder(Entity::class)
  157. ->addMethods(['_setName'])
  158. ->getMock();
  159. $entity->expects($this->once())->method('_setName')
  160. ->with('Jones')
  161. ->will($this->returnCallback(function ($name) {
  162. $this->assertSame('Jones', $name);
  163. return 'Dr. ' . $name;
  164. }));
  165. $entity->set('name', 'Jones');
  166. $this->assertSame('Dr. Jones', $entity->name);
  167. }
  168. /**
  169. * Tests setting multiple properties using a setter function
  170. *
  171. * @return void
  172. */
  173. public function testMultipleWithSetter()
  174. {
  175. $entity = $this->getMockBuilder(Entity::class)
  176. ->addMethods(['_setName', '_setStuff'])
  177. ->getMock();
  178. $entity->setAccess('*', true);
  179. $entity->expects($this->once())->method('_setName')
  180. ->with('Jones')
  181. ->will($this->returnCallback(function ($name) {
  182. $this->assertSame('Jones', $name);
  183. return 'Dr. ' . $name;
  184. }));
  185. $entity->expects($this->once())->method('_setStuff')
  186. ->with(['a', 'b'])
  187. ->will($this->returnCallback(function ($stuff) {
  188. $this->assertEquals(['a', 'b'], $stuff);
  189. return ['c', 'd'];
  190. }));
  191. $entity->set(['name' => 'Jones', 'stuff' => ['a', 'b']]);
  192. $this->assertSame('Dr. Jones', $entity->name);
  193. $this->assertEquals(['c', 'd'], $entity->stuff);
  194. }
  195. /**
  196. * Tests that it is possible to bypass the setters
  197. *
  198. * @return void
  199. */
  200. public function testBypassSetters()
  201. {
  202. $entity = $this->getMockBuilder(Entity::class)
  203. ->addMethods(['_setName', '_setStuff'])
  204. ->getMock();
  205. $entity->setAccess('*', true);
  206. $entity->expects($this->never())->method('_setName');
  207. $entity->expects($this->never())->method('_setStuff');
  208. $entity->set('name', 'Jones', ['setter' => false]);
  209. $this->assertSame('Jones', $entity->name);
  210. $entity->set('stuff', 'Thing', ['setter' => false]);
  211. $this->assertSame('Thing', $entity->stuff);
  212. $entity->set(['name' => 'foo', 'stuff' => 'bar'], ['setter' => false]);
  213. $this->assertSame('bar', $entity->stuff);
  214. }
  215. /**
  216. * Tests that the constructor will set initial properties
  217. *
  218. * @return void
  219. */
  220. public function testConstructor()
  221. {
  222. $entity = $this->getMockBuilder(Entity::class)
  223. ->onlyMethods(['set'])
  224. ->disableOriginalConstructor()
  225. ->getMock();
  226. $entity->expects($this->exactly(2))
  227. ->method('set')
  228. ->withConsecutive(
  229. [
  230. ['a' => 'b', 'c' => 'd'], ['setter' => true, 'guard' => false],
  231. ],
  232. [['foo' => 'bar'], ['setter' => false, 'guard' => false]]
  233. );
  234. $entity->__construct(['a' => 'b', 'c' => 'd']);
  235. $entity->__construct(['foo' => 'bar'], ['useSetters' => false]);
  236. }
  237. /**
  238. * Tests that the constructor will set initial properties and pass the guard
  239. * option along
  240. *
  241. * @return void
  242. */
  243. public function testConstructorWithGuard()
  244. {
  245. $entity = $this->getMockBuilder(Entity::class)
  246. ->onlyMethods(['set'])
  247. ->disableOriginalConstructor()
  248. ->getMock();
  249. $entity->expects($this->once())
  250. ->method('set')
  251. ->with(['foo' => 'bar'], ['setter' => true, 'guard' => true]);
  252. $entity->__construct(['foo' => 'bar'], ['guard' => true]);
  253. }
  254. /**
  255. * Tests getting properties with no custom getters
  256. *
  257. * @return void
  258. */
  259. public function testGetNoGetters()
  260. {
  261. $entity = new Entity(['id' => 1, 'foo' => 'bar']);
  262. $this->assertSame(1, $entity->get('id'));
  263. $this->assertSame('bar', $entity->get('foo'));
  264. }
  265. /**
  266. * Tests get with custom getter
  267. *
  268. * @return void
  269. */
  270. public function testGetCustomGetters()
  271. {
  272. $entity = $this->getMockBuilder(Entity::class)
  273. ->addMethods(['_getName'])
  274. ->getMock();
  275. $entity->expects($this->any())
  276. ->method('_getName')
  277. ->with('Jones')
  278. ->will($this->returnCallback(function ($name) {
  279. return 'Dr. ' . $name;
  280. }));
  281. $entity->set('name', 'Jones');
  282. $this->assertSame('Dr. Jones', $entity->get('name'));
  283. $this->assertSame('Dr. Jones', $entity->get('name'));
  284. }
  285. /**
  286. * Tests get with custom getter
  287. *
  288. * @return void
  289. */
  290. public function testGetCustomGettersAfterSet()
  291. {
  292. $entity = $this->getMockBuilder(Entity::class)
  293. ->addMethods(['_getName'])
  294. ->getMock();
  295. $entity->expects($this->any())
  296. ->method('_getName')
  297. ->will($this->returnCallback(function ($name) {
  298. return 'Dr. ' . $name;
  299. }));
  300. $entity->set('name', 'Jones');
  301. $this->assertSame('Dr. Jones', $entity->get('name'));
  302. $this->assertSame('Dr. Jones', $entity->get('name'));
  303. $entity->set('name', 'Mark');
  304. $this->assertSame('Dr. Mark', $entity->get('name'));
  305. $this->assertSame('Dr. Mark', $entity->get('name'));
  306. }
  307. /**
  308. * Tests that the get cache is cleared by unsetProperty.
  309. *
  310. * @return void
  311. */
  312. public function testGetCacheClearedByUnset()
  313. {
  314. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  315. $entity = $this->getMockBuilder(Entity::class)
  316. ->addMethods(['_getName'])
  317. ->getMock();
  318. $entity->expects($this->any())->method('_getName')
  319. ->will($this->returnCallback(function ($name) {
  320. return 'Dr. ' . $name;
  321. }));
  322. $entity->set('name', 'Jones');
  323. $this->assertSame('Dr. Jones', $entity->get('name'));
  324. $entity->unset('name');
  325. $this->assertSame('Dr. ', $entity->get('name'));
  326. }
  327. /**
  328. * Test getting camelcased virtual fields.
  329. *
  330. * @return void
  331. */
  332. public function testGetCamelCasedProperties()
  333. {
  334. $entity = $this->getMockBuilder(Entity::class)
  335. ->addMethods(['_getListIdName'])
  336. ->getMock();
  337. $entity->expects($this->any())->method('_getListIdName')
  338. ->will($this->returnCallback(function ($name) {
  339. return 'A name';
  340. }));
  341. $entity->setVirtual(['ListIdName']);
  342. $this->assertSame('A name', $entity->list_id_name, 'underscored virtual field should be accessible');
  343. $this->assertSame('A name', $entity->listIdName, 'Camelbacked virtual field should be accessible');
  344. }
  345. /**
  346. * Test magic property setting with no custom setter
  347. *
  348. * @return void
  349. */
  350. public function testMagicSet()
  351. {
  352. $entity = new Entity();
  353. $entity->name = 'Jones';
  354. $this->assertSame('Jones', $entity->name);
  355. $entity->name = 'George';
  356. $this->assertSame('George', $entity->name);
  357. }
  358. /**
  359. * Tests magic set with custom setter function
  360. *
  361. * @return void
  362. */
  363. public function testMagicSetWithSetter()
  364. {
  365. $entity = $this->getMockBuilder(Entity::class)
  366. ->addMethods(['_setName'])
  367. ->getMock();
  368. $entity->expects($this->once())->method('_setName')
  369. ->with('Jones')
  370. ->will($this->returnCallback(function ($name) {
  371. $this->assertSame('Jones', $name);
  372. return 'Dr. ' . $name;
  373. }));
  374. $entity->name = 'Jones';
  375. $this->assertSame('Dr. Jones', $entity->name);
  376. }
  377. /**
  378. * Tests magic set with custom setter function using a Title cased property
  379. *
  380. * @return void
  381. */
  382. public function testMagicSetWithSetterTitleCase()
  383. {
  384. $entity = $this->getMockBuilder(Entity::class)
  385. ->addMethods(['_setName'])
  386. ->getMock();
  387. $entity->expects($this->once())
  388. ->method('_setName')
  389. ->with('Jones')
  390. ->will($this->returnCallback(function ($name) {
  391. $this->assertSame('Jones', $name);
  392. return 'Dr. ' . $name;
  393. }));
  394. $entity->Name = 'Jones';
  395. $this->assertSame('Dr. Jones', $entity->Name);
  396. }
  397. /**
  398. * Tests the magic getter with a custom getter function
  399. *
  400. * @return void
  401. */
  402. public function testMagicGetWithGetter()
  403. {
  404. $entity = $this->getMockBuilder(Entity::class)
  405. ->addMethods(['_getName'])
  406. ->getMock();
  407. $entity->expects($this->once())->method('_getName')
  408. ->with('Jones')
  409. ->will($this->returnCallback(function ($name) {
  410. $this->assertSame('Jones', $name);
  411. return 'Dr. ' . $name;
  412. }));
  413. $entity->set('name', 'Jones');
  414. $this->assertSame('Dr. Jones', $entity->name);
  415. }
  416. /**
  417. * Tests magic get with custom getter function using a Title cased property
  418. *
  419. * @return void
  420. */
  421. public function testMagicGetWithGetterTitleCase()
  422. {
  423. $entity = $this->getMockBuilder(Entity::class)
  424. ->addMethods(['_getName'])
  425. ->getMock();
  426. $entity->expects($this->once())
  427. ->method('_getName')
  428. ->with('Jones')
  429. ->will($this->returnCallback(function ($name) {
  430. $this->assertSame('Jones', $name);
  431. return 'Dr. ' . $name;
  432. }));
  433. $entity->set('Name', 'Jones');
  434. $this->assertSame('Dr. Jones', $entity->Name);
  435. }
  436. /**
  437. * Test indirectly modifying internal properties
  438. *
  439. * @return void
  440. */
  441. public function testIndirectModification()
  442. {
  443. $entity = new Entity();
  444. $entity->things = ['a', 'b'];
  445. $entity->things[] = 'c';
  446. $this->assertEquals(['a', 'b', 'c'], $entity->things);
  447. }
  448. /**
  449. * Tests has() method
  450. *
  451. * @return void
  452. */
  453. public function testHas()
  454. {
  455. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  456. $this->assertTrue($entity->has('id'));
  457. $this->assertTrue($entity->has('name'));
  458. $this->assertFalse($entity->has('foo'));
  459. $this->assertFalse($entity->has('last_name'));
  460. $this->assertTrue($entity->has(['id']));
  461. $this->assertTrue($entity->has(['id', 'name']));
  462. $this->assertFalse($entity->has(['id', 'foo']));
  463. $this->assertFalse($entity->has(['id', 'nope']));
  464. $entity = $this->getMockBuilder(Entity::class)
  465. ->addMethods(['_getThings'])
  466. ->getMock();
  467. $entity->expects($this->once())->method('_getThings')
  468. ->will($this->returnValue(0));
  469. $this->assertTrue($entity->has('things'));
  470. }
  471. /**
  472. * Tests unsetProperty one property at a time
  473. *
  474. * @return void
  475. */
  476. public function testUnset()
  477. {
  478. $entity = new Entity(['id' => 1, 'name' => 'bar']);
  479. $entity->unset('id');
  480. $this->assertFalse($entity->has('id'));
  481. $this->assertTrue($entity->has('name'));
  482. $entity->unset('name');
  483. $this->assertFalse($entity->has('id'));
  484. }
  485. /**
  486. * Unsetting a property should not mark it as dirty.
  487. *
  488. * @return void
  489. */
  490. public function testUnsetMakesClean()
  491. {
  492. $entity = new Entity(['id' => 1, 'name' => 'bar']);
  493. $this->assertTrue($entity->isDirty('name'));
  494. $entity->unset('name');
  495. $this->assertFalse($entity->isDirty('name'), 'Removed properties are not dirty.');
  496. }
  497. /**
  498. * Tests unsetProperty with multiple properties
  499. *
  500. * @return void
  501. */
  502. public function testUnsetMultiple()
  503. {
  504. $entity = new Entity(['id' => 1, 'name' => 'bar', 'thing' => 2]);
  505. $entity->unset(['id', 'thing']);
  506. $this->assertFalse($entity->has('id'));
  507. $this->assertTrue($entity->has('name'));
  508. $this->assertFalse($entity->has('thing'));
  509. }
  510. /**
  511. * Tests the magic __isset() method
  512. *
  513. * @return void
  514. */
  515. public function testMagicIsset()
  516. {
  517. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  518. $this->assertTrue(isset($entity->id));
  519. $this->assertTrue(isset($entity->name));
  520. $this->assertFalse(isset($entity->foo));
  521. $this->assertFalse(isset($entity->thing));
  522. }
  523. /**
  524. * Tests the magic __unset() method
  525. *
  526. * @return void
  527. */
  528. public function testMagicUnset()
  529. {
  530. $entity = $this->getMockBuilder(Entity::class)
  531. ->onlyMethods(['unset'])
  532. ->getMock();
  533. $entity->expects($this->once())
  534. ->method('unset')
  535. ->with('foo');
  536. unset($entity->foo);
  537. }
  538. /**
  539. * Tests the deprecated unsetProperty() method
  540. *
  541. * @return void
  542. */
  543. public function testUnsetDeprecated()
  544. {
  545. $this->deprecated(function () {
  546. $entity = new Entity();
  547. $entity->foo = 'foo';
  548. $entity->unsetProperty('foo');
  549. $this->assertNull($entity->foo);
  550. });
  551. }
  552. /**
  553. * Tests isset with array access
  554. *
  555. * @return void
  556. */
  557. public function testIssetArrayAccess()
  558. {
  559. $entity = new Entity(['id' => 1, 'name' => 'Juan', 'foo' => null]);
  560. $this->assertArrayHasKey('id', $entity);
  561. $this->assertArrayHasKey('name', $entity);
  562. $this->assertArrayNotHasKey('foo', $entity);
  563. $this->assertArrayNotHasKey('thing', $entity);
  564. }
  565. /**
  566. * Tests get property with array access
  567. *
  568. * @return void
  569. */
  570. public function testGetArrayAccess()
  571. {
  572. $entity = $this->getMockBuilder(Entity::class)
  573. ->onlyMethods(['get'])
  574. ->getMock();
  575. $entity->expects($this->exactly(2))
  576. ->method('get')
  577. ->withConsecutive(['foo'], ['bar'])
  578. ->will($this->onConsecutiveCalls(
  579. $this->returnValue('worked'),
  580. $this->returnValue('worked too')
  581. ));
  582. $this->assertSame('worked', $entity['foo']);
  583. $this->assertSame('worked too', $entity['bar']);
  584. }
  585. /**
  586. * Tests set with array access
  587. *
  588. * @return void
  589. */
  590. public function testSetArrayAccess()
  591. {
  592. $entity = $this->getMockBuilder(Entity::class)
  593. ->onlyMethods(['set'])
  594. ->getMock();
  595. $entity->setAccess('*', true);
  596. $entity->expects($this->exactly(2))
  597. ->method('set')
  598. ->withConsecutive(['foo', 1], ['bar', 2])
  599. ->will($this->onConsecutiveCalls(
  600. $this->returnSelf(),
  601. $this->returnSelf()
  602. ));
  603. $entity['foo'] = 1;
  604. $entity['bar'] = 2;
  605. }
  606. /**
  607. * Tests unset with array access
  608. *
  609. * @return void
  610. */
  611. public function testUnsetArrayAccess()
  612. {
  613. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  614. $entity = $this->getMockBuilder(Entity::class)
  615. ->onlyMethods(['unset'])
  616. ->getMock();
  617. $entity->expects($this->once())
  618. ->method('unset')
  619. ->with('foo');
  620. unset($entity['foo']);
  621. }
  622. /**
  623. * Tests that the method cache will only report the methods for the called class,
  624. * this is, calling methods defined in another entity will not cause a fatal error
  625. * when trying to call directly an inexistent method in another class
  626. *
  627. * @return void
  628. */
  629. public function testMethodCache()
  630. {
  631. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  632. $entity = $this->getMockBuilder(Entity::class)
  633. ->addMethods(['_setFoo', '_getBar'])
  634. ->getMock();
  635. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity2 */
  636. $entity2 = $this->getMockBuilder(Entity::class)
  637. ->addMethods(['_setBar'])
  638. ->getMock();
  639. $entity->expects($this->once())->method('_setFoo');
  640. $entity->expects($this->once())->method('_getBar');
  641. $entity2->expects($this->once())->method('_setBar');
  642. $entity->set('foo', 1);
  643. $entity->get('bar');
  644. $entity2->set('bar', 1);
  645. }
  646. /**
  647. * Tests that long properties in the entity are inflected correctly
  648. *
  649. * @return void
  650. */
  651. public function testSetGetLongPropertyNames()
  652. {
  653. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  654. $entity = $this->getMockBuilder(Entity::class)
  655. ->addMethods(['_getVeryLongProperty', '_setVeryLongProperty'])
  656. ->getMock();
  657. $entity->expects($this->once())->method('_getVeryLongProperty');
  658. $entity->expects($this->once())->method('_setVeryLongProperty');
  659. $entity->get('very_long_property');
  660. $entity->set('very_long_property', 1);
  661. }
  662. /**
  663. * Tests serializing an entity as JSON
  664. *
  665. * @return void
  666. */
  667. public function testJsonSerialize()
  668. {
  669. $data = ['name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  670. $entity = new Entity($data);
  671. $this->assertEquals(json_encode($data), json_encode($entity));
  672. }
  673. /**
  674. * Tests serializing an entity as PHP
  675. *
  676. * @return void
  677. */
  678. public function testPhpSerialize()
  679. {
  680. $data = ['name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  681. $entity = new Entity($data);
  682. $copy = unserialize(serialize($entity));
  683. $this->assertInstanceOf(Entity::class, $copy);
  684. $this->assertEquals($data, $copy->toArray());
  685. }
  686. /**
  687. * Tests that jsonSerialize is called recursively for contained entities
  688. *
  689. * @return void
  690. */
  691. public function testJsonSerializeRecursive()
  692. {
  693. $phone = $this->getMockBuilder(Entity::class)
  694. ->onlyMethods(['jsonSerialize'])
  695. ->getMock();
  696. $phone->expects($this->once())->method('jsonSerialize')->will($this->returnValue(['something']));
  697. $data = ['name' => 'James', 'age' => 20, 'phone' => $phone];
  698. $entity = new Entity($data);
  699. $expected = ['name' => 'James', 'age' => 20, 'phone' => ['something']];
  700. $this->assertEquals(json_encode($expected), json_encode($entity));
  701. }
  702. /**
  703. * Tests the extract method
  704. *
  705. * @return void
  706. */
  707. public function testExtract()
  708. {
  709. $entity = new Entity([
  710. 'id' => 1,
  711. 'title' => 'Foo',
  712. 'author_id' => 3,
  713. ]);
  714. $expected = ['author_id' => 3, 'title' => 'Foo',];
  715. $this->assertEquals($expected, $entity->extract(['author_id', 'title']));
  716. $expected = ['id' => 1];
  717. $this->assertEquals($expected, $entity->extract(['id']));
  718. $expected = [];
  719. $this->assertEquals($expected, $entity->extract([]));
  720. $expected = ['id' => 1, 'craziness' => null];
  721. $this->assertEquals($expected, $entity->extract(['id', 'craziness']));
  722. }
  723. /**
  724. * Tests isDirty() method on a newly created object
  725. *
  726. * @return void
  727. */
  728. public function testIsDirty()
  729. {
  730. $entity = new Entity([
  731. 'id' => 1,
  732. 'title' => 'Foo',
  733. 'author_id' => 3,
  734. ]);
  735. $this->assertTrue($entity->isDirty('id'));
  736. $this->assertTrue($entity->isDirty('title'));
  737. $this->assertTrue($entity->isDirty('author_id'));
  738. $this->assertTrue($entity->isDirty());
  739. $entity->setDirty('id', false);
  740. $this->assertFalse($entity->isDirty('id'));
  741. $this->assertTrue($entity->isDirty('title'));
  742. $entity->setDirty('title', false);
  743. $this->assertFalse($entity->isDirty('title'));
  744. $this->assertTrue($entity->isDirty(), 'should be dirty, one field left');
  745. $entity->setDirty('author_id', false);
  746. $this->assertFalse($entity->isDirty(), 'all fields are clean.');
  747. }
  748. /**
  749. * Test setDirty().
  750. *
  751. * @return void
  752. */
  753. public function testSetDirty()
  754. {
  755. $entity = new Entity([
  756. 'id' => 1,
  757. 'title' => 'Foo',
  758. 'author_id' => 3,
  759. ], ['markClean' => true]);
  760. $this->assertFalse($entity->isDirty());
  761. $this->assertSame($entity, $entity->setDirty('title'));
  762. $this->assertSame($entity, $entity->setDirty('id', false));
  763. $entity->setErrors(['title' => ['badness']]);
  764. $entity->setDirty('title', true);
  765. $this->assertEmpty($entity->getErrors(), 'Making a field dirty clears errors.');
  766. }
  767. /**
  768. * Tests dirty() when altering properties values and adding new ones
  769. *
  770. * @return void
  771. */
  772. public function testDirtyChangingProperties()
  773. {
  774. $entity = new Entity([
  775. 'title' => 'Foo',
  776. ]);
  777. $entity->setDirty('title', false);
  778. $this->assertFalse($entity->isDirty('title'));
  779. $entity->set('title', 'Foo');
  780. $this->assertTrue($entity->isDirty('title'));
  781. $entity->set('title', 'Foo');
  782. $this->assertTrue($entity->isDirty('title'));
  783. $entity->set('something', 'else');
  784. $this->assertTrue($entity->isDirty('something'));
  785. }
  786. /**
  787. * Tests extract only dirty properties
  788. *
  789. * @return void
  790. */
  791. public function testExtractDirty()
  792. {
  793. $entity = new Entity([
  794. 'id' => 1,
  795. 'title' => 'Foo',
  796. 'author_id' => 3,
  797. ]);
  798. $entity->setDirty('id', false);
  799. $entity->setDirty('title', false);
  800. $expected = ['author_id' => 3];
  801. $result = $entity->extract(['id', 'title', 'author_id'], true);
  802. $this->assertEquals($expected, $result);
  803. }
  804. /**
  805. * Tests the getDirty method
  806. *
  807. * @return void
  808. */
  809. public function testGetDirty()
  810. {
  811. $entity = new Entity([
  812. 'id' => 1,
  813. 'title' => 'Foo',
  814. 'author_id' => 3,
  815. ]);
  816. $expected = [
  817. 'id',
  818. 'title',
  819. 'author_id',
  820. ];
  821. $result = $entity->getDirty();
  822. $this->assertSame($expected, $entity->getDirty());
  823. }
  824. /**
  825. * Tests the clean method
  826. *
  827. * @return void
  828. */
  829. public function testClean()
  830. {
  831. $entity = new Entity([
  832. 'id' => 1,
  833. 'title' => 'Foo',
  834. 'author_id' => 3,
  835. ]);
  836. $this->assertTrue($entity->isDirty('id'));
  837. $this->assertTrue($entity->isDirty('title'));
  838. $this->assertTrue($entity->isDirty('author_id'));
  839. $entity->clean();
  840. $this->assertFalse($entity->isDirty('id'));
  841. $this->assertFalse($entity->isDirty('title'));
  842. $this->assertFalse($entity->isDirty('author_id'));
  843. }
  844. /**
  845. * Tests the isNew method
  846. *
  847. * @return void
  848. */
  849. public function testIsNew()
  850. {
  851. $data = [
  852. 'id' => 1,
  853. 'title' => 'Foo',
  854. 'author_id' => 3,
  855. ];
  856. $entity = new Entity($data);
  857. $this->assertTrue($entity->isNew());
  858. $entity->setNew(true);
  859. $this->assertTrue($entity->isNew());
  860. $entity->setNew(false);
  861. $this->assertFalse($entity->isNew());
  862. }
  863. /**
  864. * Tests the constructor when passing the markClean option
  865. *
  866. * @return void
  867. */
  868. public function testConstructorWithClean()
  869. {
  870. $entity = $this->getMockBuilder(Entity::class)
  871. ->onlyMethods(['clean'])
  872. ->disableOriginalConstructor()
  873. ->getMock();
  874. $entity->expects($this->never())->method('clean');
  875. $entity->__construct(['a' => 'b', 'c' => 'd']);
  876. $entity = $this->getMockBuilder(Entity::class)
  877. ->onlyMethods(['clean'])
  878. ->disableOriginalConstructor()
  879. ->getMock();
  880. $entity->expects($this->once())->method('clean');
  881. $entity->__construct(['a' => 'b', 'c' => 'd'], ['markClean' => true]);
  882. }
  883. /**
  884. * Tests the constructor when passing the markClean option
  885. *
  886. * @return void
  887. */
  888. public function testConstructorWithMarkNew()
  889. {
  890. $entity = $this->getMockBuilder(Entity::class)
  891. ->onlyMethods(['setNew', 'clean'])
  892. ->disableOriginalConstructor()
  893. ->getMock();
  894. $entity->expects($this->never())->method('clean');
  895. $entity->__construct(['a' => 'b', 'c' => 'd']);
  896. $entity = $this->getMockBuilder(Entity::class)
  897. ->onlyMethods(['setNew'])
  898. ->disableOriginalConstructor()
  899. ->getMock();
  900. $entity->expects($this->once())->method('setNew');
  901. $entity->__construct(['a' => 'b', 'c' => 'd'], ['markNew' => true]);
  902. }
  903. /**
  904. * Test toArray method.
  905. *
  906. * @return void
  907. */
  908. public function testToArray()
  909. {
  910. $data = ['name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  911. $entity = new Entity($data);
  912. $this->assertEquals($data, $entity->toArray());
  913. }
  914. /**
  915. * Test toArray recursive.
  916. *
  917. * @return void
  918. */
  919. public function testToArrayRecursive()
  920. {
  921. $data = ['id' => 1, 'name' => 'James', 'age' => 20, 'phones' => ['123', '457']];
  922. $user = new Extending($data);
  923. $comments = [
  924. new NonExtending(['user_id' => 1, 'body' => 'Comment 1']),
  925. new NonExtending(['user_id' => 1, 'body' => 'Comment 2']),
  926. ];
  927. $user->comments = $comments;
  928. $user->profile = new Entity(['email' => 'mark@example.com']);
  929. $expected = [
  930. 'id' => 1,
  931. 'name' => 'James',
  932. 'age' => 20,
  933. 'phones' => ['123', '457'],
  934. 'profile' => ['email' => 'mark@example.com'],
  935. 'comments' => [
  936. ['user_id' => 1, 'body' => 'Comment 1'],
  937. ['user_id' => 1, 'body' => 'Comment 2'],
  938. ],
  939. ];
  940. $this->assertEquals($expected, $user->toArray());
  941. }
  942. /**
  943. * Tests that an entity with entities and other misc types can be properly toArray'd
  944. *
  945. * @return void
  946. */
  947. public function testToArrayMixed()
  948. {
  949. $test = new Entity([
  950. 'id' => 1,
  951. 'foo' => [
  952. new Entity(['hi' => 'test']),
  953. 'notentity' => 1,
  954. ],
  955. ]);
  956. $expected = [
  957. 'id' => 1,
  958. 'foo' => [
  959. ['hi' => 'test'],
  960. 'notentity' => 1,
  961. ],
  962. ];
  963. $this->assertEquals($expected, $test->toArray());
  964. }
  965. /**
  966. * Test that get accessors are called when converting to arrays.
  967. *
  968. * @return void
  969. */
  970. public function testToArrayWithAccessor()
  971. {
  972. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  973. $entity = $this->getMockBuilder(Entity::class)
  974. ->addMethods(['_getName'])
  975. ->getMock();
  976. $entity->setAccess('*', true);
  977. $entity->set(['name' => 'Mark', 'email' => 'mark@example.com']);
  978. $entity->expects($this->any())
  979. ->method('_getName')
  980. ->will($this->returnValue('Jose'));
  981. $expected = ['name' => 'Jose', 'email' => 'mark@example.com'];
  982. $this->assertEquals($expected, $entity->toArray());
  983. }
  984. /**
  985. * Test that toArray respects hidden properties.
  986. *
  987. * @return void
  988. */
  989. public function testToArrayHiddenProperties()
  990. {
  991. $data = ['secret' => 'sauce', 'name' => 'mark', 'id' => 1];
  992. $entity = new Entity($data);
  993. $entity->setHidden(['secret']);
  994. $this->assertEquals(['name' => 'mark', 'id' => 1], $entity->toArray());
  995. }
  996. /**
  997. * Tests setting hidden properties.
  998. *
  999. * @return void
  1000. */
  1001. public function testSetHidden()
  1002. {
  1003. $data = ['secret' => 'sauce', 'name' => 'mark', 'id' => 1];
  1004. $entity = new Entity($data);
  1005. $entity->setHidden(['secret']);
  1006. $result = $entity->getHidden();
  1007. $this->assertSame(['secret'], $result);
  1008. $entity->setHidden(['name']);
  1009. $result = $entity->getHidden();
  1010. $this->assertSame(['name'], $result);
  1011. }
  1012. /**
  1013. * Tests setting hidden properties with merging.
  1014. *
  1015. * @return void
  1016. */
  1017. public function testSetHiddenWithMerge()
  1018. {
  1019. $data = ['secret' => 'sauce', 'name' => 'mark', 'id' => 1];
  1020. $entity = new Entity($data);
  1021. $entity->setHidden(['secret'], true);
  1022. $result = $entity->getHidden();
  1023. $this->assertSame(['secret'], $result);
  1024. $entity->setHidden(['name'], true);
  1025. $result = $entity->getHidden();
  1026. $this->assertSame(['secret', 'name'], $result);
  1027. $entity->setHidden(['name'], true);
  1028. $result = $entity->getHidden();
  1029. $this->assertSame(['secret', 'name'], $result);
  1030. }
  1031. /**
  1032. * Test toArray includes 'virtual' properties.
  1033. *
  1034. * @return void
  1035. */
  1036. public function testToArrayVirtualProperties()
  1037. {
  1038. /** @var \Cake\ORM\Entity|\PHPUnit\Framework\MockObject\MockObject $entity */
  1039. $entity = $this->getMockBuilder(Entity::class)
  1040. ->addMethods(['_getName'])
  1041. ->getMock();
  1042. $entity->setAccess('*', true);
  1043. $entity->expects($this->any())
  1044. ->method('_getName')
  1045. ->will($this->returnValue('Jose'));
  1046. $entity->set(['email' => 'mark@example.com']);
  1047. $entity->setVirtual(['name']);
  1048. $expected = ['name' => 'Jose', 'email' => 'mark@example.com'];
  1049. $this->assertEquals($expected, $entity->toArray());
  1050. $this->assertEquals(['name'], $entity->getVirtual());
  1051. $entity->setHidden(['name']);
  1052. $expected = ['email' => 'mark@example.com'];
  1053. $this->assertEquals($expected, $entity->toArray());
  1054. $this->assertEquals(['name'], $entity->getHidden());
  1055. }
  1056. /**
  1057. * Tests the getVisible() method
  1058. *
  1059. * @return void
  1060. */
  1061. public function testGetVisible()
  1062. {
  1063. $entity = new Entity();
  1064. $entity->foo = 'foo';
  1065. $entity->bar = 'bar';
  1066. $expected = $entity->getVisible();
  1067. $this->assertSame(['foo', 'bar'], $expected);
  1068. }
  1069. /**
  1070. * Tests setting virtual properties with merging.
  1071. *
  1072. * @return void
  1073. */
  1074. public function testSetVirtualWithMerge()
  1075. {
  1076. $data = ['virtual' => 'sauce', 'name' => 'mark', 'id' => 1];
  1077. $entity = new Entity($data);
  1078. $entity->setVirtual(['virtual']);
  1079. $result = $entity->getVirtual();
  1080. $this->assertSame(['virtual'], $result);
  1081. $entity->setVirtual(['name'], true);
  1082. $result = $entity->getVirtual();
  1083. $this->assertSame(['virtual', 'name'], $result);
  1084. $entity->setVirtual(['name'], true);
  1085. $result = $entity->getVirtual();
  1086. $this->assertSame(['virtual', 'name'], $result);
  1087. }
  1088. /**
  1089. * Tests error getters and setters
  1090. *
  1091. * @return void
  1092. */
  1093. public function testGetErrorAndSetError()
  1094. {
  1095. $entity = new Entity();
  1096. $this->assertEmpty($entity->getErrors());
  1097. $entity->setError('foo', 'bar');
  1098. $this->assertEquals(['bar'], $entity->getError('foo'));
  1099. $expected = [
  1100. 'foo' => ['bar'],
  1101. ];
  1102. $result = $entity->getErrors();
  1103. $this->assertEquals($expected, $result);
  1104. $indexedErrors = [2 => ['foo' => 'bar']];
  1105. $entity = new Entity();
  1106. $entity->setError('indexes', $indexedErrors);
  1107. $expectedIndexed = [
  1108. 'indexes' => ['2' => ['foo' => 'bar']],
  1109. ];
  1110. $result = $entity->getErrors();
  1111. $this->assertEquals($expectedIndexed, $result);
  1112. }
  1113. /**
  1114. * Tests reading errors from nested validator
  1115. *
  1116. * @return void
  1117. */
  1118. public function testGetErrorNested()
  1119. {
  1120. $entity = new Entity();
  1121. $entity->setError('options', ['subpages' => ['_empty' => 'required']]);
  1122. $expected = [
  1123. 'subpages' => ['_empty' => 'required'],
  1124. ];
  1125. $this->assertEquals($expected, $entity->getError('options'));
  1126. $expected = ['_empty' => 'required'];
  1127. $this->assertEquals($expected, $entity->getError('options.subpages'));
  1128. }
  1129. /**
  1130. * Tests that it is possible to get errors for nested entities
  1131. *
  1132. * @return void
  1133. */
  1134. public function testErrorsDeep()
  1135. {
  1136. $user = new Entity();
  1137. $owner = new NonExtending();
  1138. $author = new Extending([
  1139. 'foo' => 'bar',
  1140. 'thing' => 'baz',
  1141. 'user' => $user,
  1142. 'owner' => $owner,
  1143. ]);
  1144. $author->setError('thing', ['this is a mistake']);
  1145. $user->setErrors(['a' => ['error1'], 'b' => ['error2']]);
  1146. $owner->setErrors(['c' => ['error3'], 'd' => ['error4']]);
  1147. $expected = ['a' => ['error1'], 'b' => ['error2']];
  1148. $this->assertEquals($expected, $author->getError('user'));
  1149. $expected = ['c' => ['error3'], 'd' => ['error4']];
  1150. $this->assertEquals($expected, $author->getError('owner'));
  1151. $author->set('multiple', [$user, $owner]);
  1152. $expected = [
  1153. ['a' => ['error1'], 'b' => ['error2']],
  1154. ['c' => ['error3'], 'd' => ['error4']],
  1155. ];
  1156. $this->assertEquals($expected, $author->getError('multiple'));
  1157. $expected = [
  1158. 'thing' => $author->getError('thing'),
  1159. 'user' => $author->getError('user'),
  1160. 'owner' => $author->getError('owner'),
  1161. 'multiple' => $author->getError('multiple'),
  1162. ];
  1163. $this->assertEquals($expected, $author->getErrors());
  1164. }
  1165. /**
  1166. * Tests that check if hasErrors() works
  1167. *
  1168. * @return void
  1169. */
  1170. public function testHasErrors()
  1171. {
  1172. $entity = new Entity();
  1173. $hasErrors = $entity->hasErrors();
  1174. $this->assertFalse($hasErrors);
  1175. $nestedEntity = new Entity();
  1176. $entity->set([
  1177. 'nested' => $nestedEntity,
  1178. ]);
  1179. $hasErrors = $entity->hasErrors();
  1180. $this->assertFalse($hasErrors);
  1181. $nestedEntity->setError('description', 'oops');
  1182. $hasErrors = $entity->hasErrors();
  1183. $this->assertTrue($hasErrors);
  1184. $hasErrors = $entity->hasErrors(false);
  1185. $this->assertFalse($hasErrors);
  1186. $entity->clean();
  1187. $hasErrors = $entity->hasErrors();
  1188. $this->assertTrue($hasErrors);
  1189. $hasErrors = $entity->hasErrors(false);
  1190. $this->assertFalse($hasErrors);
  1191. $nestedEntity->clean();
  1192. $hasErrors = $entity->hasErrors();
  1193. $this->assertFalse($hasErrors);
  1194. $entity->setError('foo', []);
  1195. $this->assertFalse($entity->hasErrors());
  1196. }
  1197. /**
  1198. * Test that errors can be read with a path.
  1199. *
  1200. * @return void
  1201. */
  1202. public function testErrorPathReading()
  1203. {
  1204. $assoc = new Entity();
  1205. $assoc2 = new NonExtending();
  1206. $entity = new Extending([
  1207. 'field' => 'value',
  1208. 'one' => $assoc,
  1209. 'many' => [$assoc2],
  1210. ]);
  1211. $entity->setError('wrong', 'Bad stuff');
  1212. $assoc->setError('nope', 'Terrible things');
  1213. $assoc2->setError('nope', 'Terrible things');
  1214. $this->assertEquals(['Bad stuff'], $entity->getError('wrong'));
  1215. $this->assertEquals(['Terrible things'], $entity->getError('many.0.nope'));
  1216. $this->assertEquals(['Terrible things'], $entity->getError('one.nope'));
  1217. $this->assertEquals(['nope' => ['Terrible things']], $entity->getError('one'));
  1218. $this->assertEquals([0 => ['nope' => ['Terrible things']]], $entity->getError('many'));
  1219. $this->assertEquals(['nope' => ['Terrible things']], $entity->getError('many.0'));
  1220. $this->assertEquals([], $entity->getError('many.0.mistake'));
  1221. $this->assertEquals([], $entity->getError('one.mistake'));
  1222. $this->assertEquals([], $entity->getError('one.1.mistake'));
  1223. $this->assertEquals([], $entity->getError('many.1.nope'));
  1224. }
  1225. /**
  1226. * Tests that changing the value of a property will remove errors
  1227. * stored for it
  1228. *
  1229. * @return void
  1230. */
  1231. public function testDirtyRemovesError()
  1232. {
  1233. $entity = new Entity(['a' => 'b']);
  1234. $entity->setError('a', 'is not good');
  1235. $entity->set('a', 'c');
  1236. $this->assertEmpty($entity->getError('a'));
  1237. $entity->setError('a', 'is not good');
  1238. $entity->setDirty('a', true);
  1239. $this->assertEmpty($entity->getError('a'));
  1240. }
  1241. /**
  1242. * Tests that marking an entity as clean will remove errors too
  1243. *
  1244. * @return void
  1245. */
  1246. public function testCleanRemovesErrors()
  1247. {
  1248. $entity = new Entity(['a' => 'b']);
  1249. $entity->setError('a', 'is not good');
  1250. $entity->clean();
  1251. $this->assertEmpty($entity->getErrors());
  1252. }
  1253. /**
  1254. * Tests getAccessible() method
  1255. *
  1256. * @return void
  1257. */
  1258. public function testGetAccessible()
  1259. {
  1260. $entity = new Entity();
  1261. $entity->setAccess('*', false);
  1262. $entity->setAccess('bar', true);
  1263. $accessible = $entity->getAccessible();
  1264. $expected = [
  1265. '*' => false,
  1266. 'bar' => true,
  1267. ];
  1268. $this->assertSame($expected, $accessible);
  1269. }
  1270. /**
  1271. * Tests isAccessible() and setAccess() methods
  1272. *
  1273. * @return void
  1274. */
  1275. public function testIsAccessible()
  1276. {
  1277. $entity = new Entity();
  1278. $entity->setAccess('*', false);
  1279. $this->assertFalse($entity->isAccessible('foo'));
  1280. $this->assertFalse($entity->isAccessible('bar'));
  1281. $this->assertSame($entity, $entity->setAccess('foo', true));
  1282. $this->assertTrue($entity->isAccessible('foo'));
  1283. $this->assertFalse($entity->isAccessible('bar'));
  1284. $this->assertSame($entity, $entity->setAccess('bar', true));
  1285. $this->assertTrue($entity->isAccessible('foo'));
  1286. $this->assertTrue($entity->isAccessible('bar'));
  1287. $this->assertSame($entity, $entity->setAccess('foo', false));
  1288. $this->assertFalse($entity->isAccessible('foo'));
  1289. $this->assertTrue($entity->isAccessible('bar'));
  1290. $this->assertSame($entity, $entity->setAccess('bar', false));
  1291. $this->assertFalse($entity->isAccessible('foo'));
  1292. $this->assertFalse($entity->isAccessible('bar'));
  1293. }
  1294. /**
  1295. * Tests that an array can be used to set
  1296. *
  1297. * @return void
  1298. */
  1299. public function testAccessibleAsArray()
  1300. {
  1301. $entity = new Entity();
  1302. $entity->setAccess(['foo', 'bar', 'baz'], true);
  1303. $this->assertTrue($entity->isAccessible('foo'));
  1304. $this->assertTrue($entity->isAccessible('bar'));
  1305. $this->assertTrue($entity->isAccessible('baz'));
  1306. $entity->setAccess('foo', false);
  1307. $this->assertFalse($entity->isAccessible('foo'));
  1308. $this->assertTrue($entity->isAccessible('bar'));
  1309. $this->assertTrue($entity->isAccessible('baz'));
  1310. $entity->setAccess(['foo', 'bar', 'baz'], false);
  1311. $this->assertFalse($entity->isAccessible('foo'));
  1312. $this->assertFalse($entity->isAccessible('bar'));
  1313. $this->assertFalse($entity->isAccessible('baz'));
  1314. }
  1315. /**
  1316. * Tests that a wildcard can be used for setting accessible properties
  1317. *
  1318. * @return void
  1319. */
  1320. public function testAccessibleWildcard()
  1321. {
  1322. $entity = new Entity();
  1323. $entity->setAccess(['foo', 'bar', 'baz'], true);
  1324. $this->assertTrue($entity->isAccessible('foo'));
  1325. $this->assertTrue($entity->isAccessible('bar'));
  1326. $this->assertTrue($entity->isAccessible('baz'));
  1327. $entity->setAccess('*', false);
  1328. $this->assertFalse($entity->isAccessible('foo'));
  1329. $this->assertFalse($entity->isAccessible('bar'));
  1330. $this->assertFalse($entity->isAccessible('baz'));
  1331. $this->assertFalse($entity->isAccessible('newOne'));
  1332. $entity->setAccess('*', true);
  1333. $this->assertTrue($entity->isAccessible('foo'));
  1334. $this->assertTrue($entity->isAccessible('bar'));
  1335. $this->assertTrue($entity->isAccessible('baz'));
  1336. $this->assertTrue($entity->isAccessible('newOne2'));
  1337. }
  1338. /**
  1339. * Tests that only accessible properties can be set
  1340. *
  1341. * @return void
  1342. */
  1343. public function testSetWithAccessible()
  1344. {
  1345. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1346. $options = ['guard' => true];
  1347. $entity->setAccess('*', false);
  1348. $entity->setAccess('foo', true);
  1349. $entity->set('bar', 3, $options);
  1350. $entity->set('foo', 4, $options);
  1351. $this->assertSame(2, $entity->get('bar'));
  1352. $this->assertSame(4, $entity->get('foo'));
  1353. $entity->setAccess('bar', true);
  1354. $entity->set('bar', 3, $options);
  1355. $this->assertSame(3, $entity->get('bar'));
  1356. }
  1357. /**
  1358. * Tests that only accessible properties can be set
  1359. *
  1360. * @return void
  1361. */
  1362. public function testSetWithAccessibleWithArray()
  1363. {
  1364. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1365. $options = ['guard' => true];
  1366. $entity->setAccess('*', false);
  1367. $entity->setAccess('foo', true);
  1368. $entity->set(['bar' => 3, 'foo' => 4], $options);
  1369. $this->assertSame(2, $entity->get('bar'));
  1370. $this->assertSame(4, $entity->get('foo'));
  1371. $entity->setAccess('bar', true);
  1372. $entity->set(['bar' => 3, 'foo' => 5], $options);
  1373. $this->assertSame(3, $entity->get('bar'));
  1374. $this->assertSame(5, $entity->get('foo'));
  1375. }
  1376. /**
  1377. * Test that accessible() and single property setting works.
  1378. *
  1379. * @return void
  1380. */
  1381. public function testSetWithAccessibleSingleProperty()
  1382. {
  1383. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1384. $entity->setAccess('*', false);
  1385. $entity->setAccess('title', true);
  1386. $entity->set(['title' => 'test', 'body' => 'Nope']);
  1387. $this->assertSame('test', $entity->title);
  1388. $this->assertNull($entity->body);
  1389. $entity->body = 'Yep';
  1390. $this->assertSame('Yep', $entity->body, 'Single set should bypass guards.');
  1391. $entity->set('body', 'Yes');
  1392. $this->assertSame('Yes', $entity->body, 'Single set should bypass guards.');
  1393. }
  1394. /**
  1395. * Tests the entity's __toString method
  1396. *
  1397. * @return void
  1398. */
  1399. public function testToString()
  1400. {
  1401. $entity = new Entity(['foo' => 1, 'bar' => 2]);
  1402. $this->assertEquals(json_encode($entity, JSON_PRETTY_PRINT), (string)$entity);
  1403. }
  1404. /**
  1405. * Tests __debugInfo
  1406. *
  1407. * @return void
  1408. */
  1409. public function testDebugInfo()
  1410. {
  1411. $entity = new Entity(['foo' => 'bar'], ['markClean' => true]);
  1412. $entity->somethingElse = 'value';
  1413. $entity->setAccess('id', false);
  1414. $entity->setAccess('name', true);
  1415. $entity->setVirtual(['baz']);
  1416. $entity->setDirty('foo', true);
  1417. $entity->setError('foo', ['An error']);
  1418. $entity->setInvalidField('foo', 'a value');
  1419. $entity->setSource('foos');
  1420. $result = $entity->__debugInfo();
  1421. $expected = [
  1422. 'foo' => 'bar',
  1423. 'somethingElse' => 'value',
  1424. 'baz' => null,
  1425. '[new]' => true,
  1426. '[accessible]' => ['*' => true, 'id' => false, 'name' => true],
  1427. '[dirty]' => ['somethingElse' => true, 'foo' => true],
  1428. '[original]' => [],
  1429. '[virtual]' => ['baz'],
  1430. '[hasErrors]' => true,
  1431. '[errors]' => ['foo' => ['An error']],
  1432. '[invalid]' => ['foo' => 'a value'],
  1433. '[repository]' => 'foos',
  1434. ];
  1435. $this->assertSame($expected, $result);
  1436. }
  1437. /**
  1438. * Test the source getter
  1439. */
  1440. public function testGetAndSetSource()
  1441. {
  1442. $entity = new Entity();
  1443. $this->assertSame('', $entity->getSource());
  1444. $entity->setSource('foos');
  1445. $this->assertSame('foos', $entity->getSource());
  1446. }
  1447. /**
  1448. * Provides empty values
  1449. *
  1450. * @return array
  1451. */
  1452. public function emptyNamesProvider()
  1453. {
  1454. return [[''], [null]];
  1455. }
  1456. /**
  1457. * Tests that trying to get an empty property name throws exception
  1458. *
  1459. * @return void
  1460. */
  1461. public function testEmptyProperties()
  1462. {
  1463. $this->expectException(\InvalidArgumentException::class);
  1464. $entity = new Entity();
  1465. $entity->get('');
  1466. }
  1467. /**
  1468. * Tests that setting an empty property name does nothing
  1469. *
  1470. * @dataProvider emptyNamesProvider
  1471. * @return void
  1472. */
  1473. public function testSetEmptyPropertyName($property)
  1474. {
  1475. $this->expectException(\InvalidArgumentException::class);
  1476. $entity = new Entity();
  1477. $entity->set($property, 'bar');
  1478. }
  1479. /**
  1480. * Provides empty values
  1481. *
  1482. * @return void
  1483. */
  1484. public function testIsDirtyFromClone()
  1485. {
  1486. $entity = new Entity(
  1487. ['a' => 1, 'b' => 2],
  1488. ['markNew' => false, 'markClean' => true]
  1489. );
  1490. $this->assertFalse($entity->isNew());
  1491. $this->assertFalse($entity->isDirty());
  1492. $cloned = clone $entity;
  1493. $cloned->setNew(true);
  1494. $this->assertTrue($cloned->isDirty());
  1495. $this->assertTrue($cloned->isDirty('a'));
  1496. $this->assertTrue($cloned->isDirty('b'));
  1497. }
  1498. /**
  1499. * Tests getInvalid and setInvalid
  1500. *
  1501. * @return void
  1502. */
  1503. public function testGetSetInvalid()
  1504. {
  1505. $entity = new Entity();
  1506. $return = $entity->setInvalid([
  1507. 'title' => 'albert',
  1508. 'body' => 'einstein',
  1509. ]);
  1510. $this->assertSame($entity, $return);
  1511. $this->assertSame([
  1512. 'title' => 'albert',
  1513. 'body' => 'einstein',
  1514. ], $entity->getInvalid());
  1515. $set = $entity->setInvalid([
  1516. 'title' => 'nikola',
  1517. 'body' => 'tesla',
  1518. ]);
  1519. $this->assertSame([
  1520. 'title' => 'albert',
  1521. 'body' => 'einstein',
  1522. ], $set->getInvalid());
  1523. $overwrite = $entity->setInvalid([
  1524. 'title' => 'nikola',
  1525. 'body' => 'tesla',
  1526. ], true);
  1527. $this->assertSame($entity, $overwrite);
  1528. $this->assertSame([
  1529. 'title' => 'nikola',
  1530. 'body' => 'tesla',
  1531. ], $entity->getInvalid());
  1532. }
  1533. /**
  1534. * Tests getInvalidField
  1535. *
  1536. * @return void
  1537. */
  1538. public function testGetSetInvalidField()
  1539. {
  1540. $entity = new Entity();
  1541. $return = $entity->setInvalidField('title', 'albert');
  1542. $this->assertSame($entity, $return);
  1543. $this->assertSame('albert', $entity->getInvalidField('title'));
  1544. $overwrite = $entity->setInvalidField('title', 'nikola');
  1545. $this->assertSame($entity, $overwrite);
  1546. $this->assertSame('nikola', $entity->getInvalidField('title'));
  1547. }
  1548. /**
  1549. * Tests getInvalidFieldNull
  1550. *
  1551. * @return void
  1552. */
  1553. public function testGetInvalidFieldNull()
  1554. {
  1555. $entity = new Entity();
  1556. $this->assertNull($entity->getInvalidField('foo'));
  1557. }
  1558. /**
  1559. * Test the isEmpty() check
  1560. *
  1561. * @return void
  1562. */
  1563. public function testIsEmpty()
  1564. {
  1565. $entity = new Entity([
  1566. 'array' => ['foo' => 'bar'],
  1567. 'emptyArray' => [],
  1568. 'object' => new \stdClass(),
  1569. 'string' => 'string',
  1570. 'stringZero' => '0',
  1571. 'emptyString' => '',
  1572. 'intZero' => 0,
  1573. 'intNotZero' => 1,
  1574. 'floatZero' => 0.0,
  1575. 'floatNonZero' => 1.5,
  1576. 'null' => null,
  1577. ]);
  1578. $this->assertFalse($entity->isEmpty('array'));
  1579. $this->assertTrue($entity->isEmpty('emptyArray'));
  1580. $this->assertFalse($entity->isEmpty('object'));
  1581. $this->assertFalse($entity->isEmpty('string'));
  1582. $this->assertFalse($entity->isEmpty('stringZero'));
  1583. $this->assertTrue($entity->isEmpty('emptyString'));
  1584. $this->assertFalse($entity->isEmpty('intZero'));
  1585. $this->assertFalse($entity->isEmpty('intNotZero'));
  1586. $this->assertFalse($entity->isEmpty('floatZero'));
  1587. $this->assertFalse($entity->isEmpty('floatNonZero'));
  1588. $this->assertTrue($entity->isEmpty('null'));
  1589. }
  1590. /**
  1591. * Test hasValue()
  1592. *
  1593. * @return void
  1594. */
  1595. public function testHasValue()
  1596. {
  1597. $entity = new Entity([
  1598. 'array' => ['foo' => 'bar'],
  1599. 'emptyArray' => [],
  1600. 'object' => new \stdClass(),
  1601. 'string' => 'string',
  1602. 'stringZero' => '0',
  1603. 'emptyString' => '',
  1604. 'intZero' => 0,
  1605. 'intNotZero' => 1,
  1606. 'floatZero' => 0.0,
  1607. 'floatNonZero' => 1.5,
  1608. 'null' => null,
  1609. ]);
  1610. $this->assertTrue($entity->hasValue('array'));
  1611. $this->assertFalse($entity->hasValue('emptyArray'));
  1612. $this->assertTrue($entity->hasValue('object'));
  1613. $this->assertTrue($entity->hasValue('string'));
  1614. $this->assertTrue($entity->hasValue('stringZero'));
  1615. $this->assertFalse($entity->hasValue('emptyString'));
  1616. $this->assertTrue($entity->hasValue('intZero'));
  1617. $this->assertTrue($entity->hasValue('intNotZero'));
  1618. $this->assertTrue($entity->hasValue('floatZero'));
  1619. $this->assertTrue($entity->hasValue('floatNonZero'));
  1620. $this->assertFalse($entity->hasValue('null'));
  1621. }
  1622. }