CompositeKeysTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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\Database\Driver\Sqlite;
  17. use Cake\Database\Driver\Sqlserver;
  18. use Cake\Datasource\ConnectionManager;
  19. use Cake\ORM\Entity;
  20. use Cake\ORM\Marshaller;
  21. use Cake\ORM\Query;
  22. use Cake\ORM\Table;
  23. use Cake\TestSuite\TestCase;
  24. use PDO;
  25. /**
  26. * Test entity for mass assignment.
  27. */
  28. class OpenArticleEntity extends Entity
  29. {
  30. protected $_accessible = [
  31. '*' => true,
  32. ];
  33. }
  34. /**
  35. * Integration tests for table operations involving composite keys
  36. */
  37. class CompositeKeyTest extends TestCase
  38. {
  39. /**
  40. * Fixture to be used
  41. *
  42. * @var array
  43. */
  44. public $fixtures = [
  45. 'core.CompositeIncrements',
  46. 'core.SiteArticles',
  47. 'core.SiteArticlesTags',
  48. 'core.SiteAuthors',
  49. 'core.SiteTags',
  50. ];
  51. /**
  52. * setUp method
  53. *
  54. * @return void
  55. */
  56. public function setUp()
  57. {
  58. parent::setUp();
  59. $this->connection = ConnectionManager::get('test');
  60. }
  61. /**
  62. * Data provider for the two types of strategies HasOne implements
  63. *
  64. * @return array
  65. */
  66. public function strategiesProviderHasOne()
  67. {
  68. return [['join'], ['select']];
  69. }
  70. /**
  71. * Data provider for the two types of strategies HasMany implements
  72. *
  73. * @return array
  74. */
  75. public function strategiesProviderHasMany()
  76. {
  77. return [['subquery'], ['select']];
  78. }
  79. /**
  80. * Data provider for the two types of strategies BelongsTo implements
  81. *
  82. * @return array
  83. */
  84. public function strategiesProviderBelongsTo()
  85. {
  86. return [['join'], ['select']];
  87. }
  88. /**
  89. * Data provider for the two types of strategies BelongsToMany implements
  90. *
  91. * @return array
  92. */
  93. public function strategiesProviderBelongsToMany()
  94. {
  95. return [['subquery'], ['select']];
  96. }
  97. /**
  98. * Test that you cannot save rows with composite keys if some columns are missing.
  99. *
  100. * @group save
  101. * @return void
  102. */
  103. public function testSaveNewErrorCompositeKeyNoIncrement()
  104. {
  105. $this->expectException(\RuntimeException::class);
  106. $this->expectExceptionMessage('Cannot insert row, some of the primary key values are missing');
  107. $articles = $this->getTableLocator()->get('SiteArticles');
  108. $article = $articles->newEntity(['site_id' => 1, 'author_id' => 1, 'title' => 'testing']);
  109. $articles->save($article);
  110. }
  111. /**
  112. * Test that saving into composite primary keys where one column is missing & autoIncrement works.
  113. *
  114. * SQLite is skipped because it doesn't support autoincrement composite keys.
  115. *
  116. * @group save
  117. * @return void
  118. */
  119. public function testSaveNewCompositeKeyIncrement()
  120. {
  121. $this->skipIfSqlite();
  122. $table = $this->getTableLocator()->get('CompositeIncrements');
  123. $thing = $table->newEntity(['account_id' => 3, 'name' => 'new guy']);
  124. $this->assertSame($thing, $table->save($thing));
  125. $this->assertNotEmpty($thing->id, 'Primary key should have been populated');
  126. $this->assertSame(3, $thing->account_id);
  127. }
  128. /**
  129. * Tests that HasMany associations are correctly eager loaded and results
  130. * correctly nested when multiple foreignKeys are used
  131. *
  132. * @dataProvider strategiesProviderHasMany
  133. * @return void
  134. */
  135. public function testHasManyEager($strategy)
  136. {
  137. $table = $this->getTableLocator()->get('SiteAuthors');
  138. $table->hasMany('SiteArticles', [
  139. 'propertyName' => 'articles',
  140. 'strategy' => $strategy,
  141. 'sort' => ['SiteArticles.id' => 'asc'],
  142. 'foreignKey' => ['author_id', 'site_id'],
  143. ]);
  144. $query = new Query($this->connection, $table);
  145. $results = $query->select()
  146. ->contain('SiteArticles')
  147. ->enableHydration(false)
  148. ->toArray();
  149. $expected = [
  150. [
  151. 'id' => 1,
  152. 'name' => 'mark',
  153. 'site_id' => 1,
  154. 'articles' => [
  155. [
  156. 'id' => 1,
  157. 'title' => 'First Article',
  158. 'body' => 'First Article Body',
  159. 'author_id' => 1,
  160. 'site_id' => 1,
  161. ],
  162. ],
  163. ],
  164. [
  165. 'id' => 2,
  166. 'name' => 'juan',
  167. 'site_id' => 2,
  168. 'articles' => [],
  169. ],
  170. [
  171. 'id' => 3,
  172. 'name' => 'jose',
  173. 'site_id' => 2,
  174. 'articles' => [
  175. [
  176. 'id' => 2,
  177. 'title' => 'Second Article',
  178. 'body' => 'Second Article Body',
  179. 'author_id' => 3,
  180. 'site_id' => 2,
  181. ],
  182. ],
  183. ],
  184. [
  185. 'id' => 4,
  186. 'name' => 'andy',
  187. 'site_id' => 1,
  188. 'articles' => [],
  189. ],
  190. ];
  191. $this->assertEquals($expected, $results);
  192. $results = $query->repository($table)
  193. ->select()
  194. ->contain(['SiteArticles' => ['conditions' => ['SiteArticles.id' => 2]]])
  195. ->enableHydration(false)
  196. ->toArray();
  197. $expected[0]['articles'] = [];
  198. $this->assertEquals($expected, $results);
  199. $this->assertEquals($table->getAssociation('SiteArticles')->getStrategy(), $strategy);
  200. }
  201. /**
  202. * Tests that BelongsToMany associations are correctly eager loaded when multiple
  203. * foreignKeys are used
  204. *
  205. * @dataProvider strategiesProviderBelongsToMany
  206. * @return void
  207. */
  208. public function testBelongsToManyEager($strategy)
  209. {
  210. $articles = $this->getTableLocator()->get('SiteArticles');
  211. $tags = $this->getTableLocator()->get('SiteTags');
  212. $junction = $this->getTableLocator()->get('SiteArticlesTags');
  213. $articles->belongsToMany('SiteTags', [
  214. 'strategy' => $strategy,
  215. 'targetTable' => $tags,
  216. 'propertyName' => 'tags',
  217. 'through' => 'SiteArticlesTags',
  218. 'sort' => ['SiteTags.id' => 'asc'],
  219. 'foreignKey' => ['article_id', 'site_id'],
  220. 'targetForeignKey' => ['tag_id', 'site_id'],
  221. ]);
  222. $query = new Query($this->connection, $articles);
  223. $results = $query->select()->contain('SiteTags')->enableHydration(false)->toArray();
  224. $expected = [
  225. [
  226. 'id' => 1,
  227. 'author_id' => 1,
  228. 'title' => 'First Article',
  229. 'body' => 'First Article Body',
  230. 'site_id' => 1,
  231. 'tags' => [
  232. [
  233. 'id' => 1,
  234. 'name' => 'tag1',
  235. '_joinData' => ['article_id' => 1, 'tag_id' => 1, 'site_id' => 1],
  236. 'site_id' => 1,
  237. ],
  238. [
  239. 'id' => 3,
  240. 'name' => 'tag3',
  241. '_joinData' => ['article_id' => 1, 'tag_id' => 3, 'site_id' => 1],
  242. 'site_id' => 1,
  243. ],
  244. ],
  245. ],
  246. [
  247. 'id' => 2,
  248. 'title' => 'Second Article',
  249. 'body' => 'Second Article Body',
  250. 'author_id' => 3,
  251. 'site_id' => 2,
  252. 'tags' => [
  253. [
  254. 'id' => 4,
  255. 'name' => 'tag4',
  256. '_joinData' => ['article_id' => 2, 'tag_id' => 4, 'site_id' => 2],
  257. 'site_id' => 2,
  258. ],
  259. ],
  260. ],
  261. [
  262. 'id' => 3,
  263. 'title' => 'Third Article',
  264. 'body' => 'Third Article Body',
  265. 'author_id' => 1,
  266. 'site_id' => 2,
  267. 'tags' => [],
  268. ],
  269. [
  270. 'id' => 4,
  271. 'title' => 'Fourth Article',
  272. 'body' => 'Fourth Article Body',
  273. 'author_id' => 3,
  274. 'site_id' => 1,
  275. 'tags' => [
  276. [
  277. 'id' => 1,
  278. 'name' => 'tag1',
  279. '_joinData' => ['article_id' => 4, 'tag_id' => 1, 'site_id' => 1],
  280. 'site_id' => 1,
  281. ],
  282. ],
  283. ],
  284. ];
  285. $this->assertEquals($expected, $results);
  286. $this->assertEquals($articles->getAssociation('SiteTags')->getStrategy(), $strategy);
  287. }
  288. /**
  289. * Tests loading belongsTo with composite keys
  290. *
  291. * @dataProvider strategiesProviderBelongsTo
  292. * @return void
  293. */
  294. public function testBelongsToEager($strategy)
  295. {
  296. $table = $this->getTableLocator()->get('SiteArticles');
  297. $table->belongsTo('SiteAuthors', [
  298. 'propertyName' => 'author',
  299. 'strategy' => $strategy,
  300. 'foreignKey' => ['author_id', 'site_id'],
  301. ]);
  302. $query = new Query($this->connection, $table);
  303. $results = $query->select()
  304. ->where(['SiteArticles.id IN' => [1, 2]])
  305. ->contain('SiteAuthors')
  306. ->enableHydration(false)
  307. ->toArray();
  308. $expected = [
  309. [
  310. 'id' => 1,
  311. 'author_id' => 1,
  312. 'site_id' => 1,
  313. 'title' => 'First Article',
  314. 'body' => 'First Article Body',
  315. 'author' => [
  316. 'id' => 1,
  317. 'name' => 'mark',
  318. 'site_id' => 1,
  319. ],
  320. ],
  321. [
  322. 'id' => 2,
  323. 'author_id' => 3,
  324. 'site_id' => 2,
  325. 'title' => 'Second Article',
  326. 'body' => 'Second Article Body',
  327. 'author' => [
  328. 'id' => 3,
  329. 'name' => 'jose',
  330. 'site_id' => 2,
  331. ],
  332. ],
  333. ];
  334. $this->assertEquals($expected, $results);
  335. }
  336. /**
  337. * Tests loading hasOne with composite keys
  338. *
  339. * @dataProvider strategiesProviderHasOne
  340. * @return void
  341. */
  342. public function testHasOneEager($strategy)
  343. {
  344. $table = $this->getTableLocator()->get('SiteAuthors');
  345. $table->hasOne('SiteArticles', [
  346. 'propertyName' => 'first_article',
  347. 'strategy' => $strategy,
  348. 'foreignKey' => ['author_id', 'site_id'],
  349. ]);
  350. $query = new Query($this->connection, $table);
  351. $results = $query->select()
  352. ->where(['SiteAuthors.id IN' => [1, 3]])
  353. ->contain('SiteArticles')
  354. ->enableHydration(false)
  355. ->toArray();
  356. $expected = [
  357. [
  358. 'id' => 1,
  359. 'name' => 'mark',
  360. 'site_id' => 1,
  361. 'first_article' => [
  362. 'id' => 1,
  363. 'author_id' => 1,
  364. 'site_id' => 1,
  365. 'title' => 'First Article',
  366. 'body' => 'First Article Body',
  367. ],
  368. ],
  369. [
  370. 'id' => 3,
  371. 'name' => 'jose',
  372. 'site_id' => 2,
  373. 'first_article' => [
  374. 'id' => 2,
  375. 'author_id' => 3,
  376. 'site_id' => 2,
  377. 'title' => 'Second Article',
  378. 'body' => 'Second Article Body',
  379. ],
  380. ],
  381. ];
  382. $this->assertEquals($expected, $results);
  383. }
  384. /**
  385. * Tests that it is possible to insert a new row using the save method
  386. * if the entity has composite primary key
  387. *
  388. * @group save
  389. * @return void
  390. */
  391. public function testSaveNewEntity()
  392. {
  393. $entity = new Entity([
  394. 'id' => 5,
  395. 'site_id' => 1,
  396. 'title' => 'Fifth Article',
  397. 'body' => 'Fifth Article Body',
  398. 'author_id' => 3,
  399. ]);
  400. $table = $this->getTableLocator()->get('SiteArticles');
  401. $this->assertSame($entity, $table->save($entity));
  402. $this->assertEquals($entity->id, 5);
  403. $row = $table->find('all')->where(['id' => 5, 'site_id' => 1])->first();
  404. $this->assertEquals($entity->toArray(), $row->toArray());
  405. }
  406. /**
  407. * Tests that it is possible to insert a new row using the save method
  408. * if the entity has composite primary key
  409. *
  410. * @group save
  411. * @return void
  412. */
  413. public function testSaveNewEntityMissingKey()
  414. {
  415. $this->expectException(\RuntimeException::class);
  416. $this->expectExceptionMessage('Cannot insert row, some of the primary key values are missing. Got (5, ), expecting (id, site_id)');
  417. $entity = new Entity([
  418. 'id' => 5,
  419. 'title' => 'Fifth Article',
  420. 'body' => 'Fifth Article Body',
  421. 'author_id' => 3,
  422. ]);
  423. $table = $this->getTableLocator()->get('SiteArticles');
  424. $table->save($entity);
  425. }
  426. /**
  427. * Test simple delete with composite primary key
  428. *
  429. * @return void
  430. */
  431. public function testDelete()
  432. {
  433. $table = $this->getTableLocator()->get('SiteAuthors');
  434. $table->save(new Entity(['id' => 1, 'site_id' => 2]));
  435. $entity = $table->get([1, 1]);
  436. $result = $table->delete($entity);
  437. $this->assertTrue($result);
  438. $this->assertEquals(4, $table->find('all')->count());
  439. $this->assertEmpty($table->find()->where(['id' => 1, 'site_id' => 1])->first());
  440. }
  441. /**
  442. * Test delete with dependent records having composite keys
  443. *
  444. * @return void
  445. */
  446. public function testDeleteDependent()
  447. {
  448. $table = $this->getTableLocator()->get('SiteAuthors');
  449. $table->hasMany('SiteArticles', [
  450. 'foreignKey' => ['author_id', 'site_id'],
  451. 'dependent' => true,
  452. ]);
  453. $entity = $table->get([3, 2]);
  454. $result = $table->delete($entity);
  455. $query = $table->getAssociation('SiteArticles')->find('all', [
  456. 'conditions' => [
  457. 'author_id' => $entity->id,
  458. 'site_id' => $entity->site_id,
  459. ],
  460. ]);
  461. $this->assertNull($query->all()->first(), 'Should not find any rows.');
  462. }
  463. /**
  464. * Test generating a list of entities from a list of composite ids
  465. *
  466. * @return void
  467. */
  468. public function testOneGenerateBelongsToManyEntitiesFromIds()
  469. {
  470. $articles = $this->getTableLocator()->get('SiteArticles');
  471. $articles->setEntityClass(__NAMESPACE__ . '\OpenArticleEntity');
  472. $tags = $this->getTableLocator()->get('SiteTags');
  473. $junction = $this->getTableLocator()->get('SiteArticlesTags');
  474. $articles->belongsToMany('SiteTags', [
  475. 'targetTable' => $tags,
  476. 'propertyName' => 'tags',
  477. 'through' => 'SiteArticlesTags',
  478. 'foreignKey' => ['article_id', 'site_id'],
  479. 'targetForeignKey' => ['tag_id', 'site_id'],
  480. ]);
  481. $data = [
  482. 'title' => 'Haz tags',
  483. 'body' => 'Some content here',
  484. 'tags' => ['_ids' => [[1, 1], [2, 2], [3, 1]]],
  485. ];
  486. $marshall = new Marshaller($articles);
  487. $result = $marshall->one($data, ['associated' => ['SiteTags']]);
  488. $this->assertCount(3, $result->tags);
  489. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
  490. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[1]);
  491. $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[2]);
  492. $data = [
  493. 'title' => 'Haz tags',
  494. 'body' => 'Some content here',
  495. 'tags' => ['_ids' => [1, 2, 3]],
  496. ];
  497. $marshall = new Marshaller($articles);
  498. $result = $marshall->one($data, ['associated' => ['SiteTags']]);
  499. $this->assertEmpty($result->tags);
  500. }
  501. /**
  502. * Tests find('list') with composite keys
  503. *
  504. * @return void
  505. */
  506. public function testFindListCompositeKeys()
  507. {
  508. $table = new Table([
  509. 'table' => 'site_authors',
  510. 'connection' => $this->connection,
  511. ]);
  512. $table->setDisplayField('name');
  513. $query = $table->find('list')
  514. ->enableHydration(false)
  515. ->order('id');
  516. $expected = [
  517. '1;1' => 'mark',
  518. '2;2' => 'juan',
  519. '3;2' => 'jose',
  520. '4;1' => 'andy',
  521. ];
  522. $this->assertEquals($expected, $query->toArray());
  523. $table->setDisplayField(['name', 'site_id']);
  524. $query = $table->find('list')
  525. ->enableHydration(false)
  526. ->order('id');
  527. $expected = [
  528. '1;1' => 'mark;1',
  529. '2;2' => 'juan;2',
  530. '3;2' => 'jose;2',
  531. '4;1' => 'andy;1',
  532. ];
  533. $this->assertEquals($expected, $query->toArray());
  534. $query = $table->find('list', ['groupField' => ['site_id', 'site_id']])
  535. ->enableHydration(false)
  536. ->order('id');
  537. $expected = [
  538. '1;1' => [
  539. '1;1' => 'mark;1',
  540. '4;1' => 'andy;1',
  541. ],
  542. '2;2' => [
  543. '2;2' => 'juan;2',
  544. '3;2' => 'jose;2',
  545. ],
  546. ];
  547. $this->assertEquals($expected, $query->toArray());
  548. }
  549. /**
  550. * Tests find('threaded') with composite keys
  551. *
  552. * @return void
  553. */
  554. public function testFindThreadedCompositeKeys()
  555. {
  556. $table = $this->getTableLocator()->get('SiteAuthors');
  557. $query = $this->getMockBuilder('\Cake\ORM\Query')
  558. ->setMethods(['_addDefaultFields', 'execute'])
  559. ->setConstructorArgs([null, $table])
  560. ->getMock();
  561. $items = new \Cake\Datasource\ResultSetDecorator([
  562. ['id' => 1, 'name' => 'a', 'site_id' => 1, 'parent_id' => null],
  563. ['id' => 2, 'name' => 'a', 'site_id' => 2, 'parent_id' => null],
  564. ['id' => 3, 'name' => 'a', 'site_id' => 1, 'parent_id' => 1],
  565. ['id' => 4, 'name' => 'a', 'site_id' => 2, 'parent_id' => 2],
  566. ['id' => 5, 'name' => 'a', 'site_id' => 2, 'parent_id' => 4],
  567. ['id' => 6, 'name' => 'a', 'site_id' => 1, 'parent_id' => 2],
  568. ['id' => 7, 'name' => 'a', 'site_id' => 1, 'parent_id' => 3],
  569. ['id' => 8, 'name' => 'a', 'site_id' => 2, 'parent_id' => 4],
  570. ]);
  571. $query->find('threaded', ['parentField' => ['parent_id', 'site_id']]);
  572. $formatter = $query->getResultFormatters()[0];
  573. $expected = [
  574. [
  575. 'id' => 1,
  576. 'name' => 'a',
  577. 'site_id' => 1,
  578. 'parent_id' => null,
  579. 'children' => [
  580. [
  581. 'id' => 3,
  582. 'name' => 'a',
  583. 'site_id' => 1,
  584. 'parent_id' => 1,
  585. 'children' => [
  586. [
  587. 'id' => 7,
  588. 'name' => 'a',
  589. 'site_id' => 1,
  590. 'parent_id' => 3,
  591. 'children' => [],
  592. ],
  593. ],
  594. ],
  595. ],
  596. ],
  597. [
  598. 'id' => 2,
  599. 'name' => 'a',
  600. 'site_id' => 2,
  601. 'parent_id' => null,
  602. 'children' => [
  603. [
  604. 'id' => 4,
  605. 'name' => 'a',
  606. 'site_id' => 2,
  607. 'parent_id' => 2,
  608. 'children' => [
  609. [
  610. 'id' => 5,
  611. 'name' => 'a',
  612. 'site_id' => 2,
  613. 'parent_id' => 4,
  614. 'children' => [],
  615. ],
  616. [
  617. 'id' => 8,
  618. 'name' => 'a',
  619. 'site_id' => 2,
  620. 'parent_id' => 4,
  621. 'children' => [],
  622. ],
  623. ],
  624. ],
  625. ],
  626. ],
  627. [
  628. 'id' => 6,
  629. 'name' => 'a',
  630. 'site_id' => 1,
  631. 'parent_id' => 2,
  632. 'children' => [],
  633. ],
  634. ];
  635. $this->assertEquals($expected, $formatter($items)->toArray());
  636. }
  637. /**
  638. * Tests that loadInto() is capable of handling composite primary keys
  639. *
  640. * @return void
  641. */
  642. public function testLoadInto()
  643. {
  644. $table = $this->getTableLocator()->get('SiteAuthors');
  645. $tags = $this->getTableLocator()->get('SiteTags');
  646. $table->hasMany('SiteArticles', [
  647. 'foreignKey' => ['author_id', 'site_id'],
  648. ]);
  649. $author = $table->get([1, 1]);
  650. $result = $table->loadInto($author, ['SiteArticles']);
  651. $this->assertSame($author, $result);
  652. $this->assertNotEmpty($result->site_articles);
  653. $expected = $table->get([1, 1], ['contain' => ['SiteArticles']]);
  654. $this->assertEquals($expected, $result);
  655. }
  656. /**
  657. * Tests that loadInto() is capable of handling composite primary keys
  658. * when loading belongsTo associations
  659. *
  660. * @return void
  661. */
  662. public function testLoadIntoWithBelongsTo()
  663. {
  664. $table = $this->getTableLocator()->get('SiteArticles');
  665. $table->belongsTo('SiteAuthors', [
  666. 'foreignKey' => ['author_id', 'site_id'],
  667. ]);
  668. $author = $table->get([2, 2]);
  669. $result = $table->loadInto($author, ['SiteAuthors']);
  670. $this->assertSame($author, $result);
  671. $this->assertNotEmpty($result->site_author);
  672. $expected = $table->get([2, 2], ['contain' => ['SiteAuthors']]);
  673. $this->assertEquals($expected, $result);
  674. }
  675. /**
  676. * Tests that loadInto() is capable of handling composite primary keys
  677. * when loading into multiple entities
  678. *
  679. * @return void
  680. */
  681. public function testLoadIntoMany()
  682. {
  683. $table = $this->getTableLocator()->get('SiteAuthors');
  684. $tags = $this->getTableLocator()->get('SiteTags');
  685. $table->hasMany('SiteArticles', [
  686. 'foreignKey' => ['author_id', 'site_id'],
  687. ]);
  688. $authors = $table->find()->toList();
  689. $result = $table->loadInto($authors, ['SiteArticles']);
  690. foreach ($authors as $k => $v) {
  691. $this->assertSame($result[$k], $v);
  692. }
  693. $expected = $table->find('all', ['contain' => ['SiteArticles']])->toList();
  694. $this->assertEquals($expected, $result);
  695. }
  696. /**
  697. * Tests notMatching() with a belongsToMany association
  698. *
  699. * @return void
  700. */
  701. public function testNotMatchingBelongsToMany()
  702. {
  703. $driver = $this->connection->getDriver();
  704. if ($driver instanceof Sqlserver) {
  705. $this->markTestSkipped('Sqlserver does not support the requirements of this test.');
  706. } elseif ($driver instanceof Sqlite) {
  707. $serverVersion = $driver->getConnection()->getAttribute(PDO::ATTR_SERVER_VERSION);
  708. if (version_compare($serverVersion, '3.15.0', '<')) {
  709. $this->markTestSkipped("Sqlite ($serverVersion) does not support the requirements of this test.");
  710. }
  711. }
  712. $articles = $this->getTableLocator()->get('SiteArticles');
  713. $articles->belongsToMany('SiteTags', [
  714. 'through' => 'SiteArticlesTags',
  715. 'foreignKey' => ['article_id', 'site_id'],
  716. 'targetForeignKey' => ['tag_id', 'site_id'],
  717. ]);
  718. $results = $articles->find()
  719. ->enableHydration(false)
  720. ->notMatching('SiteTags')
  721. ->toArray();
  722. $expected = [
  723. [
  724. 'id' => 3,
  725. 'author_id' => 1,
  726. 'site_id' => 2,
  727. 'title' => 'Third Article',
  728. 'body' => 'Third Article Body',
  729. ],
  730. ];
  731. $this->assertEquals($expected, $results);
  732. }
  733. /**
  734. * Helper method to skip tests when connection is SQLite.
  735. *
  736. * @return void
  737. */
  738. public function skipIfSqlite()
  739. {
  740. $this->skipIf(
  741. $this->connection->getDriver() instanceof Sqlite,
  742. 'SQLite does not support the requirements of this test.'
  743. );
  744. }
  745. }