QueryRegressionTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  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\ORM;
  16. use Cake\Core\Plugin;
  17. use Cake\I18n\Time;
  18. use Cake\ORM\Query;
  19. use Cake\ORM\Table;
  20. use Cake\ORM\TableRegistry;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * Contains regression test for the Query builder
  24. *
  25. */
  26. class QueryRegressionTest extends TestCase
  27. {
  28. /**
  29. * Fixture to be used
  30. *
  31. * @var array
  32. */
  33. public $fixtures = [
  34. 'core.users',
  35. 'core.articles',
  36. 'core.comments',
  37. 'core.tags',
  38. 'core.articles_tags',
  39. 'core.authors',
  40. 'core.special_tags',
  41. 'core.translates',
  42. 'core.authors_tags',
  43. ];
  44. /**
  45. * Tear down
  46. *
  47. * @return void
  48. */
  49. public function tearDown()
  50. {
  51. parent::tearDown();
  52. TableRegistry::clear();
  53. }
  54. /**
  55. * Test for https://github.com/cakephp/cakephp/issues/3087
  56. *
  57. * @return void
  58. */
  59. public function testSelectTimestampColumn()
  60. {
  61. $table = TableRegistry::get('users');
  62. $user = $table->find()->where(['id' => 1])->first();
  63. $this->assertEquals(new Time('2007-03-17 01:16:23'), $user->created);
  64. $this->assertEquals(new Time('2007-03-17 01:18:31'), $user->updated);
  65. }
  66. /**
  67. * Tests that EagerLoader does not try to create queries for associations having no
  68. * keys to compare against
  69. *
  70. * @return void
  71. */
  72. public function testEagerLoadingFromEmptyResults()
  73. {
  74. $table = TableRegistry::get('Articles');
  75. $table->belongsToMany('ArticlesTags');
  76. $results = $table->find()->where(['id >' => 100])->contain('ArticlesTags')->toArray();
  77. $this->assertEmpty($results);
  78. }
  79. /**
  80. * Tests that eagerloading belongsToMany with find list fails with a helpful message.
  81. *
  82. * @expectedException \RuntimeException
  83. * @return void
  84. */
  85. public function testEagerLoadingBelongsToManyList()
  86. {
  87. $table = TableRegistry::get('Articles');
  88. $table->belongsToMany('Tags', [
  89. 'finder' => 'list'
  90. ]);
  91. $table->find()->contain('Tags')->toArray();
  92. }
  93. /**
  94. * Tests that duplicate aliases in contain() can be used, even when they would
  95. * naturally be attached to the query instead of eagerly loaded. What should
  96. * happen here is that One of the duplicates will be changed to be loaded using
  97. * an extra query, but yielding the same results
  98. *
  99. * @return void
  100. */
  101. public function testDuplicateAttachableAliases()
  102. {
  103. TableRegistry::get('Stuff', ['table' => 'tags']);
  104. TableRegistry::get('Things', ['table' => 'articles_tags']);
  105. $table = TableRegistry::get('Articles');
  106. $table->belongsTo('Authors');
  107. $table->hasOne('Things', ['propertyName' => 'articles_tag']);
  108. $table->Authors->target()->hasOne('Stuff', [
  109. 'foreignKey' => 'id',
  110. 'propertyName' => 'favorite_tag'
  111. ]);
  112. $table->Things->target()->belongsTo('Stuff', [
  113. 'foreignKey' => 'tag_id',
  114. 'propertyName' => 'foo'
  115. ]);
  116. $results = $table->find()
  117. ->contain(['Authors.Stuff', 'Things.Stuff'])
  118. ->order(['Articles.id' => 'ASC'])
  119. ->toArray();
  120. $this->assertEquals(1, $results[0]->articles_tag->foo->id);
  121. $this->assertEquals(1, $results[0]->author->favorite_tag->id);
  122. $this->assertEquals(2, $results[1]->articles_tag->foo->id);
  123. $this->assertEquals(1, $results[0]->author->favorite_tag->id);
  124. $this->assertEquals(1, $results[2]->articles_tag->foo->id);
  125. $this->assertEquals(3, $results[2]->author->favorite_tag->id);
  126. $this->assertEquals(3, $results[3]->articles_tag->foo->id);
  127. $this->assertEquals(3, $results[3]->author->favorite_tag->id);
  128. }
  129. /**
  130. * Test for https://github.com/cakephp/cakephp/issues/3410
  131. *
  132. * @return void
  133. */
  134. public function testNullableTimeColumn()
  135. {
  136. $table = TableRegistry::get('users');
  137. $entity = $table->newEntity(['username' => 'derp', 'created' => null]);
  138. $this->assertSame($entity, $table->save($entity));
  139. $this->assertNull($entity->created);
  140. }
  141. /**
  142. * Test for https://github.com/cakephp/cakephp/issues/3626
  143. *
  144. * Checks that join data is actually created and not tried to be updated every time
  145. * @return void
  146. */
  147. public function testCreateJointData()
  148. {
  149. $articles = TableRegistry::get('Articles');
  150. $articles->belongsToMany('Highlights', [
  151. 'className' => 'TestApp\Model\Table\TagsTable',
  152. 'foreignKey' => 'article_id',
  153. 'targetForeignKey' => 'tag_id',
  154. 'through' => 'SpecialTags'
  155. ]);
  156. $entity = $articles->get(2);
  157. $data = [
  158. 'id' => 2,
  159. 'highlights' => [
  160. [
  161. 'name' => 'New Special Tag',
  162. '_joinData' => ['highlighted' => true, 'highlighted_time' => '2014-06-01 10:10:00']
  163. ]
  164. ]
  165. ];
  166. $entity = $articles->patchEntity($entity, $data, ['Highlights._joinData']);
  167. $articles->save($entity);
  168. $entity = $articles->get(2, ['contain' => ['Highlights']]);
  169. $this->assertEquals(4, $entity->highlights[0]->_joinData->tag_id);
  170. $this->assertEquals('2014-06-01', $entity->highlights[0]->_joinData->highlighted_time->format('Y-m-d'));
  171. }
  172. /**
  173. * Tests that the junction table instance taken from both sides of a belongsToMany
  174. * relationship is actually the same object.
  175. *
  176. * @return void
  177. */
  178. public function testReciprocalBelongsToMany()
  179. {
  180. $articles = TableRegistry::get('Articles');
  181. $tags = TableRegistry::get('Tags');
  182. $articles->belongsToMany('Tags');
  183. $tags->belongsToMany('Articles');
  184. $left = $articles->Tags->junction();
  185. $right = $tags->Articles->junction();
  186. $this->assertSame($left, $right);
  187. }
  188. /**
  189. * Test for https://github.com/cakephp/cakephp/issues/4253
  190. *
  191. * Makes sure that the belongsToMany association is not overwritten with conflicting information
  192. * by any of the sides when the junction() function is invoked
  193. *
  194. * @return void
  195. */
  196. public function testReciprocalBelongsToMany2()
  197. {
  198. $articles = TableRegistry::get('Articles');
  199. $tags = TableRegistry::get('Tags');
  200. $articles->belongsToMany('Tags');
  201. $tags->belongsToMany('Articles');
  202. $sub = $articles->Tags->find()->select(['id'])->matching('Articles', function ($q) {
  203. return $q->where(['Articles.id' => 1]);
  204. });
  205. $query = $articles->Tags->find()->where(['id NOT IN' => $sub]);
  206. $this->assertEquals(1, $query->count());
  207. }
  208. /**
  209. * Returns an array with the saving strategies for a belongsTo association
  210. *
  211. * @return array
  212. */
  213. public function strategyProvider()
  214. {
  215. return [['append', 'replace']];
  216. }
  217. /**
  218. * Test for https://github.com/cakephp/cakephp/issues/3677 and
  219. * https://github.com/cakephp/cakephp/issues/3714
  220. *
  221. * Checks that only relevant associations are passed when saving _joinData
  222. * Tests that _joinData can also save deeper associations
  223. *
  224. * @dataProvider strategyProvider
  225. * @param string $strategy
  226. * @return void
  227. */
  228. public function testBelongsToManyDeepSave($strategy)
  229. {
  230. $articles = TableRegistry::get('Articles');
  231. $articles->belongsToMany('Highlights', [
  232. 'className' => 'TestApp\Model\Table\TagsTable',
  233. 'foreignKey' => 'article_id',
  234. 'targetForeignKey' => 'tag_id',
  235. 'through' => 'SpecialTags',
  236. 'saveStrategy' => $strategy
  237. ]);
  238. $articles->Highlights->junction()->belongsTo('Authors');
  239. $articles->Highlights->hasOne('Authors', [
  240. 'foreignKey' => 'id'
  241. ]);
  242. $entity = $articles->get(2, ['contain' => ['Highlights']]);
  243. $data = [
  244. 'highlights' => [
  245. [
  246. 'name' => 'New Special Tag',
  247. '_joinData' => [
  248. 'highlighted' => true,
  249. 'highlighted_time' => '2014-06-01 10:10:00',
  250. 'author' => [
  251. 'name' => 'mariano'
  252. ]
  253. ],
  254. 'author' => ['name' => 'mark']
  255. ]
  256. ]
  257. ];
  258. $options = [
  259. 'associated' => [
  260. 'Highlights._joinData.Authors', 'Highlights.Authors'
  261. ]
  262. ];
  263. $entity = $articles->patchEntity($entity, $data, $options);
  264. $articles->save($entity, $options);
  265. $entity = $articles->get(2, [
  266. 'contain' => [
  267. 'SpecialTags' => ['sort' => ['SpecialTags.id' => 'ASC']],
  268. 'SpecialTags.Authors',
  269. 'Highlights.Authors'
  270. ]
  271. ]);
  272. $this->assertEquals('mark', end($entity->highlights)->author->name);
  273. $lastTag = end($entity->special_tags);
  274. $this->assertTrue($lastTag->highlighted);
  275. $this->assertEquals('2014-06-01 10:10:00', $lastTag->highlighted_time->format('Y-m-d H:i:s'));
  276. $this->assertEquals('mariano', $lastTag->author->name);
  277. }
  278. /**
  279. * Tests that no exceptions are generated becuase of ambiguous column names in queries
  280. * during a save operation
  281. *
  282. * @see https://github.com/cakephp/cakephp/issues/3803
  283. * @return void
  284. */
  285. public function testSaveWithCallbacks()
  286. {
  287. $articles = TableRegistry::get('Articles');
  288. $articles->belongsTo('Authors');
  289. $articles->eventManager()->attach(function ($event, $query) {
  290. return $query->contain('Authors');
  291. }, 'Model.beforeFind');
  292. $article = $articles->newEntity();
  293. $article->title = 'Foo';
  294. $article->body = 'Bar';
  295. $this->assertSame($article, $articles->save($article));
  296. }
  297. /**
  298. * Test that save() works with entities containing expressions
  299. * as properties.
  300. *
  301. * @return void
  302. */
  303. public function testSaveWithExpressionProperty()
  304. {
  305. $articles = TableRegistry::get('Articles');
  306. $article = $articles->newEntity();
  307. $article->title = new \Cake\Database\Expression\QueryExpression("SELECT 'jose'");
  308. $this->assertSame($article, $articles->save($article));
  309. }
  310. /**
  311. * Tests that whe saving deep associations for a belongsToMany property,
  312. * data is not removed becuase of excesive associations filtering.
  313. *
  314. * @see https://github.com/cakephp/cakephp/issues/4009
  315. * @return void
  316. */
  317. public function testBelongsToManyDeepSave2()
  318. {
  319. $articles = TableRegistry::get('Articles');
  320. $articles->belongsToMany('Highlights', [
  321. 'className' => 'TestApp\Model\Table\TagsTable',
  322. 'foreignKey' => 'article_id',
  323. 'targetForeignKey' => 'tag_id',
  324. 'through' => 'SpecialTags',
  325. ]);
  326. $articles->Highlights->hasMany('TopArticles', [
  327. 'className' => 'TestApp\Model\Table\ArticlesTable',
  328. 'foreignKey' => 'author_id',
  329. ]);
  330. $entity = $articles->get(2, ['contain' => ['Highlights']]);
  331. $data = [
  332. 'highlights' => [
  333. [
  334. 'name' => 'New Special Tag',
  335. '_joinData' => [
  336. 'highlighted' => true,
  337. 'highlighted_time' => '2014-06-01 10:10:00',
  338. ],
  339. 'top_articles' => [
  340. ['title' => 'First top article'],
  341. ['title' => 'Second top article'],
  342. ]
  343. ]
  344. ]
  345. ];
  346. $options = [
  347. 'associated' => [
  348. 'Highlights._joinData', 'Highlights.TopArticles'
  349. ]
  350. ];
  351. $entity = $articles->patchEntity($entity, $data, $options);
  352. $articles->save($entity, $options);
  353. $entity = $articles->get(2, [
  354. 'contain' => [
  355. 'Highlights.TopArticles'
  356. ]
  357. ]);
  358. $highlights = $entity->highlights[0];
  359. $this->assertEquals('First top article', $highlights->top_articles[0]->title);
  360. $this->assertEquals('Second top article', $highlights->top_articles[1]->title);
  361. $this->assertEquals(
  362. new Time('2014-06-01 10:10:00'),
  363. $highlights->_joinData->highlighted_time
  364. );
  365. }
  366. /**
  367. * An integration test that spot checks that associations use the
  368. * correct alias names to generate queries.
  369. *
  370. * @return void
  371. */
  372. public function testPluginAssociationQueryGeneration()
  373. {
  374. Plugin::load('TestPlugin');
  375. $articles = TableRegistry::get('Articles');
  376. $articles->hasMany('TestPlugin.Comments');
  377. $articles->belongsTo('TestPlugin.Authors');
  378. $result = $articles->find()
  379. ->where(['Articles.id' => 2])
  380. ->contain(['Comments', 'Authors'])
  381. ->first();
  382. $this->assertNotEmpty(
  383. $result->comments[0]->id,
  384. 'No SQL error and comment exists.'
  385. );
  386. $this->assertNotEmpty(
  387. $result->author->id,
  388. 'No SQL error and author exists.'
  389. );
  390. }
  391. /**
  392. * Tests that loading associations having the same alias in the
  393. * joinable associations chain is not sensitive to the order in which
  394. * the associations are selected.
  395. *
  396. * @see https://github.com/cakephp/cakephp/issues/4454
  397. * @return void
  398. */
  399. public function testAssociationChainOrder()
  400. {
  401. $articles = TableRegistry::get('Articles');
  402. $articles->belongsTo('Authors');
  403. $articles->hasOne('ArticlesTags');
  404. $articlesTags = TableRegistry::get('ArticlesTags');
  405. $articlesTags->belongsTo('Authors', [
  406. 'foreignKey' => 'tag_id'
  407. ]);
  408. $resultA = $articles->find()
  409. ->contain(['ArticlesTags.Authors', 'Authors'])
  410. ->first();
  411. $resultB = $articles->find()
  412. ->contain(['Authors', 'ArticlesTags.Authors'])
  413. ->first();
  414. $this->assertEquals($resultA, $resultB);
  415. $this->assertNotEmpty($resultA->author);
  416. $this->assertNotEmpty($resultA->articles_tag->author);
  417. }
  418. /**
  419. * Test that offset/limit are elided from subquery loads.
  420. *
  421. * @return void
  422. */
  423. public function testAssociationSubQueryNoOffset()
  424. {
  425. $table = TableRegistry::get('Articles');
  426. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  427. $table->locale('eng');
  428. $query = $table->find('translations')
  429. ->order(['Articles.id' => 'ASC'])
  430. ->limit(10)
  431. ->offset(1);
  432. $result = $query->toArray();
  433. $this->assertCount(2, $result);
  434. }
  435. /**
  436. * Tests that using the subquery strategy in a deep association returns the right results
  437. *
  438. * @see https://github.com/cakephp/cakephp/issues/4484
  439. * @return void
  440. */
  441. public function testDeepBelongsToManySubqueryStrategy()
  442. {
  443. $table = TableRegistry::get('Authors');
  444. $table->hasMany('Articles');
  445. $table->Articles->belongsToMany('Tags', [
  446. 'strategy' => 'subquery'
  447. ]);
  448. $result = $table->find()->contain(['Articles.Tags'])->toArray();
  449. $this->assertEquals(
  450. ['tag1', 'tag3'],
  451. collection($result[2]->articles[0]->tags)->extract('name')->toArray()
  452. );
  453. }
  454. /**
  455. * Tests that using the subquery strategy in a deep association returns the right results
  456. *
  457. * @see https://github.com/cakephp/cakephp/issues/5769
  458. * @return void
  459. */
  460. public function testDeepBelongsToManySubqueryStrategy2()
  461. {
  462. $table = TableRegistry::get('Authors');
  463. $table->hasMany('Articles');
  464. $table->Articles->belongsToMany('Tags', [
  465. 'strategy' => 'subquery'
  466. ]);
  467. $table->belongsToMany('Tags', [
  468. 'strategy' => 'subquery',
  469. ]);
  470. $table->Articles->belongsTo('Authors');
  471. $result = $table->Articles->find()
  472. ->where(['Authors.id >' => 1])
  473. ->contain(['Authors.Tags'])
  474. ->toArray();
  475. $this->assertEquals(
  476. ['tag1', 'tag2'],
  477. collection($result[0]->author->tags)->extract('name')->toArray()
  478. );
  479. $this->assertEquals(3, $result[0]->author->id);
  480. }
  481. /**
  482. * Tests that getting the count of a query having containments return
  483. * the correct results
  484. *
  485. * @see https://github.com/cakephp/cakephp/issues/4511
  486. * @return void
  487. */
  488. public function testCountWithContain()
  489. {
  490. $table = TableRegistry::get('Articles');
  491. $table->belongsTo('Authors', ['joinType' => 'inner']);
  492. $count = $table
  493. ->find()
  494. ->contain(['Authors' => function ($q) {
  495. return $q->where(['Authors.id' => 1]);
  496. }])
  497. ->count();
  498. $this->assertEquals(2, $count);
  499. }
  500. /**
  501. * Test that deep containments don't generate empty entities for
  502. * intermediary relations.
  503. *
  504. * @return void
  505. */
  506. public function testContainNoEmptyAssociatedObjects()
  507. {
  508. $comments = TableRegistry::get('Comments');
  509. $comments->belongsTo('Users');
  510. $users = TableRegistry::get('Users');
  511. $users->hasMany('Articles', [
  512. 'foreignKey' => 'author_id'
  513. ]);
  514. $comments->updateAll(['user_id' => 99], ['id' => 1]);
  515. $result = $comments->find()
  516. ->contain(['Users'])
  517. ->where(['Comments.id' => 1])
  518. ->first();
  519. $this->assertNull($result->user, 'No record should be null.');
  520. $result = $comments->find()
  521. ->contain(['Users', 'Users.Articles'])
  522. ->where(['Comments.id' => 1])
  523. ->first();
  524. $this->assertNull($result->user, 'No record should be null.');
  525. }
  526. /**
  527. * Tests that using a comparison expression inside an OR condition works
  528. *
  529. * @see https://github.com/cakephp/cakephp/issues/5081
  530. * @return void
  531. */
  532. public function testOrConditionsWithExpression()
  533. {
  534. $table = TableRegistry::get('Articles');
  535. $query = $table->find();
  536. $query->where([
  537. 'OR' => [
  538. new \Cake\Database\Expression\Comparison('id', 1, 'integer', '>'),
  539. new \Cake\Database\Expression\Comparison('id', 3, 'integer', '<')
  540. ]
  541. ]);
  542. $results = $query->toArray();
  543. $this->assertCount(3, $results);
  544. }
  545. /**
  546. * Tests that calling count on a query having a union works correctly
  547. *
  548. * @see https://github.com/cakephp/cakephp/issues/5107
  549. * @return void
  550. */
  551. public function testCountWithUnionQuery()
  552. {
  553. $table = TableRegistry::get('Articles');
  554. $query = $table->find()->where(['id' => 1]);
  555. $query2 = $table->find()->where(['id' => 2]);
  556. $query->union($query2);
  557. $this->assertEquals(2, $query->count());
  558. }
  559. /**
  560. * Integration test when selecting no fields on the primary table.
  561. *
  562. * @return void
  563. */
  564. public function testSelectNoFieldsOnPrimaryAlias()
  565. {
  566. $table = TableRegistry::get('Articles');
  567. $table->belongsTo('Users');
  568. $query = $table->find()
  569. ->select(['Users__id' => 'id']);
  570. $results = $query->toArray();
  571. $this->assertCount(3, $results);
  572. }
  573. /**
  574. * Tests that calling first on the query results will not remove all other results
  575. * from the set.
  576. *
  577. * @return void
  578. */
  579. public function testFirstOnResultSet()
  580. {
  581. $results = TableRegistry::get('Articles')->find()->all();
  582. $this->assertEquals(3, $results->count());
  583. $this->assertNotNull($results->first());
  584. $this->assertCount(3, $results->toArray());
  585. }
  586. /**
  587. * Checks that matching and contain can be called for the same belongsTo association
  588. *
  589. * @see https://github.com/cakephp/cakephp/issues/5463
  590. * @return void
  591. */
  592. public function testFindMatchingAndContain()
  593. {
  594. $table = TableRegistry::get('Articles');
  595. $table->belongsTo('Authors');
  596. $article = $table->find()
  597. ->contain('Authors')
  598. ->matching('Authors', function ($q) {
  599. return $q->where(['Authors.id' => 1]);
  600. })
  601. ->first();
  602. $this->assertNotNull($article->author);
  603. $this->assertEquals($article->author, $article->_matchingData['Authors']);
  604. }
  605. /**
  606. * Checks that matching and contain can be called for the same belongsTo association
  607. *
  608. * @see https://github.com/cakephp/cakephp/issues/5463
  609. * @return void
  610. */
  611. public function testFindMatchingAndContainWithSubquery()
  612. {
  613. $table = TableRegistry::get('authors');
  614. $table->hasMany('articles', ['strategy' => 'subquery']);
  615. $table->articles->belongsToMany('tags');
  616. $result = $table->find()
  617. ->matching('articles.tags', function ($q) {
  618. return $q->where(['tags.id' => 2]);
  619. })
  620. ->contain('articles');
  621. $this->assertCount(2, $result->first()->articles);
  622. }
  623. /**
  624. * Tests that matching does not overwrite associations in contain
  625. *
  626. * @see https://github.com/cakephp/cakephp/issues/5584
  627. * @return void
  628. */
  629. public function testFindMatchingOverwrite()
  630. {
  631. $comments = TableRegistry::get('Comments');
  632. $comments->belongsTo('Articles');
  633. $articles = TableRegistry::get('Articles');
  634. $articles->belongsToMany('Tags');
  635. $result = $comments
  636. ->find()
  637. ->matching('Articles.Tags', function ($q) {
  638. return $q->where(['Tags.id' => 2]);
  639. })
  640. ->contain('Articles')
  641. ->first();
  642. $this->assertEquals(1, $result->id);
  643. $this->assertEquals(1, $result->_matchingData['Articles']->id);
  644. $this->assertEquals(2, $result->_matchingData['Tags']->id);
  645. $this->assertNotNull($result->article);
  646. $this->assertEquals($result->article, $result->_matchingData['Articles']);
  647. }
  648. /**
  649. * Tests that matching does not overwrite associations in contain
  650. *
  651. * @see https://github.com/cakephp/cakephp/issues/5584
  652. * @return void
  653. */
  654. public function testFindMatchingOverwrite2()
  655. {
  656. $comments = TableRegistry::get('Comments');
  657. $comments->belongsTo('Articles');
  658. $articles = TableRegistry::get('Articles');
  659. $articles->belongsTo('Authors');
  660. $articles->belongsToMany('Tags');
  661. $result = $comments
  662. ->find()
  663. ->matching('Articles.Tags', function ($q) {
  664. return $q->where(['Tags.id' => 2]);
  665. })
  666. ->contain('Articles.Authors')
  667. ->first();
  668. $this->assertNotNull($result->article->author);
  669. }
  670. /**
  671. * Tests that trying to contain an inexistent association
  672. * throws an exception and not a fatal error.
  673. *
  674. * @expectedException InvalidArgumentException
  675. * @return void
  676. */
  677. public function testQueryNotFatalError()
  678. {
  679. $comments = TableRegistry::get('Comments');
  680. $comments->find()->contain('Deprs')->all();
  681. }
  682. /**
  683. * Tests that using matching and contain on belongsTo associations
  684. * works correctly.
  685. *
  686. * @see https://github.com/cakephp/cakephp/issues/5721
  687. * @return void
  688. */
  689. public function testFindMatchingWithContain()
  690. {
  691. $comments = TableRegistry::get('Comments');
  692. $comments->belongsTo('Articles');
  693. $comments->belongsTo('Users');
  694. $result = $comments->find()
  695. ->contain(['Articles', 'Users'])
  696. ->matching('Articles', function ($q) {
  697. return $q->where(['Articles.id >=' => 1]);
  698. })
  699. ->matching('Users', function ($q) {
  700. return $q->where(['Users.id >=' => 1]);
  701. })
  702. ->order(['Comments.id' => 'ASC'])
  703. ->first();
  704. $this->assertInstanceOf('Cake\ORM\Entity', $result->article);
  705. $this->assertInstanceOf('Cake\ORM\Entity', $result->user);
  706. $this->assertEquals(2, $result->user->id);
  707. $this->assertEquals(1, $result->article->id);
  708. }
  709. /**
  710. * Tests that HasMany associations don't use duplicate PK values.
  711. *
  712. * @return void
  713. */
  714. public function testHasManyEagerLoadingUniqueKey()
  715. {
  716. $table = TableRegistry::get('ArticlesTags');
  717. $table->belongsTo('Articles', [
  718. 'strategy' => 'select'
  719. ]);
  720. $result = $table->find()
  721. ->contain(['Articles' => function ($q) {
  722. $result = $q->sql();
  723. $this->assertNotContains(':c2', $result, 'Only 2 bindings as there are only 2 rows.');
  724. $this->assertNotContains(':c3', $result, 'Only 2 bindings as there are only 2 rows.');
  725. return $q;
  726. }])
  727. ->toArray();
  728. $this->assertNotEmpty($result[0]->article);
  729. }
  730. }