QueryTest.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Database;
  17. use Cake\Database\Expression\CommonTableExpression;
  18. use Cake\Database\Expression\IdentifierExpression;
  19. use Cake\Database\ExpressionInterface;
  20. use Cake\Database\Query;
  21. use Cake\Datasource\ConnectionManager;
  22. use Cake\TestSuite\TestCase;
  23. use InvalidArgumentException;
  24. /**
  25. * Tests Query class
  26. */
  27. class QueryTest extends TestCase
  28. {
  29. use QueryAssertsTrait;
  30. protected array $fixtures = [
  31. 'core.Articles',
  32. 'core.Authors',
  33. 'core.Comments',
  34. 'core.Profiles',
  35. 'core.MenuLinkTrees',
  36. ];
  37. /**
  38. * @var \Cake\Database\Connection
  39. */
  40. protected $connection;
  41. /**
  42. * @var bool
  43. */
  44. protected $autoQuote;
  45. protected Query $query;
  46. public function setUp(): void
  47. {
  48. parent::setUp();
  49. $this->connection = ConnectionManager::get('test');
  50. $this->autoQuote = $this->connection->getDriver()->isAutoQuotingEnabled();
  51. $this->query = $this->newQuery();
  52. }
  53. public function tearDown(): void
  54. {
  55. parent::tearDown();
  56. $this->connection->getDriver()->enableAutoQuoting($this->autoQuote);
  57. unset($this->query);
  58. unset($this->connection);
  59. }
  60. protected function newQuery()
  61. {
  62. return $this->getMockForAbstractClass(Query::class, [$this->connection]);
  63. }
  64. /**
  65. * Tests that empty values don't set where clauses.
  66. */
  67. public function testWhereEmptyValues(): void
  68. {
  69. $this->query->from('comments')
  70. ->where('');
  71. $this->assertCount(0, $this->query->clause('where'));
  72. $this->query->where([]);
  73. $this->assertCount(0, $this->query->clause('where'));
  74. }
  75. /**
  76. * Tests that the identifier method creates an expression object.
  77. */
  78. public function testIdentifierExpression(): void
  79. {
  80. /** @var \Cake\Database\Expression\IdentifierExpression $identifier */
  81. $identifier = $this->query->identifier('foo');
  82. $this->assertInstanceOf(IdentifierExpression::class, $identifier);
  83. $this->assertSame('foo', $identifier->getIdentifier());
  84. }
  85. /**
  86. * Tests the interface contract of identifier
  87. */
  88. public function testIdentifierInterface(): void
  89. {
  90. $identifier = $this->query->identifier('description');
  91. $this->assertInstanceOf(ExpressionInterface::class, $identifier);
  92. $this->assertSame('description', $identifier->getIdentifier());
  93. $identifier->setIdentifier('title');
  94. $this->assertSame('title', $identifier->getIdentifier());
  95. }
  96. /**
  97. * Tests __debugInfo on incomplete query
  98. */
  99. public function testDebugInfoIncompleteQuery(): void
  100. {
  101. $this->query = $this->newQuery()
  102. ->from(['articles']);
  103. $result = $this->query->__debugInfo();
  104. $this->assertStringContainsString('incomplete', $result['sql']);
  105. $this->assertSame([], $result['params']);
  106. }
  107. public function testCloneWithExpression(): void
  108. {
  109. $this->query
  110. ->with(
  111. new CommonTableExpression(
  112. 'cte',
  113. $this->newQuery()
  114. )
  115. )
  116. ->with(function (CommonTableExpression $cte, Query $query) {
  117. return $cte
  118. ->name('cte2')
  119. ->query($query);
  120. });
  121. $clause = $this->query->clause('with');
  122. $clauseClone = (clone $this->query)->clause('with');
  123. $this->assertIsArray($clause);
  124. foreach ($clause as $key => $value) {
  125. $this->assertEquals($value, $clauseClone[$key]);
  126. $this->assertNotSame($value, $clauseClone[$key]);
  127. }
  128. }
  129. public function testCloneModifierExpression(): void
  130. {
  131. $this->query->modifier($this->query->newExpr('modifier'));
  132. $clause = $this->query->clause('modifier');
  133. $clauseClone = (clone $this->query)->clause('modifier');
  134. $this->assertIsArray($clause);
  135. foreach ($clause as $key => $value) {
  136. $this->assertEquals($value, $clauseClone[$key]);
  137. $this->assertNotSame($value, $clauseClone[$key]);
  138. }
  139. }
  140. public function testCloneFromExpression(): void
  141. {
  142. $this->query->from(['alias' => $this->newQuery()]);
  143. $clause = $this->query->clause('from');
  144. $clauseClone = (clone $this->query)->clause('from');
  145. $this->assertIsArray($clause);
  146. foreach ($clause as $key => $value) {
  147. $this->assertEquals($value, $clauseClone[$key]);
  148. $this->assertNotSame($value, $clauseClone[$key]);
  149. }
  150. }
  151. public function testCloneJoinExpression(): void
  152. {
  153. $this->query
  154. ->innerJoin(
  155. ['alias_inner' => $this->newQuery()],
  156. ['alias_inner.fk = parent.pk']
  157. )
  158. ->leftJoin(
  159. ['alias_left' => $this->newQuery()],
  160. ['alias_left.fk = parent.pk']
  161. )
  162. ->rightJoin(
  163. ['alias_right' => $this->newQuery()],
  164. ['alias_right.fk = parent.pk']
  165. );
  166. $clause = $this->query->clause('join');
  167. $clauseClone = (clone $this->query)->clause('join');
  168. $this->assertIsArray($clause);
  169. foreach ($clause as $key => $value) {
  170. $this->assertEquals($value['table'], $clauseClone[$key]['table']);
  171. $this->assertNotSame($value['table'], $clauseClone[$key]['table']);
  172. $this->assertEquals($value['conditions'], $clauseClone[$key]['conditions']);
  173. $this->assertNotSame($value['conditions'], $clauseClone[$key]['conditions']);
  174. }
  175. }
  176. public function testCloneWhereExpression(): void
  177. {
  178. $this->query
  179. ->where($this->query->newExpr('where'))
  180. ->where(['field' => $this->query->newExpr('where')]);
  181. $clause = $this->query->clause('where');
  182. $clauseClone = (clone $this->query)->clause('where');
  183. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  184. $this->assertEquals($clause, $clauseClone);
  185. $this->assertNotSame($clause, $clauseClone);
  186. }
  187. public function testCloneOrderExpression(): void
  188. {
  189. $this->query
  190. ->order($this->query->newExpr('order'))
  191. ->orderAsc($this->query->newExpr('order_asc'))
  192. ->orderDesc($this->query->newExpr('order_desc'));
  193. $clause = $this->query->clause('order');
  194. $clauseClone = (clone $this->query)->clause('order');
  195. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  196. $this->assertEquals($clause, $clauseClone);
  197. $this->assertNotSame($clause, $clauseClone);
  198. }
  199. public function testCloneLimitExpression(): void
  200. {
  201. $this->query->limit($this->query->newExpr('1'));
  202. $clause = $this->query->clause('limit');
  203. $clauseClone = (clone $this->query)->clause('limit');
  204. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  205. $this->assertEquals($clause, $clauseClone);
  206. $this->assertNotSame($clause, $clauseClone);
  207. }
  208. public function testCloneOffsetExpression(): void
  209. {
  210. $this->query->offset($this->query->newExpr('1'));
  211. $clause = $this->query->clause('offset');
  212. $clauseClone = (clone $this->query)->clause('offset');
  213. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  214. $this->assertEquals($clause, $clauseClone);
  215. $this->assertNotSame($clause, $clauseClone);
  216. }
  217. public function testCloneEpilogExpression(): void
  218. {
  219. $this->query->epilog($this->query->newExpr('epilog'));
  220. $clause = $this->query->clause('epilog');
  221. $clauseClone = (clone $this->query)->clause('epilog');
  222. $this->assertInstanceOf(ExpressionInterface::class, $clause);
  223. $this->assertEquals($clause, $clauseClone);
  224. $this->assertNotSame($clause, $clauseClone);
  225. }
  226. /**
  227. * Test getValueBinder()
  228. */
  229. public function testGetValueBinder(): void
  230. {
  231. $this->assertInstanceOf('Cake\Database\ValueBinder', $this->query->getValueBinder());
  232. }
  233. /**
  234. * Test that reading an undefined clause does not emit an error.
  235. */
  236. public function testClauseUndefined(): void
  237. {
  238. $this->expectException(InvalidArgumentException::class);
  239. $this->expectExceptionMessage('The `nope` clause is not defined. Valid clauses are: `delete`, `update`');
  240. $this->assertEmpty($this->query->clause('where'));
  241. $this->query->clause('nope');
  242. }
  243. }