EntityTest.php 56 KB

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