EntityContextTest.php 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\View\Form;
  16. use ArrayIterator;
  17. use ArrayObject;
  18. use Cake\Collection\Collection;
  19. use Cake\Network\Request;
  20. use Cake\ORM\Entity;
  21. use Cake\ORM\TableRegistry;
  22. use Cake\TestSuite\TestCase;
  23. use Cake\Validation\Validator;
  24. use Cake\View\Form\EntityContext;
  25. /**
  26. * Test stub.
  27. */
  28. class Article extends Entity
  29. {
  30. /**
  31. * Testing stub method.
  32. *
  33. * @return bool
  34. */
  35. public function isRequired()
  36. {
  37. return true;
  38. }
  39. }
  40. /**
  41. * Entity context test case.
  42. */
  43. class EntityContextTest extends TestCase
  44. {
  45. /**
  46. * Fixtures to use.
  47. *
  48. * @var array
  49. */
  50. public $fixtures = ['core.articles', 'core.comments'];
  51. /**
  52. * setup method.
  53. *
  54. * @return void
  55. */
  56. public function setUp()
  57. {
  58. parent::setUp();
  59. $this->request = new Request();
  60. }
  61. /**
  62. * Test getting entity back from context.
  63. *
  64. * @return void
  65. */
  66. public function testEntity()
  67. {
  68. $row = new Article();
  69. $context = new EntityContext($this->request, [
  70. 'entity' => $row,
  71. ]);
  72. $this->assertSame($row, $context->entity());
  73. }
  74. /**
  75. * Test getting primary key data.
  76. *
  77. * @return void
  78. */
  79. public function testPrimaryKey()
  80. {
  81. $row = new Article();
  82. $context = new EntityContext($this->request, [
  83. 'entity' => $row,
  84. ]);
  85. $this->assertEquals(['id'], $context->primaryKey());
  86. }
  87. /**
  88. * Test isPrimaryKey
  89. *
  90. * @return void
  91. */
  92. public function testIsPrimaryKey()
  93. {
  94. $row = new Article();
  95. $context = new EntityContext($this->request, [
  96. 'entity' => $row,
  97. ]);
  98. $this->assertTrue($context->isPrimaryKey('id'));
  99. $this->assertFalse($context->isPrimaryKey('title'));
  100. $this->assertTrue($context->isPrimaryKey('1.id'));
  101. $this->assertTrue($context->isPrimaryKey('Articles.1.id'));
  102. $this->assertTrue($context->isPrimaryKey('comments.0.id'));
  103. $this->assertTrue($context->isPrimaryKey('1.comments.0.id'));
  104. $this->assertFalse($context->isPrimaryKey('1.comments.0.comment'));
  105. $this->assertFalse($context->isPrimaryKey('Articles.1.comments.0.comment'));
  106. }
  107. /**
  108. * Test isCreate on a single entity.
  109. *
  110. * @return void
  111. */
  112. public function testIsCreateSingle()
  113. {
  114. $row = new Article();
  115. $context = new EntityContext($this->request, [
  116. 'entity' => $row,
  117. ]);
  118. $this->assertTrue($context->isCreate());
  119. $row->isNew(false);
  120. $this->assertFalse($context->isCreate());
  121. $row->isNew(true);
  122. $this->assertTrue($context->isCreate());
  123. }
  124. /**
  125. * Test isCreate on a collection.
  126. *
  127. * @dataProvider collectionProvider
  128. * @return void
  129. */
  130. public function testIsCreateCollection($collection)
  131. {
  132. $context = new EntityContext($this->request, [
  133. 'entity' => $collection,
  134. ]);
  135. $this->assertTrue($context->isCreate());
  136. }
  137. /**
  138. * Test an invalid table scope throws an error.
  139. *
  140. * @expectedException \RuntimeException
  141. * @expectedExceptionMessage Unable to find table class for current entity
  142. */
  143. public function testInvalidTable()
  144. {
  145. $row = new \StdClass();
  146. $context = new EntityContext($this->request, [
  147. 'entity' => $row,
  148. ]);
  149. }
  150. /**
  151. * Tests that passing a plain entity will give an error as it cannot be matched
  152. *
  153. * @expectedException \RuntimeException
  154. * @expectedExceptionMessage Unable to find table class for current entity
  155. */
  156. public function testDefaultEntityError()
  157. {
  158. $context = new EntityContext($this->request, [
  159. 'entity' => new \Cake\ORM\Entity,
  160. ]);
  161. }
  162. /**
  163. * Tests that the table can be derived from the entity source if it is present
  164. *
  165. * @return void
  166. */
  167. public function testTableFromEntitySource()
  168. {
  169. $entity = new Entity;
  170. $entity->source('Articles');
  171. $context = new EntityContext($this->request, [
  172. 'entity' => $entity,
  173. ]);
  174. $expected = ['id', 'author_id', 'title', 'body', 'published'];
  175. $this->assertEquals($expected, $context->fieldNames());
  176. }
  177. /**
  178. * Test operations with no entity.
  179. *
  180. * @return void
  181. */
  182. public function testOperationsNoEntity()
  183. {
  184. $context = new EntityContext($this->request, [
  185. 'table' => 'Articles'
  186. ]);
  187. $this->assertNull($context->val('title'));
  188. $this->assertFalse($context->isRequired('title'));
  189. $this->assertFalse($context->hasError('title'));
  190. $this->assertEquals('string', $context->type('title'));
  191. $this->assertEquals([], $context->error('title'));
  192. $attrs = $context->attributes('title');
  193. $this->assertArrayHasKey('length', $attrs);
  194. $this->assertArrayHasKey('precision', $attrs);
  195. }
  196. /**
  197. * Test operations that lack a table argument.
  198. *
  199. * @return void
  200. */
  201. public function testOperationsNoTableArg()
  202. {
  203. $row = new Article([
  204. 'title' => 'Test entity',
  205. 'body' => 'Something new'
  206. ]);
  207. $row->errors('title', ['Title is required.']);
  208. $context = new EntityContext($this->request, [
  209. 'entity' => $row,
  210. ]);
  211. $result = $context->val('title');
  212. $this->assertEquals($row->title, $result);
  213. $result = $context->error('title');
  214. $this->assertEquals($row->errors('title'), $result);
  215. $this->assertTrue($context->hasError('title'));
  216. }
  217. /**
  218. * Test collection operations that lack a table argument.
  219. *
  220. * @dataProvider collectionProvider
  221. * @return void
  222. */
  223. public function testCollectionOperationsNoTableArg($collection)
  224. {
  225. $context = new EntityContext($this->request, [
  226. 'entity' => $collection,
  227. ]);
  228. $result = $context->val('0.title');
  229. $this->assertEquals('First post', $result);
  230. $result = $context->error('1.body');
  231. $this->assertEquals(['Not long enough'], $result);
  232. }
  233. /**
  234. * Data provider for testing collections.
  235. *
  236. * @return array
  237. */
  238. public static function collectionProvider()
  239. {
  240. $one = new Article([
  241. 'title' => 'First post',
  242. 'body' => 'Stuff',
  243. 'user' => new Entity(['username' => 'mark'])
  244. ]);
  245. $one->errors('title', 'Required field');
  246. $two = new Article([
  247. 'title' => 'Second post',
  248. 'body' => 'Some text',
  249. 'user' => new Entity(['username' => 'jose'])
  250. ]);
  251. $two->errors('body', 'Not long enough');
  252. return [
  253. 'array' => [[$one, $two]],
  254. 'basic iterator' => [new ArrayObject([$one, $two])],
  255. 'array iterator' => [new ArrayIterator([$one, $two])],
  256. 'collection' => [new Collection([$one, $two])],
  257. ];
  258. }
  259. /**
  260. * Test operations on a collection of entities.
  261. *
  262. * @dataProvider collectionProvider
  263. * @return void
  264. */
  265. public function testValOnCollections($collection)
  266. {
  267. $context = new EntityContext($this->request, [
  268. 'entity' => $collection,
  269. 'table' => 'Articles',
  270. ]);
  271. $result = $context->val('0.title');
  272. $this->assertEquals('First post', $result);
  273. $result = $context->val('0.user.username');
  274. $this->assertEquals('mark', $result);
  275. $result = $context->val('1.title');
  276. $this->assertEquals('Second post', $result);
  277. $result = $context->val('1.user.username');
  278. $this->assertEquals('jose', $result);
  279. $this->assertNull($context->val('nope'));
  280. $this->assertNull($context->val('99.title'));
  281. }
  282. /**
  283. * Test operations on a collection of entities when prefixing with the
  284. * table name
  285. *
  286. * @dataProvider collectionProvider
  287. * @return void
  288. */
  289. public function testValOnCollectionsWithRootName($collection)
  290. {
  291. $context = new EntityContext($this->request, [
  292. 'entity' => $collection,
  293. 'table' => 'Articles',
  294. ]);
  295. $result = $context->val('Articles.0.title');
  296. $this->assertEquals('First post', $result);
  297. $result = $context->val('Articles.0.user.username');
  298. $this->assertEquals('mark', $result);
  299. $result = $context->val('Articles.1.title');
  300. $this->assertEquals('Second post', $result);
  301. $result = $context->val('Articles.1.user.username');
  302. $this->assertEquals('jose', $result);
  303. $this->assertNull($context->val('Articles.99.title'));
  304. }
  305. /**
  306. * Test error operations on a collection of entities.
  307. *
  308. * @dataProvider collectionProvider
  309. * @return void
  310. */
  311. public function testErrorsOnCollections($collection)
  312. {
  313. $context = new EntityContext($this->request, [
  314. 'entity' => $collection,
  315. 'table' => 'Articles',
  316. ]);
  317. $this->assertTrue($context->hasError('0.title'));
  318. $this->assertEquals(['Required field'], $context->error('0.title'));
  319. $this->assertFalse($context->hasError('0.body'));
  320. $this->assertFalse($context->hasError('1.title'));
  321. $this->assertEquals(['Not long enough'], $context->error('1.body'));
  322. $this->assertTrue($context->hasError('1.body'));
  323. $this->assertFalse($context->hasError('nope'));
  324. $this->assertFalse($context->hasError('99.title'));
  325. }
  326. /**
  327. * Test schema operations on a collection of entities.
  328. *
  329. * @dataProvider collectionProvider
  330. * @return void
  331. */
  332. public function testSchemaOnCollections($collection)
  333. {
  334. $this->_setupTables();
  335. $context = new EntityContext($this->request, [
  336. 'entity' => $collection,
  337. 'table' => 'Articles',
  338. ]);
  339. $this->assertEquals('string', $context->type('0.title'));
  340. $this->assertEquals('text', $context->type('1.body'));
  341. $this->assertEquals('string', $context->type('0.user.username'));
  342. $this->assertEquals('string', $context->type('1.user.username'));
  343. $this->assertEquals('string', $context->type('99.title'));
  344. $this->assertNull($context->type('0.nope'));
  345. $expected = ['length' => 255, 'precision' => null];
  346. $this->assertEquals($expected, $context->attributes('0.user.username'));
  347. }
  348. /**
  349. * Test validation operations on a collection of entities.
  350. *
  351. * @dataProvider collectionProvider
  352. * @return void
  353. */
  354. public function testValidatorsOnCollections($collection)
  355. {
  356. $this->_setupTables();
  357. $context = new EntityContext($this->request, [
  358. 'entity' => $collection,
  359. 'table' => 'Articles',
  360. 'validator' => [
  361. 'Articles' => 'create',
  362. 'Users' => 'custom',
  363. ]
  364. ]);
  365. $this->assertFalse($context->isRequired('nope'));
  366. $this->assertTrue($context->isRequired('0.title'));
  367. $this->assertTrue($context->isRequired('0.user.username'));
  368. $this->assertFalse($context->isRequired('1.body'));
  369. $this->assertTrue($context->isRequired('99.title'));
  370. $this->assertFalse($context->isRequired('99.nope'));
  371. }
  372. /**
  373. * Test reading data.
  374. *
  375. * @return void
  376. */
  377. public function testValBasic()
  378. {
  379. $row = new Article([
  380. 'title' => 'Test entity',
  381. 'body' => 'Something new'
  382. ]);
  383. $context = new EntityContext($this->request, [
  384. 'entity' => $row,
  385. 'table' => 'Articles',
  386. ]);
  387. $result = $context->val('title');
  388. $this->assertEquals($row->title, $result);
  389. $result = $context->val('body');
  390. $this->assertEquals($row->body, $result);
  391. $result = $context->val('nope');
  392. $this->assertNull($result);
  393. }
  394. /**
  395. * Test reading array values from an entity.
  396. *
  397. * @return void
  398. */
  399. public function testValGetArrayValue()
  400. {
  401. $row = new Article([
  402. 'title' => 'Test entity',
  403. 'types' => [1, 2, 3],
  404. 'tag' => [
  405. 'name' => 'Test tag',
  406. ],
  407. 'author' => new Entity([
  408. 'roles' => ['admin', 'publisher']
  409. ])
  410. ]);
  411. $context = new EntityContext($this->request, [
  412. 'entity' => $row,
  413. 'table' => 'Articles',
  414. ]);
  415. $result = $context->val('types');
  416. $this->assertEquals($row->types, $result);
  417. $result = $context->val('author.roles');
  418. $this->assertEquals($row->author->roles, $result);
  419. $result = $context->val('tag.name');
  420. $this->assertEquals($row->tag['name'], $result);
  421. $result = $context->val('tag.nope');
  422. $this->assertNull($result);
  423. $result = $context->val('author.roles.3');
  424. $this->assertNull($result);
  425. }
  426. /**
  427. * Test that val() reads from the request.
  428. *
  429. * @return void
  430. */
  431. public function testValReadsRequest()
  432. {
  433. $this->request->data = [
  434. 'title' => 'New title',
  435. 'notInEntity' => 'yes',
  436. ];
  437. $row = new Article([
  438. 'title' => 'Test entity',
  439. 'body' => 'Something new'
  440. ]);
  441. $context = new EntityContext($this->request, [
  442. 'entity' => $row,
  443. 'table' => 'Articles',
  444. ]);
  445. $this->assertEquals('New title', $context->val('title'));
  446. $this->assertEquals('yes', $context->val('notInEntity'));
  447. $this->assertEquals($row->body, $context->val('body'));
  448. }
  449. /**
  450. * Test reading values from associated entities.
  451. *
  452. * @return void
  453. */
  454. public function testValAssociated()
  455. {
  456. $row = new Article([
  457. 'title' => 'Test entity',
  458. 'user' => new Entity([
  459. 'username' => 'mark',
  460. 'fname' => 'Mark'
  461. ]),
  462. 'comments' => [
  463. new Entity(['comment' => 'Test comment']),
  464. new Entity(['comment' => 'Second comment']),
  465. ]
  466. ]);
  467. $context = new EntityContext($this->request, [
  468. 'entity' => $row,
  469. 'table' => 'Articles',
  470. ]);
  471. $result = $context->val('user.fname');
  472. $this->assertEquals($row->user->fname, $result);
  473. $result = $context->val('comments.0.comment');
  474. $this->assertEquals($row->comments[0]->comment, $result);
  475. $result = $context->val('comments.1.comment');
  476. $this->assertEquals($row->comments[1]->comment, $result);
  477. $result = $context->val('comments.0.nope');
  478. $this->assertNull($result);
  479. $result = $context->val('comments.0.nope.no_way');
  480. $this->assertNull($result);
  481. }
  482. /**
  483. * Tests that trying to get values from missing associations returns null
  484. *
  485. * @return void
  486. */
  487. public function testValMissingAssociation()
  488. {
  489. $row = new Article([
  490. 'id' => 1
  491. ]);
  492. $context = new EntityContext($this->request, [
  493. 'entity' => $row,
  494. 'table' => 'Articles',
  495. ]);
  496. $result = $context->val('id');
  497. $this->assertEquals($row->id, $result);
  498. $this->assertNull($context->val('profile.id'));
  499. }
  500. /**
  501. * Test reading values from associated entities.
  502. *
  503. * @return void
  504. */
  505. public function testValAssociatedHasMany()
  506. {
  507. $row = new Article([
  508. 'title' => 'First post',
  509. 'user' => new Entity([
  510. 'username' => 'mark',
  511. 'fname' => 'Mark',
  512. 'articles' => [
  513. new Article(['title' => 'First post']),
  514. new Article(['title' => 'Second post']),
  515. ]
  516. ]),
  517. ]);
  518. $context = new EntityContext($this->request, [
  519. 'entity' => $row,
  520. 'table' => 'Articles',
  521. ]);
  522. $result = $context->val('user.articles.0.title');
  523. $this->assertEquals('First post', $result);
  524. $result = $context->val('user.articles.1.title');
  525. $this->assertEquals('Second post', $result);
  526. }
  527. /**
  528. * Test reading values for magic _ids input
  529. *
  530. * @return void
  531. */
  532. public function testValAssociatedDefaultIds()
  533. {
  534. $row = new Article([
  535. 'title' => 'First post',
  536. 'user' => new Entity([
  537. 'username' => 'mark',
  538. 'fname' => 'Mark',
  539. 'groups' => [
  540. new Entity(['title' => 'PHP', 'id' => 1]),
  541. new Entity(['title' => 'Javascript', 'id' => 2]),
  542. ]
  543. ]),
  544. ]);
  545. $context = new EntityContext($this->request, [
  546. 'entity' => $row,
  547. 'table' => 'Articles',
  548. ]);
  549. $result = $context->val('user.groups._ids');
  550. $this->assertEquals([1, 2], $result);
  551. }
  552. /**
  553. * Test reading values for magic _ids input
  554. *
  555. * @return void
  556. */
  557. public function testValAssociatedCustomIds()
  558. {
  559. $this->_setupTables();
  560. $row = new Article([
  561. 'title' => 'First post',
  562. 'user' => new Entity([
  563. 'username' => 'mark',
  564. 'fname' => 'Mark',
  565. 'groups' => [
  566. new Entity(['title' => 'PHP', 'thing' => 1]),
  567. new Entity(['title' => 'Javascript', 'thing' => 4]),
  568. ]
  569. ]),
  570. ]);
  571. $context = new EntityContext($this->request, [
  572. 'entity' => $row,
  573. 'table' => 'Articles',
  574. ]);
  575. TableRegistry::get('Users')->belongsToMany('Groups');
  576. TableRegistry::get('Groups')->primaryKey('thing');
  577. $result = $context->val('user.groups._ids');
  578. $this->assertEquals([1, 4], $result);
  579. }
  580. /**
  581. * Test getting default value from table schema.
  582. *
  583. * @return void
  584. */
  585. public function testValSchemaDefault()
  586. {
  587. $table = TableRegistry::get('Articles');
  588. $column = $table->schema()->column('title');
  589. $table->schema()->addColumn('title', ['default' => 'default title'] + $column);
  590. $row = $table->newEntity();
  591. $context = new EntityContext($this->request, [
  592. 'entity' => $row,
  593. 'table' => 'Articles',
  594. ]);
  595. $result = $context->val('title');
  596. $this->assertEquals('default title', $result);
  597. }
  598. /**
  599. * Test validator for boolean fields.
  600. *
  601. * @return void
  602. */
  603. public function testIsRequiredBooleanField()
  604. {
  605. $this->_setupTables();
  606. $context = new EntityContext($this->request, [
  607. 'entity' => new Entity(),
  608. 'table' => 'Articles',
  609. ]);
  610. $articles = TableRegistry::get('Articles');
  611. $articles->schema()->addColumn('comments_on', [
  612. 'type' => 'boolean'
  613. ]);
  614. $validator = $articles->validator();
  615. $validator->add('comments_on', 'is_bool', [
  616. 'rule' => 'boolean'
  617. ]);
  618. $articles->validator('default', $validator);
  619. $this->assertFalse($context->isRequired('title'));
  620. }
  621. /**
  622. * Test validator as a string.
  623. *
  624. * @return void
  625. */
  626. public function testIsRequiredStringValidator()
  627. {
  628. $this->_setupTables();
  629. $context = new EntityContext($this->request, [
  630. 'entity' => new Entity(),
  631. 'table' => 'Articles',
  632. 'validator' => 'create',
  633. ]);
  634. $this->assertTrue($context->isRequired('title'));
  635. $this->assertFalse($context->isRequired('body'));
  636. $this->assertFalse($context->isRequired('Herp.derp.derp'));
  637. $this->assertFalse($context->isRequired('nope'));
  638. $this->assertFalse($context->isRequired(''));
  639. }
  640. /**
  641. * Test isRequired on associated entities.
  642. *
  643. * @return void
  644. */
  645. public function testIsRequiredAssociatedHasMany()
  646. {
  647. $this->_setupTables();
  648. $comments = TableRegistry::get('Comments');
  649. $validator = $comments->validator();
  650. $validator->add('user_id', 'number', [
  651. 'rule' => 'numeric',
  652. ]);
  653. $row = new Article([
  654. 'title' => 'My title',
  655. 'comments' => [
  656. new Entity(['comment' => 'First comment']),
  657. new Entity(['comment' => 'Second comment']),
  658. ]
  659. ]);
  660. $context = new EntityContext($this->request, [
  661. 'entity' => $row,
  662. 'table' => 'Articles',
  663. 'validator' => 'default',
  664. ]);
  665. $this->assertTrue($context->isRequired('comments.0.user_id'));
  666. $this->assertFalse($context->isRequired('comments.0.other'));
  667. $this->assertFalse($context->isRequired('user.0.other'));
  668. $this->assertFalse($context->isRequired(''));
  669. }
  670. /**
  671. * Test isRequired on associated entities with boolean fields
  672. *
  673. * @return void
  674. */
  675. public function testIsRequiredAssociatedHasManyBoolean()
  676. {
  677. $this->_setupTables();
  678. $comments = TableRegistry::get('Comments');
  679. $comments->schema()->addColumn('starred', 'boolean');
  680. $comments->validator()->add('starred', 'valid', ['rule' => 'boolean']);
  681. $row = new Article([
  682. 'title' => 'My title',
  683. 'comments' => [
  684. new Entity(['comment' => 'First comment']),
  685. ]
  686. ]);
  687. $context = new EntityContext($this->request, [
  688. 'entity' => $row,
  689. 'table' => 'Articles',
  690. 'validator' => 'default',
  691. ]);
  692. $this->assertFalse($context->isRequired('comments.0.starred'));
  693. }
  694. /**
  695. * Test isRequired on associated entities with custom validators.
  696. *
  697. * Ensures that missing associations use the correct entity class
  698. * so provider methods work correctly.
  699. *
  700. * @return void
  701. */
  702. public function testIsRequiredAssociatedCustomValidator()
  703. {
  704. $this->_setupTables();
  705. $users = TableRegistry::get('Users');
  706. $articles = TableRegistry::get('Articles');
  707. $validator = $articles->validator();
  708. $validator->notEmpty('title', 'nope', function ($context) {
  709. return $context['providers']['entity']->isRequired();
  710. });
  711. $articles->validator('default', $validator);
  712. $row = new Entity([
  713. 'username' => 'mark'
  714. ]);
  715. $context = new EntityContext($this->request, [
  716. 'entity' => $row,
  717. 'table' => 'Users',
  718. 'validator' => 'default',
  719. ]);
  720. $this->assertTrue($context->isRequired('articles.0.title'));
  721. }
  722. /**
  723. * Test isRequired on associated entities.
  724. *
  725. * @return void
  726. */
  727. public function testIsRequiredAssociatedHasManyMissingObject()
  728. {
  729. $this->_setupTables();
  730. $comments = TableRegistry::get('Comments');
  731. $validator = $comments->validator();
  732. $validator->allowEmpty('comment', function ($context) {
  733. return $context['providers']['entity']->isNew();
  734. });
  735. $row = new Article([
  736. 'title' => 'My title',
  737. 'comments' => [
  738. new Entity(['comment' => 'First comment'], ['markNew' => false]),
  739. ]
  740. ]);
  741. $context = new EntityContext($this->request, [
  742. 'entity' => $row,
  743. 'table' => 'Articles',
  744. 'validator' => 'default',
  745. ]);
  746. $this->assertTrue(
  747. $context->isRequired('comments.0.comment'),
  748. 'comment is required as object is not new'
  749. );
  750. $this->assertFalse(
  751. $context->isRequired('comments.1.comment'),
  752. 'comment is not required as missing object is "new"'
  753. );
  754. }
  755. /**
  756. * Test isRequired on associated entities with custom validators.
  757. *
  758. * @return void
  759. */
  760. public function testIsRequiredAssociatedValidator()
  761. {
  762. $this->_setupTables();
  763. $row = new Article([
  764. 'title' => 'My title',
  765. 'comments' => [
  766. new Entity(['comment' => 'First comment']),
  767. new Entity(['comment' => 'Second comment']),
  768. ]
  769. ]);
  770. $context = new EntityContext($this->request, [
  771. 'entity' => $row,
  772. 'table' => 'Articles',
  773. 'validator' => [
  774. 'Articles' => 'create',
  775. 'Comments' => 'custom'
  776. ]
  777. ]);
  778. $this->assertTrue($context->isRequired('title'));
  779. $this->assertFalse($context->isRequired('body'));
  780. $this->assertTrue($context->isRequired('comments.0.comment'));
  781. $this->assertTrue($context->isRequired('comments.1.comment'));
  782. }
  783. /**
  784. * Test isRequired on associated entities.
  785. *
  786. * @return void
  787. */
  788. public function testIsRequiredAssociatedBelongsTo()
  789. {
  790. $this->_setupTables();
  791. $row = new Article([
  792. 'title' => 'My title',
  793. 'user' => new Entity(['username' => 'Mark']),
  794. ]);
  795. $context = new EntityContext($this->request, [
  796. 'entity' => $row,
  797. 'table' => 'Articles',
  798. 'validator' => [
  799. 'Articles' => 'create',
  800. 'Users' => 'custom'
  801. ]
  802. ]);
  803. $this->assertTrue($context->isRequired('user.username'));
  804. $this->assertFalse($context->isRequired('user.first_name'));
  805. }
  806. /**
  807. * Test type() basic
  808. *
  809. * @return void
  810. */
  811. public function testType()
  812. {
  813. $this->_setupTables();
  814. $row = new Article([
  815. 'title' => 'My title',
  816. 'body' => 'Some content',
  817. ]);
  818. $context = new EntityContext($this->request, [
  819. 'entity' => $row,
  820. 'table' => 'Articles',
  821. ]);
  822. $this->assertEquals('string', $context->type('title'));
  823. $this->assertEquals('text', $context->type('body'));
  824. $this->assertEquals('integer', $context->type('user_id'));
  825. $this->assertNull($context->type('nope'));
  826. }
  827. /**
  828. * Test getting types for associated records.
  829. *
  830. * @return void
  831. */
  832. public function testTypeAssociated()
  833. {
  834. $this->_setupTables();
  835. $row = new Article([
  836. 'title' => 'My title',
  837. 'user' => new Entity(['username' => 'Mark']),
  838. ]);
  839. $context = new EntityContext($this->request, [
  840. 'entity' => $row,
  841. 'table' => 'Articles',
  842. ]);
  843. $this->assertEquals('string', $context->type('user.username'));
  844. $this->assertEquals('text', $context->type('user.bio'));
  845. $this->assertNull($context->type('user.nope'));
  846. }
  847. /**
  848. * Test attributes for fields.
  849. *
  850. * @return void
  851. */
  852. public function testAttributes()
  853. {
  854. $this->_setupTables();
  855. $row = new Article([
  856. 'title' => 'My title',
  857. 'user' => new Entity(['username' => 'Mark']),
  858. ]);
  859. $context = new EntityContext($this->request, [
  860. 'entity' => $row,
  861. 'table' => 'Articles',
  862. ]);
  863. $expected = [
  864. 'length' => 255, 'precision' => null
  865. ];
  866. $this->assertEquals($expected, $context->attributes('title'));
  867. $expected = [
  868. 'length' => null, 'precision' => null
  869. ];
  870. $this->assertEquals($expected, $context->attributes('body'));
  871. $expected = [
  872. 'length' => 10, 'precision' => 3
  873. ];
  874. $this->assertEquals($expected, $context->attributes('user.rating'));
  875. }
  876. /**
  877. * Test hasError
  878. *
  879. * @return void
  880. */
  881. public function testHasError()
  882. {
  883. $this->_setupTables();
  884. $row = new Article([
  885. 'title' => 'My title',
  886. 'user' => new Entity(['username' => 'Mark']),
  887. ]);
  888. $row->errors('title', []);
  889. $row->errors('body', 'Gotta have one');
  890. $row->errors('user_id', ['Required field']);
  891. $context = new EntityContext($this->request, [
  892. 'entity' => $row,
  893. 'table' => 'Articles',
  894. ]);
  895. $this->assertFalse($context->hasError('title'));
  896. $this->assertFalse($context->hasError('nope'));
  897. $this->assertTrue($context->hasError('body'));
  898. $this->assertTrue($context->hasError('user_id'));
  899. }
  900. /**
  901. * Test hasError on associated records
  902. *
  903. * @return void
  904. */
  905. public function testHasErrorAssociated()
  906. {
  907. $this->_setupTables();
  908. $row = new Article([
  909. 'title' => 'My title',
  910. 'user' => new Entity(['username' => 'Mark']),
  911. ]);
  912. $row->errors('title', []);
  913. $row->errors('body', 'Gotta have one');
  914. $row->user->errors('username', ['Required']);
  915. $context = new EntityContext($this->request, [
  916. 'entity' => $row,
  917. 'table' => 'Articles',
  918. ]);
  919. $this->assertTrue($context->hasError('user.username'));
  920. $this->assertFalse($context->hasError('user.nope'));
  921. $this->assertFalse($context->hasError('no.nope'));
  922. }
  923. /**
  924. * Test error
  925. *
  926. * @return void
  927. */
  928. public function testError()
  929. {
  930. $this->_setupTables();
  931. $row = new Article([
  932. 'title' => 'My title',
  933. 'user' => new Entity(['username' => 'Mark']),
  934. ]);
  935. $row->errors('title', []);
  936. $row->errors('body', 'Gotta have one');
  937. $row->errors('user_id', ['Required field']);
  938. $row->user->errors('username', ['Required']);
  939. $context = new EntityContext($this->request, [
  940. 'entity' => $row,
  941. 'table' => 'Articles',
  942. ]);
  943. $this->assertEquals([], $context->error('title'));
  944. $expected = ['Gotta have one'];
  945. $this->assertEquals($expected, $context->error('body'));
  946. $expected = ['Required'];
  947. $this->assertEquals($expected, $context->error('user.username'));
  948. }
  949. /**
  950. * Test error on associated entities.
  951. *
  952. * @return void
  953. */
  954. public function testErrorAssociatedHasMany()
  955. {
  956. $this->_setupTables();
  957. $comments = TableRegistry::get('Comments');
  958. $row = new Article([
  959. 'title' => 'My title',
  960. 'comments' => [
  961. new Entity(['comment' => '']),
  962. new Entity(['comment' => 'Second comment']),
  963. ]
  964. ]);
  965. $row->comments[0]->errors('comment', ['Is required']);
  966. $row->comments[0]->errors('article_id', ['Is required']);
  967. $context = new EntityContext($this->request, [
  968. 'entity' => $row,
  969. 'table' => 'Articles',
  970. 'validator' => 'default',
  971. ]);
  972. $this->assertEquals([], $context->error('title'));
  973. $this->assertEquals([], $context->error('comments.0.user_id'));
  974. $this->assertEquals([], $context->error('comments.0'));
  975. $this->assertEquals(['Is required'], $context->error('comments.0.comment'));
  976. $this->assertEquals(['Is required'], $context->error('comments.0.article_id'));
  977. $this->assertEquals([], $context->error('comments.1'));
  978. $this->assertEquals([], $context->error('comments.1.comment'));
  979. $this->assertEquals([], $context->error('comments.1.article_id'));
  980. }
  981. /**
  982. * Setup tables for tests.
  983. *
  984. * @return void
  985. */
  986. protected function _setupTables()
  987. {
  988. $articles = TableRegistry::get('Articles');
  989. $articles->belongsTo('Users');
  990. $articles->hasMany('Comments');
  991. $articles->entityClass(__NAMESPACE__ . '\Article');
  992. $comments = TableRegistry::get('Comments');
  993. $users = TableRegistry::get('Users');
  994. $users->hasMany('Articles');
  995. $articles->schema([
  996. 'id' => ['type' => 'integer', 'length' => 11, 'null' => false],
  997. 'title' => ['type' => 'string', 'length' => 255],
  998. 'user_id' => ['type' => 'integer', 'length' => 11, 'null' => false],
  999. 'body' => ['type' => 'crazy_text', 'baseType' => 'text']
  1000. ]);
  1001. $users->schema([
  1002. 'id' => ['type' => 'integer', 'length' => 11],
  1003. 'username' => ['type' => 'string', 'length' => 255],
  1004. 'bio' => ['type' => 'text'],
  1005. 'rating' => ['type' => 'decimal', 'length' => 10, 'precision' => 3],
  1006. ]);
  1007. $validator = new Validator();
  1008. $validator->add('title', 'minlength', [
  1009. 'rule' => ['minlength', 10]
  1010. ])
  1011. ->add('body', 'maxlength', [
  1012. 'rule' => ['maxlength', 1000]
  1013. ])->allowEmpty('body');
  1014. $articles->validator('create', $validator);
  1015. $validator = new Validator();
  1016. $validator->add('username', 'length', [
  1017. 'rule' => ['minlength', 10]
  1018. ]);
  1019. $users->validator('custom', $validator);
  1020. $validator = new Validator();
  1021. $validator->add('comment', 'length', [
  1022. 'rule' => ['minlength', 10]
  1023. ]);
  1024. $comments->validator('custom', $validator);
  1025. }
  1026. /**
  1027. * Test the fieldnames method.
  1028. *
  1029. * @return void
  1030. */
  1031. public function testFieldNames()
  1032. {
  1033. $context = new EntityContext($this->request, [
  1034. 'entity' => new Entity(),
  1035. 'table' => 'Articles',
  1036. ]);
  1037. $articles = TableRegistry::get('Articles');
  1038. $this->assertEquals($articles->schema()->columns(), $context->fieldNames());
  1039. }
  1040. /**
  1041. * Test automatic entity provider setting
  1042. *
  1043. * @return void
  1044. */
  1045. public function testValidatorEntityProvider()
  1046. {
  1047. $row = new Article([
  1048. 'title' => 'Test entity',
  1049. 'body' => 'Something new'
  1050. ]);
  1051. $context = new EntityContext($this->request, [
  1052. 'entity' => $row,
  1053. 'table' => 'Articles',
  1054. ]);
  1055. $context->isRequired('title');
  1056. $articles = TableRegistry::get('Articles');
  1057. $this->assertSame($row, $articles->validator()->provider('entity'));
  1058. $row = new Article([
  1059. 'title' => 'First post',
  1060. 'user' => new Entity([
  1061. 'username' => 'mark',
  1062. 'fname' => 'Mark',
  1063. 'articles' => [
  1064. new Article(['title' => 'First post']),
  1065. new Article(['title' => 'Second post']),
  1066. ]
  1067. ]),
  1068. ]);
  1069. $context = new EntityContext($this->request, [
  1070. 'entity' => $row,
  1071. 'table' => 'Articles',
  1072. ]);
  1073. $validator = $articles->validator();
  1074. $context->isRequired('user.articles.0.title');
  1075. $this->assertSame($row->user->articles[0], $validator->provider('entity'));
  1076. $context->isRequired('user.articles.1.title');
  1077. $this->assertSame($row->user->articles[1], $validator->provider('entity'));
  1078. $context->isRequired('title');
  1079. $this->assertSame($row, $validator->provider('entity'));
  1080. }
  1081. }