AssociationCollectionTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM;
  16. use Cake\ORM\AssociationCollection;
  17. use Cake\ORM\Association\BelongsTo;
  18. use Cake\ORM\Association\BelongsToMany;
  19. use Cake\ORM\Entity;
  20. use Cake\ORM\Locator\LocatorInterface;
  21. use Cake\ORM\TableRegistry;
  22. use Cake\TestSuite\TestCase;
  23. /**
  24. * AssociationCollection test case.
  25. */
  26. class AssociationCollectionTest extends TestCase
  27. {
  28. /**
  29. * @var AssociationCollection
  30. */
  31. public $associations;
  32. /**
  33. * setup
  34. *
  35. * @return void
  36. */
  37. public function setUp()
  38. {
  39. parent::setUp();
  40. $this->associations = new AssociationCollection();
  41. }
  42. /**
  43. * Test the constructor.
  44. *
  45. * @return void
  46. */
  47. public function testConstructor()
  48. {
  49. $this->assertSame(TableRegistry::getTableLocator(), $this->associations->getTableLocator());
  50. $tableLocator = $this->createMock(LocatorInterface::class);
  51. $associations = new AssociationCollection($tableLocator);
  52. $this->assertSame($tableLocator, $associations->getTableLocator());
  53. }
  54. /**
  55. * Test the simple add/has and get methods.
  56. *
  57. * @return void
  58. */
  59. public function testAddHasRemoveAndGet()
  60. {
  61. $this->assertFalse($this->associations->has('users'));
  62. $this->assertFalse($this->associations->has('Users'));
  63. $this->assertNull($this->associations->get('users'));
  64. $this->assertNull($this->associations->get('Users'));
  65. $belongsTo = new BelongsTo('');
  66. $this->assertSame($belongsTo, $this->associations->add('Users', $belongsTo));
  67. $this->assertTrue($this->associations->has('users'));
  68. $this->assertTrue($this->associations->has('Users'));
  69. $this->assertSame($belongsTo, $this->associations->get('users'));
  70. $this->assertSame($belongsTo, $this->associations->get('Users'));
  71. $this->assertNull($this->associations->remove('Users'));
  72. $this->assertFalse($this->associations->has('users'));
  73. $this->assertFalse($this->associations->has('Users'));
  74. $this->assertNull($this->associations->get('users'));
  75. $this->assertNull($this->associations->get('Users'));
  76. }
  77. /**
  78. * Test the load method.
  79. *
  80. * @return void
  81. */
  82. public function testLoad()
  83. {
  84. $this->associations->load(BelongsTo::class, 'Users');
  85. $this->assertTrue($this->associations->has('Users'));
  86. $this->assertInstanceOf(BelongsTo::class, $this->associations->get('Users'));
  87. $this->assertSame($this->associations->getTableLocator(), $this->associations->get('Users')->getTableLocator());
  88. }
  89. /**
  90. * Test the load method with custom locator.
  91. *
  92. * @return void
  93. */
  94. public function testLoadCustomLocator()
  95. {
  96. $locator = $this->createMock(LocatorInterface::class);
  97. $this->associations->load(BelongsTo::class, 'Users', [
  98. 'tableLocator' => $locator
  99. ]);
  100. $this->assertTrue($this->associations->has('Users'));
  101. $this->assertInstanceOf(BelongsTo::class, $this->associations->get('Users'));
  102. $this->assertSame($locator, $this->associations->get('Users')->getTableLocator());
  103. }
  104. /**
  105. * Test load invalid class.
  106. *
  107. * @return void
  108. * @expectedException InvalidArgumentException
  109. * @expectedExceptionMessage The association must extend `Cake\ORM\Association` class, `stdClass` given.
  110. */
  111. public function testLoadInvalid()
  112. {
  113. $this->associations->load('stdClass', 'Users');
  114. }
  115. /**
  116. * Test removeAll method
  117. *
  118. * @return void
  119. */
  120. public function testRemoveAll()
  121. {
  122. $this->assertEmpty($this->associations->keys());
  123. $belongsTo = new BelongsTo('');
  124. $this->assertSame($belongsTo, $this->associations->add('Users', $belongsTo));
  125. $belongsToMany = new BelongsToMany('');
  126. $this->assertSame($belongsToMany, $this->associations->add('Cart', $belongsToMany));
  127. $this->associations->removeAll();
  128. $this->assertEmpty($this->associations->keys());
  129. }
  130. /**
  131. * Test getting associations by property.
  132. *
  133. * @return void
  134. */
  135. public function testGetByProperty()
  136. {
  137. $table = $this->getMockBuilder('Cake\ORM\Table')
  138. ->setMethods(['table'])
  139. ->getMock();
  140. $table->setSchema([]);
  141. $belongsTo = new BelongsTo('Users', [
  142. 'sourceTable' => $table
  143. ]);
  144. $this->assertEquals('user', $belongsTo->getProperty());
  145. $this->associations->add('Users', $belongsTo);
  146. $this->assertNull($this->associations->get('user'));
  147. $this->assertSame($belongsTo, $this->associations->getByProperty('user'));
  148. }
  149. /**
  150. * Test associations with plugin names.
  151. *
  152. * @return void
  153. */
  154. public function testAddHasRemoveGetWithPlugin()
  155. {
  156. $this->assertFalse($this->associations->has('Photos.Photos'));
  157. $this->assertFalse($this->associations->has('Photos'));
  158. $belongsTo = new BelongsTo('');
  159. $this->assertSame($belongsTo, $this->associations->add('Photos.Photos', $belongsTo));
  160. $this->assertTrue($this->associations->has('Photos'));
  161. $this->assertFalse($this->associations->has('Photos.Photos'));
  162. }
  163. /**
  164. * Test keys()
  165. *
  166. * @return void
  167. */
  168. public function testKeys()
  169. {
  170. $belongsTo = new BelongsTo('');
  171. $this->associations->add('Users', $belongsTo);
  172. $this->associations->add('Categories', $belongsTo);
  173. $this->assertEquals(['users', 'categories'], $this->associations->keys());
  174. $this->associations->remove('Categories');
  175. $this->assertEquals(['users'], $this->associations->keys());
  176. }
  177. /**
  178. * Data provider for AssociationCollection::getByType
  179. */
  180. public function associationCollectionType()
  181. {
  182. return [
  183. ['BelongsTo', 'BelongsToMany'],
  184. ['belongsTo', 'belongsToMany'],
  185. ['belongsto', 'belongstomany']
  186. ];
  187. }
  188. /**
  189. * Test getting association names by getByType.
  190. *
  191. * @param string $belongsToStr
  192. * @param string $belongsToManyStr
  193. * @dataProvider associationCollectionType
  194. */
  195. public function testGetByType($belongsToStr, $belongsToManyStr)
  196. {
  197. $belongsTo = new BelongsTo('');
  198. $this->associations->add('Users', $belongsTo);
  199. $belongsToMany = new BelongsToMany('');
  200. $this->associations->add('Tags', $belongsToMany);
  201. $this->assertSame([$belongsTo], $this->associations->getByType($belongsToStr));
  202. $this->assertSame([$belongsToMany], $this->associations->getByType($belongsToManyStr));
  203. $this->assertSame([$belongsTo, $belongsToMany], $this->associations->getByType([$belongsToStr, $belongsToManyStr]));
  204. }
  205. /**
  206. * Type should return empty array.
  207. *
  208. * @return void
  209. */
  210. public function hasTypeReturnsEmptyArray()
  211. {
  212. foreach (['HasMany', 'hasMany', 'FooBar', 'DoesNotExist'] as $value) {
  213. $this->assertSame([], $this->associations->getByType($value));
  214. }
  215. }
  216. /**
  217. * test cascading deletes.
  218. *
  219. * @return void
  220. */
  221. public function testCascadeDelete()
  222. {
  223. $mockOne = $this->getMockBuilder('Cake\ORM\Association\BelongsTo')
  224. ->setConstructorArgs([''])
  225. ->getMock();
  226. $mockTwo = $this->getMockBuilder('Cake\ORM\Association\HasMany')
  227. ->setConstructorArgs([''])
  228. ->getMock();
  229. $entity = new Entity();
  230. $options = ['option' => 'value'];
  231. $this->associations->add('One', $mockOne);
  232. $this->associations->add('Two', $mockTwo);
  233. $mockOne->expects($this->once())
  234. ->method('cascadeDelete')
  235. ->with($entity, $options);
  236. $mockTwo->expects($this->once())
  237. ->method('cascadeDelete')
  238. ->with($entity, $options);
  239. $this->assertNull($this->associations->cascadeDelete($entity, $options));
  240. }
  241. /**
  242. * Test saving parent associations
  243. *
  244. * @return void
  245. */
  246. public function testSaveParents()
  247. {
  248. $table = $this->getMockBuilder('Cake\ORM\Table')
  249. ->setMethods(['table'])
  250. ->getMock();
  251. $table->setSchema([]);
  252. $mockOne = $this->getMockBuilder('Cake\ORM\Association\BelongsTo')
  253. ->setMethods(['saveAssociated'])
  254. ->setConstructorArgs(['Parent', [
  255. 'sourceTable' => $table,
  256. ]])
  257. ->getMock();
  258. $mockTwo = $this->getMockBuilder('Cake\ORM\Association\HasMany')
  259. ->setMethods(['saveAssociated'])
  260. ->setConstructorArgs(['Child', [
  261. 'sourceTable' => $table
  262. ]])
  263. ->getMock();
  264. $this->associations->add('Parent', $mockOne);
  265. $this->associations->add('Child', $mockTwo);
  266. $entity = new Entity();
  267. $entity->set('parent', ['key' => 'value']);
  268. $entity->set('child', ['key' => 'value']);
  269. $options = ['option' => 'value'];
  270. $mockOne->expects($this->once())
  271. ->method('saveAssociated')
  272. ->with($entity, $options)
  273. ->will($this->returnValue(true));
  274. $mockTwo->expects($this->never())
  275. ->method('saveAssociated');
  276. $result = $this->associations->saveParents(
  277. $table,
  278. $entity,
  279. ['Parent', 'Child'],
  280. $options
  281. );
  282. $this->assertTrue($result, 'Save should work.');
  283. }
  284. /**
  285. * Test saving filtered parent associations.
  286. *
  287. * @return void
  288. */
  289. public function testSaveParentsFiltered()
  290. {
  291. $table = $this->getMockBuilder('Cake\ORM\Table')
  292. ->setMethods(['table'])
  293. ->getMock();
  294. $table->setSchema([]);
  295. $mockOne = $this->getMockBuilder('Cake\ORM\Association\BelongsTo')
  296. ->setMethods(['saveAssociated'])
  297. ->setConstructorArgs(['Parents', [
  298. 'sourceTable' => $table,
  299. ]])
  300. ->getMock();
  301. $mockTwo = $this->getMockBuilder('Cake\ORM\Association\BelongsTo')
  302. ->setMethods(['saveAssociated'])
  303. ->setConstructorArgs(['Categories', [
  304. 'sourceTable' => $table
  305. ]])
  306. ->getMock();
  307. $this->associations->add('Parents', $mockOne);
  308. $this->associations->add('Categories', $mockTwo);
  309. $entity = new Entity();
  310. $entity->set('parent', ['key' => 'value']);
  311. $entity->set('category', ['key' => 'value']);
  312. $options = ['atomic' => true];
  313. $mockOne->expects($this->once())
  314. ->method('saveAssociated')
  315. ->with($entity, ['atomic' => true, 'associated' => ['Others']])
  316. ->will($this->returnValue(true));
  317. $mockTwo->expects($this->never())
  318. ->method('saveAssociated');
  319. $result = $this->associations->saveParents(
  320. $table,
  321. $entity,
  322. ['Parents' => ['associated' => ['Others']]],
  323. $options
  324. );
  325. $this->assertTrue($result, 'Save should work.');
  326. }
  327. /**
  328. * Test saving filtered child associations.
  329. *
  330. * @return void
  331. */
  332. public function testSaveChildrenFiltered()
  333. {
  334. $table = $this->getMockBuilder('Cake\ORM\Table')
  335. ->setMethods(['table'])
  336. ->getMock();
  337. $table->setSchema([]);
  338. $mockOne = $this->getMockBuilder('Cake\ORM\Association\HasMany')
  339. ->setMethods(['saveAssociated'])
  340. ->setConstructorArgs(['Comments', [
  341. 'sourceTable' => $table,
  342. ]])
  343. ->getMock();
  344. $mockTwo = $this->getMockBuilder('Cake\ORM\Association\HasOne')
  345. ->setMethods(['saveAssociated'])
  346. ->setConstructorArgs(['Profiles', [
  347. 'sourceTable' => $table
  348. ]])
  349. ->getMock();
  350. $this->associations->add('Comments', $mockOne);
  351. $this->associations->add('Profiles', $mockTwo);
  352. $entity = new Entity();
  353. $entity->set('comments', ['key' => 'value']);
  354. $entity->set('profile', ['key' => 'value']);
  355. $options = ['atomic' => true];
  356. $mockOne->expects($this->once())
  357. ->method('saveAssociated')
  358. ->with($entity, $options + ['associated' => ['Other']])
  359. ->will($this->returnValue(true));
  360. $mockTwo->expects($this->never())
  361. ->method('saveAssociated');
  362. $result = $this->associations->saveChildren(
  363. $table,
  364. $entity,
  365. ['Comments' => ['associated' => ['Other']]],
  366. $options
  367. );
  368. $this->assertTrue($result, 'Should succeed.');
  369. }
  370. /**
  371. * Test exceptional case.
  372. *
  373. */
  374. public function testErrorOnUnknownAlias()
  375. {
  376. $this->expectException(\InvalidArgumentException::class);
  377. $this->expectExceptionMessage('Cannot save Profiles, it is not associated to Users');
  378. $table = $this->getMockBuilder('Cake\ORM\Table')
  379. ->setMethods(['save'])
  380. ->setConstructorArgs([['alias' => 'Users']])
  381. ->getMock();
  382. $entity = new Entity();
  383. $entity->set('profile', ['key' => 'value']);
  384. $this->associations->saveChildren(
  385. $table,
  386. $entity,
  387. ['Profiles'],
  388. ['atomic' => true]
  389. );
  390. }
  391. /**
  392. * Tests the normalizeKeys method
  393. *
  394. * @return void
  395. */
  396. public function testNormalizeKeys()
  397. {
  398. $this->assertSame([], $this->associations->normalizeKeys([]));
  399. $this->assertSame([], $this->associations->normalizeKeys(false));
  400. $assocs = ['a', 'b', 'd' => ['something']];
  401. $expected = ['a' => [], 'b' => [], 'd' => ['something']];
  402. $this->assertSame($expected, $this->associations->normalizeKeys($assocs));
  403. $belongsTo = new BelongsTo('');
  404. $this->associations->add('users', $belongsTo);
  405. $this->associations->add('categories', $belongsTo);
  406. $expected = ['users' => [], 'categories' => []];
  407. $this->assertSame($expected, $this->associations->normalizeKeys(true));
  408. }
  409. /**
  410. * Ensure that the association collection can be iterated.
  411. *
  412. * @return void
  413. */
  414. public function testAssociationsCanBeIterated()
  415. {
  416. $belongsTo = new BelongsTo('');
  417. $this->associations->add('Users', $belongsTo);
  418. $belongsToMany = new BelongsToMany('');
  419. $this->associations->add('Cart', $belongsToMany);
  420. $expected = ['users' => $belongsTo, 'cart' => $belongsToMany];
  421. $result = iterator_to_array($this->associations, true);
  422. $this->assertSame($expected, $result);
  423. }
  424. }