EntityContextTest.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  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. 'author' => new Entity([
  406. 'roles' => ['admin', 'publisher']
  407. ])
  408. ]);
  409. $context = new EntityContext($this->request, [
  410. 'entity' => $row,
  411. 'table' => 'Articles',
  412. ]);
  413. $result = $context->val('types');
  414. $this->assertEquals($row->types, $result);
  415. $result = $context->val('author.roles');
  416. $this->assertEquals($row->author->roles, $result);
  417. }
  418. /**
  419. * Test that val() reads from the request.
  420. *
  421. * @return void
  422. */
  423. public function testValReadsRequest()
  424. {
  425. $this->request->data = [
  426. 'title' => 'New title',
  427. 'notInEntity' => 'yes',
  428. ];
  429. $row = new Article([
  430. 'title' => 'Test entity',
  431. 'body' => 'Something new'
  432. ]);
  433. $context = new EntityContext($this->request, [
  434. 'entity' => $row,
  435. 'table' => 'Articles',
  436. ]);
  437. $this->assertEquals('New title', $context->val('title'));
  438. $this->assertEquals('yes', $context->val('notInEntity'));
  439. $this->assertEquals($row->body, $context->val('body'));
  440. }
  441. /**
  442. * Test reading values from associated entities.
  443. *
  444. * @return void
  445. */
  446. public function testValAssociated()
  447. {
  448. $row = new Article([
  449. 'title' => 'Test entity',
  450. 'user' => new Entity([
  451. 'username' => 'mark',
  452. 'fname' => 'Mark'
  453. ]),
  454. 'comments' => [
  455. new Entity(['comment' => 'Test comment']),
  456. new Entity(['comment' => 'Second comment']),
  457. ]
  458. ]);
  459. $context = new EntityContext($this->request, [
  460. 'entity' => $row,
  461. 'table' => 'Articles',
  462. ]);
  463. $result = $context->val('user.fname');
  464. $this->assertEquals($row->user->fname, $result);
  465. $result = $context->val('comments.0.comment');
  466. $this->assertEquals($row->comments[0]->comment, $result);
  467. $result = $context->val('comments.1.comment');
  468. $this->assertEquals($row->comments[1]->comment, $result);
  469. $result = $context->val('comments.0.nope');
  470. $this->assertNull($result);
  471. $result = $context->val('comments.0.nope.no_way');
  472. $this->assertNull($result);
  473. }
  474. /**
  475. * Tests that trying to get values from missing associations returns null
  476. *
  477. * @return void
  478. */
  479. public function testValMissingAssociation()
  480. {
  481. $row = new Article([
  482. 'id' => 1
  483. ]);
  484. $context = new EntityContext($this->request, [
  485. 'entity' => $row,
  486. 'table' => 'Articles',
  487. ]);
  488. $result = $context->val('id');
  489. $this->assertEquals($row->id, $result);
  490. $this->assertNull($context->val('profile.id'));
  491. }
  492. /**
  493. * Test reading values from associated entities.
  494. *
  495. * @return void
  496. */
  497. public function testValAssociatedHasMany()
  498. {
  499. $row = new Article([
  500. 'title' => 'First post',
  501. 'user' => new Entity([
  502. 'username' => 'mark',
  503. 'fname' => 'Mark',
  504. 'articles' => [
  505. new Article(['title' => 'First post']),
  506. new Article(['title' => 'Second post']),
  507. ]
  508. ]),
  509. ]);
  510. $context = new EntityContext($this->request, [
  511. 'entity' => $row,
  512. 'table' => 'Articles',
  513. ]);
  514. $result = $context->val('user.articles.0.title');
  515. $this->assertEquals('First post', $result);
  516. $result = $context->val('user.articles.1.title');
  517. $this->assertEquals('Second post', $result);
  518. }
  519. /**
  520. * Test reading values for magic _ids input
  521. *
  522. * @return void
  523. */
  524. public function testValAssociatedDefaultIds()
  525. {
  526. $row = new Article([
  527. 'title' => 'First post',
  528. 'user' => new Entity([
  529. 'username' => 'mark',
  530. 'fname' => 'Mark',
  531. 'groups' => [
  532. new Entity(['title' => 'PHP', 'id' => 1]),
  533. new Entity(['title' => 'Javascript', 'id' => 2]),
  534. ]
  535. ]),
  536. ]);
  537. $context = new EntityContext($this->request, [
  538. 'entity' => $row,
  539. 'table' => 'Articles',
  540. ]);
  541. $result = $context->val('user.groups._ids');
  542. $this->assertEquals([1, 2], $result);
  543. }
  544. /**
  545. * Test reading values for magic _ids input
  546. *
  547. * @return void
  548. */
  549. public function testValAssociatedCustomIds()
  550. {
  551. $row = new Article([
  552. 'title' => 'First post',
  553. 'user' => new Entity([
  554. 'username' => 'mark',
  555. 'fname' => 'Mark',
  556. 'groups' => [
  557. new Entity(['title' => 'PHP', 'thing' => 1]),
  558. new Entity(['title' => 'Javascript', 'thing' => 4]),
  559. ]
  560. ]),
  561. ]);
  562. $context = new EntityContext($this->request, [
  563. 'entity' => $row,
  564. 'table' => 'Articles',
  565. ]);
  566. TableRegistry::get('Users')->belongsToMany('Groups');
  567. TableRegistry::get('Groups')->primaryKey('thing');
  568. $result = $context->val('user.groups._ids');
  569. $this->assertEquals([1, 4], $result);
  570. }
  571. /**
  572. * Test validator for boolean fields.
  573. *
  574. * @return void
  575. */
  576. public function testIsRequiredBooleanField()
  577. {
  578. $this->_setupTables();
  579. $context = new EntityContext($this->request, [
  580. 'entity' => new Entity(),
  581. 'table' => 'Articles',
  582. ]);
  583. $articles = TableRegistry::get('Articles');
  584. $articles->schema()->addColumn('comments_on', [
  585. 'type' => 'boolean'
  586. ]);
  587. $validator = $articles->validator();
  588. $validator->add('comments_on', 'is_bool', [
  589. 'rule' => 'boolean'
  590. ]);
  591. $articles->validator('default', $validator);
  592. $this->assertFalse($context->isRequired('title'));
  593. }
  594. /**
  595. * Test validator as a string.
  596. *
  597. * @return void
  598. */
  599. public function testIsRequiredStringValidator()
  600. {
  601. $this->_setupTables();
  602. $context = new EntityContext($this->request, [
  603. 'entity' => new Entity(),
  604. 'table' => 'Articles',
  605. 'validator' => 'create',
  606. ]);
  607. $this->assertTrue($context->isRequired('title'));
  608. $this->assertFalse($context->isRequired('body'));
  609. $this->assertFalse($context->isRequired('Herp.derp.derp'));
  610. $this->assertFalse($context->isRequired('nope'));
  611. $this->assertFalse($context->isRequired(''));
  612. }
  613. /**
  614. * Test isRequired on associated entities.
  615. *
  616. * @return void
  617. */
  618. public function testIsRequiredAssociatedHasMany()
  619. {
  620. $this->_setupTables();
  621. $comments = TableRegistry::get('Comments');
  622. $validator = $comments->validator();
  623. $validator->add('user_id', 'number', [
  624. 'rule' => 'numeric',
  625. ]);
  626. $row = new Article([
  627. 'title' => 'My title',
  628. 'comments' => [
  629. new Entity(['comment' => 'First comment']),
  630. new Entity(['comment' => 'Second comment']),
  631. ]
  632. ]);
  633. $context = new EntityContext($this->request, [
  634. 'entity' => $row,
  635. 'table' => 'Articles',
  636. 'validator' => 'default',
  637. ]);
  638. $this->assertTrue($context->isRequired('comments.0.user_id'));
  639. $this->assertFalse($context->isRequired('comments.0.other'));
  640. $this->assertFalse($context->isRequired('user.0.other'));
  641. $this->assertFalse($context->isRequired(''));
  642. }
  643. /**
  644. * Test isRequired on associated entities with custom validators.
  645. *
  646. * Ensures that missing associations use the correct entity class
  647. * so provider methods work correctly.
  648. *
  649. * @return void
  650. */
  651. public function testIsRequiredAssociatedCustomValidator()
  652. {
  653. $this->_setupTables();
  654. $users = TableRegistry::get('Users');
  655. $articles = TableRegistry::get('Articles');
  656. $validator = $articles->validator();
  657. $validator->notEmpty('title', 'nope', function ($context) {
  658. return $context['providers']['entity']->isRequired();
  659. });
  660. $articles->validator('default', $validator);
  661. $row = new Entity([
  662. 'username' => 'mark'
  663. ]);
  664. $context = new EntityContext($this->request, [
  665. 'entity' => $row,
  666. 'table' => 'Users',
  667. 'validator' => 'default',
  668. ]);
  669. $this->assertTrue($context->isRequired('articles.0.title'));
  670. }
  671. /**
  672. * Test isRequired on associated entities.
  673. *
  674. * @return void
  675. */
  676. public function testIsRequiredAssociatedHasManyMissingObject()
  677. {
  678. $this->_setupTables();
  679. $comments = TableRegistry::get('Comments');
  680. $validator = $comments->validator();
  681. $validator->allowEmpty('comment', function ($context) {
  682. return $context['providers']['entity']->isNew();
  683. });
  684. $row = new Article([
  685. 'title' => 'My title',
  686. 'comments' => [
  687. new Entity(['comment' => 'First comment'], ['markNew' => false]),
  688. ]
  689. ]);
  690. $context = new EntityContext($this->request, [
  691. 'entity' => $row,
  692. 'table' => 'Articles',
  693. 'validator' => 'default',
  694. ]);
  695. $this->assertTrue(
  696. $context->isRequired('comments.0.comment'),
  697. 'comment is required as object is not new'
  698. );
  699. $this->assertFalse(
  700. $context->isRequired('comments.1.comment'),
  701. 'comment is not required as missing object is "new"'
  702. );
  703. }
  704. /**
  705. * Test isRequired on associated entities with custom validators.
  706. *
  707. * @return void
  708. */
  709. public function testIsRequiredAssociatedValidator()
  710. {
  711. $this->_setupTables();
  712. $row = new Article([
  713. 'title' => 'My title',
  714. 'comments' => [
  715. new Entity(['comment' => 'First comment']),
  716. new Entity(['comment' => 'Second comment']),
  717. ]
  718. ]);
  719. $context = new EntityContext($this->request, [
  720. 'entity' => $row,
  721. 'table' => 'Articles',
  722. 'validator' => [
  723. 'Articles' => 'create',
  724. 'Comments' => 'custom'
  725. ]
  726. ]);
  727. $this->assertTrue($context->isRequired('title'));
  728. $this->assertFalse($context->isRequired('body'));
  729. $this->assertTrue($context->isRequired('comments.0.comment'));
  730. $this->assertTrue($context->isRequired('comments.1.comment'));
  731. }
  732. /**
  733. * Test isRequired on associated entities.
  734. *
  735. * @return void
  736. */
  737. public function testIsRequiredAssociatedBelongsTo()
  738. {
  739. $this->_setupTables();
  740. $row = new Article([
  741. 'title' => 'My title',
  742. 'user' => new Entity(['username' => 'Mark']),
  743. ]);
  744. $context = new EntityContext($this->request, [
  745. 'entity' => $row,
  746. 'table' => 'Articles',
  747. 'validator' => [
  748. 'Articles' => 'create',
  749. 'Users' => 'custom'
  750. ]
  751. ]);
  752. $this->assertTrue($context->isRequired('user.username'));
  753. $this->assertFalse($context->isRequired('user.first_name'));
  754. }
  755. /**
  756. * Test type() basic
  757. *
  758. * @return void
  759. */
  760. public function testType()
  761. {
  762. $this->_setupTables();
  763. $row = new Article([
  764. 'title' => 'My title',
  765. 'body' => 'Some content',
  766. ]);
  767. $context = new EntityContext($this->request, [
  768. 'entity' => $row,
  769. 'table' => 'Articles',
  770. ]);
  771. $this->assertEquals('string', $context->type('title'));
  772. $this->assertEquals('text', $context->type('body'));
  773. $this->assertEquals('integer', $context->type('user_id'));
  774. $this->assertNull($context->type('nope'));
  775. }
  776. /**
  777. * Test getting types for associated records.
  778. *
  779. * @return void
  780. */
  781. public function testTypeAssociated()
  782. {
  783. $this->_setupTables();
  784. $row = new Article([
  785. 'title' => 'My title',
  786. 'user' => new Entity(['username' => 'Mark']),
  787. ]);
  788. $context = new EntityContext($this->request, [
  789. 'entity' => $row,
  790. 'table' => 'Articles',
  791. ]);
  792. $this->assertEquals('string', $context->type('user.username'));
  793. $this->assertEquals('text', $context->type('user.bio'));
  794. $this->assertNull($context->type('user.nope'));
  795. }
  796. /**
  797. * Test attributes for fields.
  798. *
  799. * @return void
  800. */
  801. public function testAttributes()
  802. {
  803. $this->_setupTables();
  804. $row = new Article([
  805. 'title' => 'My title',
  806. 'user' => new Entity(['username' => 'Mark']),
  807. ]);
  808. $context = new EntityContext($this->request, [
  809. 'entity' => $row,
  810. 'table' => 'Articles',
  811. ]);
  812. $expected = [
  813. 'length' => 255, 'precision' => null
  814. ];
  815. $this->assertEquals($expected, $context->attributes('title'));
  816. $expected = [
  817. 'length' => null, 'precision' => null
  818. ];
  819. $this->assertEquals($expected, $context->attributes('body'));
  820. $expected = [
  821. 'length' => 10, 'precision' => 3
  822. ];
  823. $this->assertEquals($expected, $context->attributes('user.rating'));
  824. }
  825. /**
  826. * Test hasError
  827. *
  828. * @return void
  829. */
  830. public function testHasError()
  831. {
  832. $this->_setupTables();
  833. $row = new Article([
  834. 'title' => 'My title',
  835. 'user' => new Entity(['username' => 'Mark']),
  836. ]);
  837. $row->errors('title', []);
  838. $row->errors('body', 'Gotta have one');
  839. $row->errors('user_id', ['Required field']);
  840. $context = new EntityContext($this->request, [
  841. 'entity' => $row,
  842. 'table' => 'Articles',
  843. ]);
  844. $this->assertFalse($context->hasError('title'));
  845. $this->assertFalse($context->hasError('nope'));
  846. $this->assertTrue($context->hasError('body'));
  847. $this->assertTrue($context->hasError('user_id'));
  848. }
  849. /**
  850. * Test hasError on associated records
  851. *
  852. * @return void
  853. */
  854. public function testHasErrorAssociated()
  855. {
  856. $this->_setupTables();
  857. $row = new Article([
  858. 'title' => 'My title',
  859. 'user' => new Entity(['username' => 'Mark']),
  860. ]);
  861. $row->errors('title', []);
  862. $row->errors('body', 'Gotta have one');
  863. $row->user->errors('username', ['Required']);
  864. $context = new EntityContext($this->request, [
  865. 'entity' => $row,
  866. 'table' => 'Articles',
  867. ]);
  868. $this->assertTrue($context->hasError('user.username'));
  869. $this->assertFalse($context->hasError('user.nope'));
  870. $this->assertFalse($context->hasError('no.nope'));
  871. }
  872. /**
  873. * Test error
  874. *
  875. * @return void
  876. */
  877. public function testError()
  878. {
  879. $this->_setupTables();
  880. $row = new Article([
  881. 'title' => 'My title',
  882. 'user' => new Entity(['username' => 'Mark']),
  883. ]);
  884. $row->errors('title', []);
  885. $row->errors('body', 'Gotta have one');
  886. $row->errors('user_id', ['Required field']);
  887. $row->user->errors('username', ['Required']);
  888. $context = new EntityContext($this->request, [
  889. 'entity' => $row,
  890. 'table' => 'Articles',
  891. ]);
  892. $this->assertEquals([], $context->error('title'));
  893. $expected = ['Gotta have one'];
  894. $this->assertEquals($expected, $context->error('body'));
  895. $expected = ['Required'];
  896. $this->assertEquals($expected, $context->error('user.username'));
  897. }
  898. /**
  899. * Test error on associated entities.
  900. *
  901. * @return void
  902. */
  903. public function testErrorAssociatedHasMany()
  904. {
  905. $this->_setupTables();
  906. $comments = TableRegistry::get('Comments');
  907. $row = new Article([
  908. 'title' => 'My title',
  909. 'comments' => [
  910. new Entity(['comment' => '']),
  911. new Entity(['comment' => 'Second comment']),
  912. ]
  913. ]);
  914. $row->comments[0]->errors('comment', ['Is required']);
  915. $row->comments[0]->errors('article_id', ['Is required']);
  916. $context = new EntityContext($this->request, [
  917. 'entity' => $row,
  918. 'table' => 'Articles',
  919. 'validator' => 'default',
  920. ]);
  921. $this->assertEquals([], $context->error('title'));
  922. $this->assertEquals([], $context->error('comments.0.user_id'));
  923. $this->assertEquals([], $context->error('comments.0'));
  924. $this->assertEquals(['Is required'], $context->error('comments.0.comment'));
  925. $this->assertEquals(['Is required'], $context->error('comments.0.article_id'));
  926. $this->assertEquals([], $context->error('comments.1'));
  927. $this->assertEquals([], $context->error('comments.1.comment'));
  928. $this->assertEquals([], $context->error('comments.1.article_id'));
  929. }
  930. /**
  931. * Setup tables for tests.
  932. *
  933. * @return void
  934. */
  935. protected function _setupTables()
  936. {
  937. $articles = TableRegistry::get('Articles');
  938. $articles->belongsTo('Users');
  939. $articles->hasMany('Comments');
  940. $articles->entityClass(__NAMESPACE__ . '\Article');
  941. $comments = TableRegistry::get('Comments');
  942. $users = TableRegistry::get('Users');
  943. $users->hasMany('Articles');
  944. $articles->schema([
  945. 'id' => ['type' => 'integer', 'length' => 11, 'null' => false],
  946. 'title' => ['type' => 'string', 'length' => 255],
  947. 'user_id' => ['type' => 'integer', 'length' => 11, 'null' => false],
  948. 'body' => ['type' => 'text']
  949. ]);
  950. $users->schema([
  951. 'id' => ['type' => 'integer', 'length' => 11],
  952. 'username' => ['type' => 'string', 'length' => 255],
  953. 'bio' => ['type' => 'text'],
  954. 'rating' => ['type' => 'decimal', 'length' => 10, 'precision' => 3],
  955. ]);
  956. $validator = new Validator();
  957. $validator->add('title', 'minlength', [
  958. 'rule' => ['minlength', 10]
  959. ])
  960. ->add('body', 'maxlength', [
  961. 'rule' => ['maxlength', 1000]
  962. ])->allowEmpty('body');
  963. $articles->validator('create', $validator);
  964. $validator = new Validator();
  965. $validator->add('username', 'length', [
  966. 'rule' => ['minlength', 10]
  967. ]);
  968. $users->validator('custom', $validator);
  969. $validator = new Validator();
  970. $validator->add('comment', 'length', [
  971. 'rule' => ['minlength', 10]
  972. ]);
  973. $comments->validator('custom', $validator);
  974. }
  975. /**
  976. * Test the fieldnames method.
  977. *
  978. * @return void
  979. */
  980. public function testFieldNames()
  981. {
  982. $context = new EntityContext($this->request, [
  983. 'entity' => new Entity(),
  984. 'table' => 'Articles',
  985. ]);
  986. $articles = TableRegistry::get('Articles');
  987. $this->assertEquals($articles->schema()->columns(), $context->fieldNames());
  988. }
  989. /**
  990. * Test automatic entity provider setting
  991. *
  992. * @return void
  993. */
  994. public function testValidatorEntityProvider()
  995. {
  996. $row = new Article([
  997. 'title' => 'Test entity',
  998. 'body' => 'Something new'
  999. ]);
  1000. $context = new EntityContext($this->request, [
  1001. 'entity' => $row,
  1002. 'table' => 'Articles',
  1003. ]);
  1004. $context->isRequired('title');
  1005. $articles = TableRegistry::get('Articles');
  1006. $this->assertSame($row, $articles->validator()->provider('entity'));
  1007. $row = new Article([
  1008. 'title' => 'First post',
  1009. 'user' => new Entity([
  1010. 'username' => 'mark',
  1011. 'fname' => 'Mark',
  1012. 'articles' => [
  1013. new Article(['title' => 'First post']),
  1014. new Article(['title' => 'Second post']),
  1015. ]
  1016. ]),
  1017. ]);
  1018. $context = new EntityContext($this->request, [
  1019. 'entity' => $row,
  1020. 'table' => 'Articles',
  1021. ]);
  1022. $validator = $articles->validator();
  1023. $context->isRequired('user.articles.0.title');
  1024. $this->assertSame($row->user->articles[0], $validator->provider('entity'));
  1025. $context->isRequired('user.articles.1.title');
  1026. $this->assertSame($row->user->articles[1], $validator->provider('entity'));
  1027. $context->isRequired('title');
  1028. $this->assertSame($row, $validator->provider('entity'));
  1029. }
  1030. }