QueryRegressionTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  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('mariano', end($entity->special_tags)->author->name);
  273. $this->assertEquals('mark', end($entity->highlights)->author->name);
  274. }
  275. /**
  276. * Tests that no exceptions are generated becuase of ambiguous column names in queries
  277. * during a save operation
  278. *
  279. * @see https://github.com/cakephp/cakephp/issues/3803
  280. * @return void
  281. */
  282. public function testSaveWithCallbacks()
  283. {
  284. $articles = TableRegistry::get('Articles');
  285. $articles->belongsTo('Authors');
  286. $articles->eventManager()->attach(function ($event, $query) {
  287. return $query->contain('Authors');
  288. }, 'Model.beforeFind');
  289. $article = $articles->newEntity();
  290. $article->title = 'Foo';
  291. $article->body = 'Bar';
  292. $this->assertSame($article, $articles->save($article));
  293. }
  294. /**
  295. * Test that save() works with entities containing expressions
  296. * as properties.
  297. *
  298. * @return void
  299. */
  300. public function testSaveWithExpressionProperty()
  301. {
  302. $articles = TableRegistry::get('Articles');
  303. $article = $articles->newEntity();
  304. $article->title = new \Cake\Database\Expression\QueryExpression("SELECT 'jose'");
  305. $this->assertSame($article, $articles->save($article));
  306. }
  307. /**
  308. * Tests that whe saving deep associations for a belongsToMany property,
  309. * data is not removed becuase of excesive associations filtering.
  310. *
  311. * @see https://github.com/cakephp/cakephp/issues/4009
  312. * @return void
  313. */
  314. public function testBelongsToManyDeepSave2()
  315. {
  316. $articles = TableRegistry::get('Articles');
  317. $articles->belongsToMany('Highlights', [
  318. 'className' => 'TestApp\Model\Table\TagsTable',
  319. 'foreignKey' => 'article_id',
  320. 'targetForeignKey' => 'tag_id',
  321. 'through' => 'SpecialTags',
  322. ]);
  323. $articles->Highlights->hasMany('TopArticles', [
  324. 'className' => 'TestApp\Model\Table\ArticlesTable',
  325. 'foreignKey' => 'author_id',
  326. ]);
  327. $entity = $articles->get(2, ['contain' => ['Highlights']]);
  328. $data = [
  329. 'highlights' => [
  330. [
  331. 'name' => 'New Special Tag',
  332. '_joinData' => [
  333. 'highlighted' => true,
  334. 'highlighted_time' => '2014-06-01 10:10:00',
  335. ],
  336. 'top_articles' => [
  337. ['title' => 'First top article'],
  338. ['title' => 'Second top article'],
  339. ]
  340. ]
  341. ]
  342. ];
  343. $options = [
  344. 'associated' => [
  345. 'Highlights._joinData', 'Highlights.TopArticles'
  346. ]
  347. ];
  348. $entity = $articles->patchEntity($entity, $data, $options);
  349. $articles->save($entity, $options);
  350. $entity = $articles->get(2, [
  351. 'contain' => [
  352. 'Highlights.TopArticles'
  353. ]
  354. ]);
  355. $highlights = $entity->highlights[0];
  356. $this->assertEquals('First top article', $highlights->top_articles[0]->title);
  357. $this->assertEquals('Second top article', $highlights->top_articles[1]->title);
  358. $this->assertEquals(
  359. new Time('2014-06-01 10:10:00'),
  360. $highlights->_joinData->highlighted_time
  361. );
  362. }
  363. /**
  364. * An integration test that spot checks that associations use the
  365. * correct alias names to generate queries.
  366. *
  367. * @return void
  368. */
  369. public function testPluginAssociationQueryGeneration()
  370. {
  371. Plugin::load('TestPlugin');
  372. $articles = TableRegistry::get('Articles');
  373. $articles->hasMany('TestPlugin.Comments');
  374. $articles->belongsTo('TestPlugin.Authors');
  375. $result = $articles->find()
  376. ->where(['Articles.id' => 2])
  377. ->contain(['Comments', 'Authors'])
  378. ->first();
  379. $this->assertNotEmpty(
  380. $result->comments[0]->id,
  381. 'No SQL error and comment exists.'
  382. );
  383. $this->assertNotEmpty(
  384. $result->author->id,
  385. 'No SQL error and author exists.'
  386. );
  387. }
  388. /**
  389. * Tests that loading associations having the same alias in the
  390. * joinable associations chain is not sensitive to the order in which
  391. * the associations are selected.
  392. *
  393. * @see https://github.com/cakephp/cakephp/issues/4454
  394. * @return void
  395. */
  396. public function testAssociationChainOrder()
  397. {
  398. $articles = TableRegistry::get('Articles');
  399. $articles->belongsTo('Authors');
  400. $articles->hasOne('ArticlesTags');
  401. $articlesTags = TableRegistry::get('ArticlesTags');
  402. $articlesTags->belongsTo('Authors', [
  403. 'foreignKey' => 'tag_id'
  404. ]);
  405. $resultA = $articles->find()
  406. ->contain(['ArticlesTags.Authors', 'Authors'])
  407. ->first();
  408. $resultB = $articles->find()
  409. ->contain(['Authors', 'ArticlesTags.Authors'])
  410. ->first();
  411. $this->assertEquals($resultA, $resultB);
  412. $this->assertNotEmpty($resultA->author);
  413. $this->assertNotEmpty($resultA->articles_tag->author);
  414. }
  415. /**
  416. * Test that offset/limit are elided from subquery loads.
  417. *
  418. * @return void
  419. */
  420. public function testAssociationSubQueryNoOffset()
  421. {
  422. $table = TableRegistry::get('Articles');
  423. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  424. $table->locale('eng');
  425. $query = $table->find('translations')
  426. ->order(['Articles.id' => 'ASC'])
  427. ->limit(10)
  428. ->offset(1);
  429. $result = $query->toArray();
  430. $this->assertCount(2, $result);
  431. }
  432. /**
  433. * Tests that using the subquery strategy in a deep association returns the right results
  434. *
  435. * @see https://github.com/cakephp/cakephp/issues/4484
  436. * @return void
  437. */
  438. public function testDeepBelongsToManySubqueryStrategy()
  439. {
  440. $table = TableRegistry::get('Authors');
  441. $table->hasMany('Articles');
  442. $table->Articles->belongsToMany('Tags', [
  443. 'strategy' => 'subquery'
  444. ]);
  445. $result = $table->find()->contain(['Articles.Tags'])->toArray();
  446. $this->assertEquals(
  447. ['tag1', 'tag3'],
  448. collection($result[2]->articles[0]->tags)->extract('name')->toArray()
  449. );
  450. }
  451. /**
  452. * Tests that using the subquery strategy in a deep association returns the right results
  453. *
  454. * @see https://github.com/cakephp/cakephp/issues/5769
  455. * @return void
  456. */
  457. public function testDeepBelongsToManySubqueryStrategy2()
  458. {
  459. $table = TableRegistry::get('Authors');
  460. $table->hasMany('Articles');
  461. $table->Articles->belongsToMany('Tags', [
  462. 'strategy' => 'subquery'
  463. ]);
  464. $table->belongsToMany('Tags', [
  465. 'strategy' => 'subquery',
  466. ]);
  467. $table->Articles->belongsTo('Authors');
  468. $result = $table->Articles->find()
  469. ->where(['Authors.id >' => 1])
  470. ->contain(['Authors.Tags'])
  471. ->toArray();
  472. $this->assertEquals(
  473. ['tag1', 'tag2'],
  474. collection($result[0]->author->tags)->extract('name')->toArray()
  475. );
  476. $this->assertEquals(3, $result[0]->author->id);
  477. }
  478. /**
  479. * Tests that getting the count of a query having containments return
  480. * the correct results
  481. *
  482. * @see https://github.com/cakephp/cakephp/issues/4511
  483. * @return void
  484. */
  485. public function testCountWithContain()
  486. {
  487. $table = TableRegistry::get('Articles');
  488. $table->belongsTo('Authors', ['joinType' => 'inner']);
  489. $count = $table
  490. ->find()
  491. ->contain(['Authors' => function ($q) {
  492. return $q->where(['Authors.id' => 1]);
  493. }])
  494. ->count();
  495. $this->assertEquals(2, $count);
  496. }
  497. /**
  498. * Test that deep containments don't generate empty entities for
  499. * intermediary relations.
  500. *
  501. * @return void
  502. */
  503. public function testContainNoEmptyAssociatedObjects()
  504. {
  505. $comments = TableRegistry::get('Comments');
  506. $comments->belongsTo('Users');
  507. $users = TableRegistry::get('Users');
  508. $users->hasMany('Articles', [
  509. 'foreignKey' => 'author_id'
  510. ]);
  511. $comments->updateAll(['user_id' => 99], ['id' => 1]);
  512. $result = $comments->find()
  513. ->contain(['Users'])
  514. ->where(['Comments.id' => 1])
  515. ->first();
  516. $this->assertNull($result->user, 'No record should be null.');
  517. $result = $comments->find()
  518. ->contain(['Users', 'Users.Articles'])
  519. ->where(['Comments.id' => 1])
  520. ->first();
  521. $this->assertNull($result->user, 'No record should be null.');
  522. }
  523. /**
  524. * Tests that using a comparison expression inside an OR condition works
  525. *
  526. * @see https://github.com/cakephp/cakephp/issues/5081
  527. * @return void
  528. */
  529. public function testOrConditionsWithExpression()
  530. {
  531. $table = TableRegistry::get('Articles');
  532. $query = $table->find();
  533. $query->where([
  534. 'OR' => [
  535. new \Cake\Database\Expression\Comparison('id', 1, 'integer', '>'),
  536. new \Cake\Database\Expression\Comparison('id', 3, 'integer', '<')
  537. ]
  538. ]);
  539. $results = $query->toArray();
  540. $this->assertCount(3, $results);
  541. }
  542. /**
  543. * Tests that calling count on a query having a union works correctly
  544. *
  545. * @see https://github.com/cakephp/cakephp/issues/5107
  546. * @return void
  547. */
  548. public function testCountWithUnionQuery()
  549. {
  550. $table = TableRegistry::get('Articles');
  551. $query = $table->find()->where(['id' => 1]);
  552. $query2 = $table->find()->where(['id' => 2]);
  553. $query->union($query2);
  554. $this->assertEquals(2, $query->count());
  555. }
  556. /**
  557. * Integration test when selecting no fields on the primary table.
  558. *
  559. * @return void
  560. */
  561. public function testSelectNoFieldsOnPrimaryAlias()
  562. {
  563. $table = TableRegistry::get('Articles');
  564. $table->belongsTo('Users');
  565. $query = $table->find()
  566. ->select(['Users__id' => 'id']);
  567. $results = $query->toArray();
  568. $this->assertCount(3, $results);
  569. }
  570. /**
  571. * Tests that calling first on the query results will not remove all other results
  572. * from the set.
  573. *
  574. * @return void
  575. */
  576. public function testFirstOnResultSet()
  577. {
  578. $results = TableRegistry::get('Articles')->find()->all();
  579. $this->assertEquals(3, $results->count());
  580. $this->assertNotNull($results->first());
  581. $this->assertCount(3, $results->toArray());
  582. }
  583. /**
  584. * Checks that matching and contain can be called for the same belongsTo association
  585. *
  586. * @see https://github.com/cakephp/cakephp/issues/5463
  587. * @return void
  588. */
  589. public function testFindMatchingAndContain()
  590. {
  591. $table = TableRegistry::get('Articles');
  592. $table->belongsTo('Authors');
  593. $article = $table->find()
  594. ->contain('Authors')
  595. ->matching('Authors', function ($q) {
  596. return $q->where(['Authors.id' => 1]);
  597. })
  598. ->first();
  599. $this->assertNotNull($article->author);
  600. $this->assertEquals($article->author, $article->_matchingData['Authors']);
  601. }
  602. /**
  603. * Checks that matching and contain can be called for the same belongsTo association
  604. *
  605. * @see https://github.com/cakephp/cakephp/issues/5463
  606. * @return void
  607. */
  608. public function testFindMatchingAndContainWithSubquery()
  609. {
  610. $table = TableRegistry::get('authors');
  611. $table->hasMany('articles', ['strategy' => 'subquery']);
  612. $table->articles->belongsToMany('tags');
  613. $result = $table->find()
  614. ->matching('articles.tags', function ($q) {
  615. return $q->where(['tags.id' => 2]);
  616. })
  617. ->contain('articles');
  618. $this->assertCount(2, $result->first()->articles);
  619. }
  620. /**
  621. * Tests that matching does not overwrite associations in contain
  622. *
  623. * @see https://github.com/cakephp/cakephp/issues/5584
  624. * @return void
  625. */
  626. public function testFindMatchingOverwrite()
  627. {
  628. $comments = TableRegistry::get('Comments');
  629. $comments->belongsTo('Articles');
  630. $articles = TableRegistry::get('Articles');
  631. $articles->belongsToMany('Tags');
  632. $result = $comments
  633. ->find()
  634. ->matching('Articles.Tags', function ($q) {
  635. return $q->where(['Tags.id' => 2]);
  636. })
  637. ->contain('Articles')
  638. ->first();
  639. $this->assertEquals(1, $result->id);
  640. $this->assertEquals(1, $result->_matchingData['Articles']->id);
  641. $this->assertEquals(2, $result->_matchingData['Tags']->id);
  642. $this->assertNotNull($result->article);
  643. $this->assertEquals($result->article, $result->_matchingData['Articles']);
  644. }
  645. /**
  646. * Tests that matching does not overwrite associations in contain
  647. *
  648. * @see https://github.com/cakephp/cakephp/issues/5584
  649. * @return void
  650. */
  651. public function testFindMatchingOverwrite2()
  652. {
  653. $comments = TableRegistry::get('Comments');
  654. $comments->belongsTo('Articles');
  655. $articles = TableRegistry::get('Articles');
  656. $articles->belongsTo('Authors');
  657. $articles->belongsToMany('Tags');
  658. $result = $comments
  659. ->find()
  660. ->matching('Articles.Tags', function ($q) {
  661. return $q->where(['Tags.id' => 2]);
  662. })
  663. ->contain('Articles.Authors')
  664. ->first();
  665. $this->assertNotNull($result->article->author);
  666. }
  667. /**
  668. * Tests that trying to contain an inexistent association
  669. * throws an exception and not a fatal error.
  670. *
  671. * @expectedException InvalidArgumentException
  672. * @return void
  673. */
  674. public function testQueryNotFatalError()
  675. {
  676. $comments = TableRegistry::get('Comments');
  677. $comments->find()->contain('Deprs')->all();
  678. }
  679. /**
  680. * Tests that using matching and contain on belongsTo associations
  681. * works correctly.
  682. *
  683. * @see https://github.com/cakephp/cakephp/issues/5721
  684. * @return void
  685. */
  686. public function testFindMatchingWithContain()
  687. {
  688. $comments = TableRegistry::get('Comments');
  689. $comments->belongsTo('Articles');
  690. $comments->belongsTo('Users');
  691. $result = $comments->find()
  692. ->contain(['Articles', 'Users'])
  693. ->matching('Articles', function ($q) {
  694. return $q->where(['Articles.id >=' => 1]);
  695. })
  696. ->matching('Users', function ($q) {
  697. return $q->where(['Users.id >=' => 1]);
  698. })
  699. ->order(['Comments.id' => 'ASC'])
  700. ->first();
  701. $this->assertInstanceOf('Cake\ORM\Entity', $result->article);
  702. $this->assertInstanceOf('Cake\ORM\Entity', $result->user);
  703. $this->assertEquals(2, $result->user->id);
  704. $this->assertEquals(1, $result->article->id);
  705. }
  706. /**
  707. * Tests that HasMany associations don't use duplicate PK values.
  708. *
  709. * @return void
  710. */
  711. public function testHasManyEagerLoadingUniqueKey()
  712. {
  713. $table = TableRegistry::get('ArticlesTags');
  714. $table->belongsTo('Articles', [
  715. 'strategy' => 'select'
  716. ]);
  717. $result = $table->find()
  718. ->contain(['Articles' => function ($q) {
  719. $result = $q->sql();
  720. $this->assertNotContains(':c2', $result, 'Only 2 bindings as there are only 2 rows.');
  721. $this->assertNotContains(':c3', $result, 'Only 2 bindings as there are only 2 rows.');
  722. return $q;
  723. }])
  724. ->toArray();
  725. $this->assertNotEmpty($result[0]->article);
  726. }
  727. }