EntityContextTest.php 34 KB

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