ExpressionTypeCastingIntegrationTest.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 Open Group Test Suite License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  10. * @link https://cakephp.org CakePHP(tm) Project
  11. * @since 3.3.0
  12. * @license https://opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Test\TestCase\Database;
  15. use Cake\Database\Driver;
  16. use Cake\Database\Driver\Sqlserver;
  17. use Cake\Database\Expression\FunctionExpression;
  18. use Cake\Database\Type;
  19. use Cake\Database\Type\ExpressionTypeInterface;
  20. use Cake\Datasource\ConnectionManager;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * Value object for testing mappings.
  24. */
  25. class UuidValue
  26. {
  27. public $value;
  28. public function __construct($value)
  29. {
  30. $this->value = $value;
  31. }
  32. }
  33. /**
  34. * Custom type class that maps between value objects, and SQL expressions.
  35. */
  36. class OrderedUuidType extends Type implements ExpressionTypeInterface
  37. {
  38. public function toPHP($value, Driver $d)
  39. {
  40. return new UuidValue($value);
  41. }
  42. public function toExpression($value)
  43. {
  44. if ($value instanceof UuidValue) {
  45. $value = $value->value;
  46. }
  47. $substr = function ($start, $length = null) use ($value) {
  48. return new FunctionExpression(
  49. 'SUBSTR',
  50. $length === null ? [$value, $start] : [$value, $start, $length],
  51. ['string', 'integer', 'integer']
  52. );
  53. };
  54. return new FunctionExpression(
  55. 'CONCAT',
  56. [$substr(15, 4), $substr(10, 4), $substr(1, 8), $substr(20, 4), $substr(25)]
  57. );
  58. }
  59. }
  60. /**
  61. * Tests for Expression objects casting values to other expressions
  62. * using the type classes
  63. */
  64. class ExpressionTypeCastingIntegrationTest extends TestCase
  65. {
  66. public $fixtures = ['core.OrderedUuidItems'];
  67. public function setUp()
  68. {
  69. parent::setUp();
  70. $this->connection = ConnectionManager::get('test');
  71. $this->skipIf($this->connection->getDriver() instanceof Sqlserver, 'This tests uses functions specific to other drivers');
  72. Type::map('ordered_uuid', OrderedUuidType::class);
  73. }
  74. protected function _insert()
  75. {
  76. $query = $this->connection->newQuery();
  77. $query
  78. ->insert(['id', 'published', 'name'], ['id' => 'ordered_uuid'])
  79. ->into('ordered_uuid_items')
  80. ->values(['id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => 0, 'name' => 'Item 1'])
  81. ->values(['id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => 0, 'name' => 'Item 2'])
  82. ->values(['id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => 0, 'name' => 'Item 3']);
  83. $query->execute();
  84. }
  85. /**
  86. * Tests inserting a value that is to be converted to an expression
  87. * automatically
  88. *
  89. * @return void
  90. */
  91. public function testInsert()
  92. {
  93. $this->_insert();
  94. $query = $this->connection->newQuery()
  95. ->select('id')
  96. ->from('ordered_uuid_items')
  97. ->order('id')
  98. ->setDefaultTypes(['id' => 'ordered_uuid']);
  99. $query->setSelectTypeMap($query->getTypeMap());
  100. $results = $query->execute()->fetchAll('assoc');
  101. $this->assertEquals(new UuidValue('419a8da0482b7756b21f27da40cf8569'), $results[0]['id']);
  102. $this->assertEquals(new UuidValue('43e0b920481fc6d0a40d6d1740cf8569'), $results[1]['id']);
  103. $this->assertEquals(new UuidValue('4c2681c048298a29a7fb413140cf8569'), $results[2]['id']);
  104. }
  105. /**
  106. * Test selecting with a custom expression type using conditions
  107. *
  108. * @return void
  109. */
  110. public function testSelectWithConditions()
  111. {
  112. $this->_insert();
  113. $result = $this->connection->newQuery()
  114. ->select('id')
  115. ->from('ordered_uuid_items')
  116. ->where(['id' => '48298a29-81c0-4c26-a7fb-413140cf8569'], ['id' => 'ordered_uuid'])
  117. ->execute()
  118. ->fetchAll('assoc');
  119. $this->assertCount(1, $result);
  120. $this->assertEquals('4c2681c048298a29a7fb413140cf8569', $result[0]['id']);
  121. }
  122. /**
  123. * Tests Select using value object in conditions
  124. *
  125. * @return void
  126. */
  127. public function testSelectWithConditionsValueObject()
  128. {
  129. $this->_insert();
  130. $result = $this->connection->newQuery()
  131. ->select('id')
  132. ->from('ordered_uuid_items')
  133. ->where(['id' => new UuidValue('48298a29-81c0-4c26-a7fb-413140cf8569')], ['id' => 'ordered_uuid'])
  134. ->execute()
  135. ->fetchAll('assoc');
  136. $this->assertCount(1, $result);
  137. $this->assertEquals('4c2681c048298a29a7fb413140cf8569', $result[0]['id']);
  138. }
  139. /**
  140. * Tests using the expression type in with an IN condition
  141. *
  142. * @var string
  143. */
  144. public function testSelectWithInCondition()
  145. {
  146. $this->_insert();
  147. $result = $this->connection->newQuery()
  148. ->select('id')
  149. ->from('ordered_uuid_items')
  150. ->where(
  151. ['id' => ['48298a29-81c0-4c26-a7fb-413140cf8569', '482b7756-8da0-419a-b21f-27da40cf8569']],
  152. ['id' => 'ordered_uuid[]']
  153. )
  154. ->order('id')
  155. ->execute()
  156. ->fetchAll('assoc');
  157. $this->assertCount(2, $result);
  158. $this->assertEquals('419a8da0482b7756b21f27da40cf8569', $result[0]['id']);
  159. $this->assertEquals('419a8da0482b7756b21f27da40cf8569', $result[0]['id']);
  160. }
  161. /**
  162. * Tests using an expression type in a between condition
  163. *
  164. * @return void
  165. */
  166. public function testSelectWithBetween()
  167. {
  168. $this->_insert();
  169. $result = $this->connection->newQuery()
  170. ->select('id')
  171. ->from('ordered_uuid_items')
  172. ->where(function ($exp) {
  173. return $exp->between(
  174. 'id',
  175. '482b7756-8da0-419a-b21f-27da40cf8569',
  176. '48298a29-81c0-4c26-a7fb-413140cf8569',
  177. 'ordered_uuid'
  178. );
  179. })
  180. ->execute()
  181. ->fetchAll('assoc');
  182. $this->assertCount(3, $result);
  183. }
  184. /**
  185. * Tests using an expression type inside a function expression
  186. *
  187. * @return void
  188. */
  189. public function testSelectWithFunction()
  190. {
  191. $this->_insert();
  192. $result = $this->connection->newQuery()
  193. ->select('id')
  194. ->from('ordered_uuid_items')
  195. ->where(function ($exp, $q) {
  196. return $exp->eq(
  197. 'id',
  198. $q->func()->concat(['48298a29-81c0-4c26-a7fb', '-413140cf8569'], []),
  199. 'ordered_uuid'
  200. );
  201. })
  202. ->execute()
  203. ->fetchAll('assoc');
  204. $this->assertCount(1, $result);
  205. $this->assertEquals('4c2681c048298a29a7fb413140cf8569', $result[0]['id']);
  206. }
  207. }