PageRenderTime 68ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/cases/data/source/DatabaseTest.php

http://github.com/UnionOfRAD/lithium
PHP | 2103 lines | 1785 code | 286 blank | 32 comment | 2 complexity | 742c4337b8d6f25db31f4508107f387f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * li₃: the most RAD framework for PHP (http://li3.me)
  4. *
  5. * Copyright 2009, Union of RAD. All rights reserved. This source
  6. * code is distributed under the terms of the BSD 3-Clause License.
  7. * The full license text can be found in the LICENSE.txt file.
  8. */
  9. namespace lithium\tests\cases\data\source;
  10. use lithium\data\Connections;
  11. use lithium\data\model\Query;
  12. use lithium\data\entity\Record;
  13. use lithium\tests\mocks\data\model\MockDatabase;
  14. use lithium\tests\mocks\data\model\MockDatabasePost;
  15. use lithium\tests\mocks\data\model\MockDatabaseComment;
  16. use lithium\tests\mocks\data\model\MockDatabaseTagging;
  17. use lithium\tests\mocks\data\model\MockDatabasePostRevision;
  18. use lithium\tests\mocks\data\model\database\MockResult;
  19. use lithium\tests\mocks\data\model\MockGallery;
  20. use lithium\tests\mocks\data\model\MockImage;
  21. use lithium\tests\mocks\data\model\MockImageTag;
  22. use lithium\tests\mocks\data\model\MockTag;
  23. class DatabaseTest extends \lithium\test\Unit {
  24. protected $_db = null;
  25. protected $_configs = [];
  26. protected $_model = 'lithium\tests\mocks\data\model\MockDatabasePost';
  27. protected $_comment = 'lithium\tests\mocks\data\model\MockDatabaseComment';
  28. protected $_gallery = 'lithium\tests\mocks\data\model\MockGallery';
  29. protected $_imageTag = 'lithium\tests\mocks\data\model\MockImageTag';
  30. public function setUp() {
  31. $this->_db = new MockDatabase();
  32. Connections::add('mockconn', ['object' => $this->_db]);
  33. $config = ['meta' => ['connection' => 'mockconn']];
  34. MockDatabasePost::config($config);
  35. MockDatabaseComment::config($config);
  36. MockDatabaseTagging::config($config);
  37. MockDatabasePostRevision::config($config);
  38. MockGallery::config($config);
  39. MockImage::config($config);
  40. MockImageTag::config($config);
  41. MockTag::config($config);
  42. }
  43. public function tearDown() {
  44. Connections::remove('mockconn');
  45. MockDatabasePost::reset();
  46. MockDatabaseComment::reset();
  47. MockDatabaseTagging::reset();
  48. MockDatabasePostRevision::reset();
  49. MockGallery::reset();
  50. MockImage::reset();
  51. MockImageTag::reset();
  52. MockTag::reset();
  53. }
  54. public function testDefaultConfig() {
  55. $expected = [
  56. 'persistent' => true,
  57. 'host' => 'localhost',
  58. 'login' => 'root',
  59. 'password' => '',
  60. 'database' => 'mock',
  61. 'encoding' => null,
  62. 'dsn' => null,
  63. 'options' => [],
  64. 'autoConnect' => true,
  65. 'init' => true
  66. ];
  67. $result = $this->_db->testConfig();
  68. $this->assertEqual($expected, $result);
  69. }
  70. public function testModifyConfig() {
  71. $db = new MockDatabase(['host' => '127.0.0.1', 'login' => 'bob']);
  72. $expected = [
  73. 'persistent' => true,
  74. 'host' => '127.0.0.1',
  75. 'login' => 'bob',
  76. 'password' => '',
  77. 'database' => 'mock',
  78. 'encoding' => null,
  79. 'dsn' => null,
  80. 'options' => [],
  81. 'autoConnect' => true,
  82. 'init' => true
  83. ];
  84. $result = $db->testConfig();
  85. $this->assertEqual($expected, $result);
  86. }
  87. public function testName() {
  88. $result = $this->_db->name("name");
  89. $this->assertEqual("{name}", $result);
  90. $result = $this->_db->name("Model.name");
  91. $this->assertEqual("{Model}.{name}", $result);
  92. $result = $this->_db->name("Model.name name");
  93. $this->assertEqual("{Model}.{name name}", $result);
  94. }
  95. public function testNullValueWithSchemaFormatter() {
  96. $result = $this->_db->value(null);
  97. $this->assertIdentical('NULL', $result);
  98. }
  99. public function testStringValueWithSchemaFormatter() {
  100. $result = $this->_db->value('string', ['type' => 'string']);
  101. $this->assertEqual("'string'", $result);
  102. $result = $this->_db->value('1', ['type' => 'string']);
  103. $this->assertIdentical("'1'", $result);
  104. }
  105. public function testBooleanValueWithSchemaFormatter() {
  106. $result = $this->_db->value('true', ['type' => 'boolean']);
  107. $this->assertIdentical(1, $result);
  108. }
  109. public function testNumericValueWithSchemaFormatter() {
  110. $result = $this->_db->value('1', ['type' => 'integer']);
  111. $this->assertIdentical(1, $result);
  112. $result = $this->_db->value('1.1', ['type' => 'float']);
  113. $this->assertIdentical(1.1, $result);
  114. }
  115. public function testObjectValueWithSchemaFormatter() {
  116. $result = $this->_db->value((object) 'REGEXP "^fo$"');
  117. $this->assertIdentical('REGEXP "^fo$"', $result);
  118. $result = $this->_db->value((object) 'CURRENT_TIMESTAMP', ['type' => 'timestamp']);
  119. $this->assertIdentical('CURRENT_TIMESTAMP', $result);
  120. }
  121. public function testDateTimeValueWithSchemaFormatter() {
  122. $result = $this->_db->value('2012-05-25 22:44:00', ['type' => 'timestamp']);
  123. $this->assertIdentical("'2012-05-25 22:44:00'", $result);
  124. $result = $this->_db->value('2012-05-25', ['type' => 'date']);
  125. $this->assertIdentical("'2012-05-25'", $result);
  126. $result = $this->_db->value('now', ['type' => 'timestamp']);
  127. $this->assertPattern("/^'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/", $result);
  128. $result = $this->_db->value('now', ['type' => 'date']);
  129. $this->assertPattern("/^'\d{4}-\d{2}-\d{2}'/", $result);
  130. $result = $this->_db->value('now', ['type' => 'time']);
  131. $this->assertPattern("/^'\d{2}:\d{2}:\d{2}'/", $result);
  132. $result = $this->_db->value('', ['type' => 'date']);
  133. $this->assertIdentical('NULL', $result);
  134. $result = $this->_db->value('', ['type' => 'time']);
  135. $this->assertIdentical('NULL', $result);
  136. $result = $this->_db->value('', ['type' => 'timestamp']);
  137. $this->assertIdentical('NULL', $result);
  138. $result = $this->_db->value('', ['type' => 'datetime']);
  139. $this->assertIdentical('NULL', $result);
  140. $result = $this->_db->value('', [
  141. 'type' => 'date', 'default' => '2012-05-25'
  142. ]);
  143. $this->assertIdentical("'2012-05-25'", $result);
  144. $result = $this->_db->value('', [
  145. 'type' => 'time', 'default' => '08:00:00'
  146. ]);
  147. $this->assertIdentical("'08:00:00'", $result);
  148. $result = $this->_db->value('', [
  149. 'type' => 'timestamp', 'default' => 'now'
  150. ]);
  151. $this->assertPattern("/^'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/", $result);
  152. }
  153. public function testStringValueByIntrospection() {
  154. $result = $this->_db->value("string");
  155. $this->assertIdentical("'string'", $result);
  156. }
  157. public function testBooleanValueByIntrospection() {
  158. $result = $this->_db->value(true);
  159. $this->assertIdentical(1, $result);
  160. }
  161. public function testNumericValueByIntrospection() {
  162. $result = $this->_db->value('1');
  163. $this->assertIdentical(1, $result);
  164. $result = $this->_db->value('1.1');
  165. $this->assertIdentical(1.1, $result);
  166. }
  167. public function testSchema() {
  168. $model = $this->_model;
  169. $model::config();
  170. $modelName = '';
  171. $expected = [$modelName => ['id', 'author_id', 'title', 'created']];
  172. $result = $this->_db->schema(new Query(compact('model')));
  173. $this->assertEqual($expected, $result);
  174. $query = new Query(compact('model') + ['fields' => '*']);
  175. $result = $this->_db->schema($query);
  176. $this->assertEqual($expected, $result);
  177. $query = new Query([
  178. 'type' => 'read',
  179. 'model' => $this->_model,
  180. 'fields' => ['MockDatabaseComment'],
  181. 'with' => ['MockDatabaseComment']
  182. ]);
  183. $expected = [
  184. '' => ['id'],
  185. 'MockDatabaseComment' => [
  186. 'id', 'post_id', 'author_id', 'body', 'created'
  187. ]
  188. ];
  189. $result = $this->_db->schema($query);
  190. $this->assertEqual($expected, $result);
  191. $options = [
  192. 'type' => 'read',
  193. 'model' => $this->_model,
  194. 'with' => 'MockDatabaseComment'
  195. ];
  196. $options['fields'] = ['id', 'title'];
  197. $result = $this->_db->schema(new Query($options));
  198. $expected = [$modelName => $options['fields']];
  199. $this->assertEqual($expected, $result);
  200. $options['fields'] = [
  201. 'MockDatabasePost.id',
  202. 'MockDatabasePost.title',
  203. 'MockDatabaseComment.body'
  204. ];
  205. $result = $this->_db->schema(new Query($options));
  206. $expected = [
  207. $modelName => ['id', 'title'],
  208. 'MockDatabaseComment' => ['body']
  209. ];
  210. $this->assertEqual($expected, $result);
  211. $options['fields'] = [
  212. 'MockDatabasePost' => ['id', 'title'],
  213. 'MockDatabaseComment' => ['body', 'created']
  214. ];
  215. $result = $this->_db->schema(new Query($options));
  216. $expected = [
  217. $modelName => ['id', 'title'],
  218. 'MockDatabaseComment' => ['body', 'created']
  219. ];
  220. $this->assertEqual($expected, $result);
  221. $options['fields'] = ['MockDatabasePost', 'MockDatabaseComment'];
  222. $result = $this->_db->schema(new Query($options));
  223. $expected = [
  224. $modelName => ['id', 'author_id', 'title', 'created'],
  225. 'MockDatabaseComment' => ['id', 'post_id', 'author_id', 'body', 'created']
  226. ];
  227. $this->assertEqual($expected, $result);
  228. }
  229. public function testSchemaFromManualFieldList() {
  230. $fields = ['id', 'name', 'created'];
  231. $result = $this->_db->schema(new Query(compact('fields')));
  232. $this->assertEqual(['' => $fields], $result);
  233. }
  234. public function testSimpleQueryRender() {
  235. $fieldList = '{MockDatabasePost}.{id}, {MockDatabasePost}.{title},';
  236. $fieldList .= ' {MockDatabasePost}.{created}';
  237. $table = '{mock_database_posts} AS {MockDatabasePost}';
  238. $result = $this->_db->renderCommand(new Query([
  239. 'type' => 'read',
  240. 'model' => $this->_model,
  241. 'fields' => ['id', 'title', 'created']
  242. ]));
  243. $this->assertEqual("SELECT {$fieldList} FROM {$table};", $result);
  244. $result = $this->_db->renderCommand(new Query([
  245. 'type' => 'read',
  246. 'model' => $this->_model,
  247. 'fields' => ['id', 'title', 'created'],
  248. 'limit' => 1
  249. ]));
  250. $this->assertEqual("SELECT {$fieldList} FROM {$table} LIMIT 1;", $result);
  251. $result = $this->_db->renderCommand(new Query([
  252. 'type' => 'read',
  253. 'model' => $this->_model,
  254. 'fields' => ['id', 'title', 'created'],
  255. 'limit' => 1,
  256. 'conditions' => 'Post.id = 2'
  257. ]));
  258. $this->assertEqual("SELECT {$fieldList} FROM {$table} WHERE Post.id = 2 LIMIT 1;", $result);
  259. }
  260. public function testNestedQueryConditions() {
  261. $query = new Query([
  262. 'type' => 'read',
  263. 'model' => $this->_model,
  264. 'fields' => ['MockDatabasePost.title', 'MockDatabasePost.body'],
  265. 'conditions' => ['Post.id' => new Query([
  266. 'type' => 'read',
  267. 'fields' => ['post_id'],
  268. 'model' => 'lithium\tests\mocks\data\model\MockDatabaseTagging',
  269. 'conditions' => ['MockDatabaseTag.tag' => ['foo', 'bar', 'baz']]
  270. ])]
  271. ]);
  272. $result = $this->_db->renderCommand($query);
  273. $expected = "SELECT {MockDatabasePost}.{title}, {MockDatabasePost}.{body} FROM";
  274. $expected .= " {mock_database_posts} AS {MockDatabasePost} WHERE {Post}.{id} IN";
  275. $expected .= " (SELECT {MockDatabaseTagging}.{post_id} FROM {mock_database_taggings} AS ";
  276. $expected .= "{MockDatabaseTagging} WHERE {MockDatabaseTag}.{tag} IN";
  277. $expected .= " ('foo', 'bar', 'baz'));";
  278. $this->assertEqual($expected, $result);
  279. $query = new Query([
  280. 'type' => 'read',
  281. 'model' => $this->_model,
  282. 'fields' => ['MockDatabasePost.title', 'MockDatabasePost.body'],
  283. 'conditions' => ['Post.id' => ['!=' => new Query([
  284. 'type' => 'read',
  285. 'fields' => ['post_id'],
  286. 'model' => 'lithium\tests\mocks\data\model\MockDatabaseTagging',
  287. 'conditions' => ['MockDatabaseTag.tag' => ['foo', 'bar', 'baz']]
  288. ])]]
  289. ]);
  290. $result = $this->_db->renderCommand($query);
  291. $expected = "SELECT {MockDatabasePost}.{title}, {MockDatabasePost}.{body} FROM";
  292. $expected .= " {mock_database_posts} AS {MockDatabasePost} WHERE ({Post}.{id} NOT IN";
  293. $expected .= " (SELECT {MockDatabaseTagging}.{post_id} FROM {mock_database_taggings} AS ";
  294. $expected .= "{MockDatabaseTagging} WHERE {MockDatabaseTag}.{tag} IN ";
  295. $expected .= "('foo', 'bar', 'baz')));";
  296. $this->assertEqual($expected, $result);
  297. $query = new Query([
  298. 'type' => 'read', 'model' => $this->_model,
  299. 'conditions' => [
  300. 'or' => [
  301. '{MockDatabasePost}.{id}' => 'value1',
  302. '{MockDatabasePost}.{title}' => 'value2'
  303. ]
  304. ]
  305. ]);
  306. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ";
  307. $sql .= "({MockDatabasePost}.{id} = 'value1' OR {MockDatabasePost}.{title} = 'value2');";
  308. $this->assertEqual($sql, $this->_db->renderCommand($query));
  309. }
  310. public function testCastingQueryConditionsWithSchemaWithAlias() {
  311. $query = new Query([
  312. 'type' => 'read',
  313. 'model' => $this->_model,
  314. 'conditions' => [
  315. 'MockDatabasePost.title' => '007'
  316. ]
  317. ]);
  318. $result = $this->_db->renderCommand($query);
  319. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ";
  320. $sql .= "{MockDatabasePost}.{title} = '007';";
  321. $this->assertEqual($sql, $result);
  322. }
  323. public function testQueryJoin() {
  324. $query = new Query([
  325. 'type' => 'read',
  326. 'model' => $this->_model,
  327. 'fields' => ['MockDatabasePost.title', 'MockDatabasePost.body'],
  328. 'conditions' => ['MockDatabaseTag.tag' => ['foo', 'bar', 'baz']],
  329. 'joins' => [new Query([
  330. 'model' => 'lithium\tests\mocks\data\model\MockDatabaseTag',
  331. 'constraints' => '{MockDatabaseTagging}.{tag_id} = {MockDatabaseTag}.{id}'
  332. ])]
  333. ]);
  334. $result = $this->_db->renderCommand($query);
  335. $expected = "SELECT {MockDatabasePost}.{title}, {MockDatabasePost}.{body} FROM";
  336. $expected .= " {mock_database_posts} AS {MockDatabasePost} JOIN {mock_database_tags} AS";
  337. $expected .= " {MockDatabaseTag} ON ";
  338. $expected .= "{MockDatabaseTagging}.{tag_id} = {MockDatabaseTag}.{id}";
  339. $expected .= " WHERE {MockDatabaseTag}.{tag} IN ('foo', 'bar', 'baz');";
  340. $this->assertEqual($expected, $result);
  341. }
  342. public function testItem() {
  343. $model = $this->_model;
  344. $data = ['title' => 'new post', 'content' => 'This is a new post.'];
  345. $result = $model::create($data, ['defaults' => false]);
  346. $this->assertEqual($data, $result->data());
  347. }
  348. public function testCreate() {
  349. $entity = new Record([
  350. 'model' => $this->_model,
  351. 'data' => ['title' => 'new post', 'body' => 'the body']
  352. ]);
  353. $query = new Query(compact('entity') + [
  354. 'type' => 'create',
  355. 'model' => $this->_model
  356. ]);
  357. $hash = $query->export($this->_db);
  358. ksort($hash);
  359. $expected = sha1(serialize($hash));
  360. $result = $this->_db->create($query);
  361. $this->assertTrue($result);
  362. $result = $query->entity()->id;
  363. $this->assertEqual($expected, $result);
  364. $expected = "INSERT INTO {mock_database_posts} ({title}, {body})";
  365. $expected .= " VALUES ('new post', 'the body');";
  366. $result = $this->_db->sql;
  367. $this->assertEqual($expected, $result);
  368. }
  369. public function testCreateGenericSyntax() {
  370. $entity = new Record([
  371. 'model' => $this->_model,
  372. 'data' => ['data' => ['title' => 'new post', 'body' => 'the body']]
  373. ]);
  374. $query = new Query(compact('entity') + [
  375. 'type' => 'create',
  376. 'model' => $this->_model
  377. ]);
  378. $hash = $query->export($this->_db);
  379. ksort($hash);
  380. $expected = sha1(serialize($hash));
  381. $result = $this->_db->create($query);
  382. $this->assertTrue($result);
  383. $result = $query->entity()->id;
  384. $this->assertEqual($expected, $result);
  385. $expected = "INSERT INTO {mock_database_posts} ({title}, {body})";
  386. $expected .= " VALUES ('new post', 'the body');";
  387. $result = $this->_db->sql;
  388. $this->assertEqual($expected, $result);
  389. }
  390. public function testCreateWithValueBySchema() {
  391. $entity = new Record([
  392. 'model' => $this->_model,
  393. 'data' => ['title' => '007', 'body' => 'the body']
  394. ]);
  395. $query = new Query(compact('entity') + [
  396. 'type' => 'create',
  397. 'model' => $this->_model
  398. ]);
  399. $hash = $query->export($this->_db);
  400. ksort($hash);
  401. $expected = sha1(serialize($hash));
  402. $result = $this->_db->create($query);
  403. $this->assertTrue($result);
  404. $result = $query->entity()->id;
  405. $this->assertEqual($expected, $result);
  406. $expected = "INSERT INTO {mock_database_posts} ({title}, {body})";
  407. $expected .= " VALUES ('007', 'the body');";
  408. $result = $this->_db->sql;
  409. $this->assertEqual($expected, $result);
  410. }
  411. public function testCreateWithKey() {
  412. $entity = new Record([
  413. 'model' => $this->_model,
  414. 'data' => ['id' => 1, 'title' => 'new post', 'body' => 'the body']
  415. ]);
  416. $query = new Query(compact('entity') + ['type' => 'create']);
  417. $expected = 1;
  418. $result = $this->_db->create($query);
  419. $this->assertTrue($result);
  420. $result = $query->entity()->id;
  421. $this->assertEqual($expected, $result);
  422. $expected = "INSERT INTO {mock_database_posts} ({id}, {title}, {body})";
  423. $expected .= " VALUES (1, 'new post', 'the body');";
  424. $this->assertEqual($expected, $this->_db->sql);
  425. }
  426. public function testReadWithQueryStringReturnResource() {
  427. $result = $this->_db->read('SELECT * from mock_database_posts AS MockDatabasePost;', [
  428. 'return' => 'resource'
  429. ]);
  430. $this->assertNotEmpty($result);
  431. $expected = "SELECT * from mock_database_posts AS MockDatabasePost;";
  432. $this->assertEqual($expected, $this->_db->sql);
  433. }
  434. /**
  435. * @link https://github.com/UnionOfRAD/lithium/issues/1281
  436. */
  437. public function testCalculation() {
  438. $options = [
  439. 'type' => 'read',
  440. 'model' => $this->_model
  441. ];
  442. $this->_db->return['_execute'] = new MockResult([
  443. 'records' => [
  444. [23]
  445. ]
  446. ]);
  447. $expected = 23;
  448. $result = $this->_db->calculation('count', new Query($options), $options);
  449. $this->assertEqual($expected, $result);
  450. $expected = 'SELECT COUNT(*) as count FROM {mock_database_posts} AS {MockDatabasePost};';
  451. $result = $this->_db->sql;
  452. $this->assertEqual($expected, $result);
  453. $this->_db->return['_execute'] = new MockResult([
  454. 'records' => []
  455. ]);
  456. $result = $this->_db->calculation('count', new Query($options), $options);
  457. $this->assertNull($result);
  458. }
  459. public function testReadWithQueryStringReturnArrayWithSchema() {
  460. $result = $this->_db->read('SELECT * FROM {:table} WHERE user_id = {:uid};', [
  461. 'table' => 'mock_database_posts',
  462. 'uid' => '3',
  463. 'schema' => ['id', 'title', 'text']
  464. ]);
  465. $expected = 'SELECT * FROM \'mock_database_posts\' WHERE user_id = 3;';
  466. $this->assertEqual($expected, $this->_db->sql);
  467. }
  468. public function testReadWithQueryObjectRecordSet() {
  469. $query = new Query(['type' => 'read', 'model' => $this->_model]);
  470. $result = $this->_db->read($query);
  471. $this->assertInstanceOf('lithium\data\collection\RecordSet', $result);
  472. $expected = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost};";
  473. $result = $this->_db->sql;
  474. $this->assertEqual($expected, $result);
  475. }
  476. public function testReadWithQueryObjectArray() {
  477. $query = new Query(['type' => 'read', 'model' => $this->_model]);
  478. $result = $this->_db->read($query, ['return' => 'array']);
  479. $this->assertInternalType('array', $result);
  480. $expected = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost};";
  481. $result = $this->_db->sql;
  482. $this->assertEqual($expected, $result);
  483. }
  484. public function testUpdate() {
  485. $entity = new Record([
  486. 'model' => $this->_model,
  487. 'data' => ['id' => 1, 'title' => 'the post', 'body' => 'the body'],
  488. 'exists' => true
  489. ]);
  490. $entity->title = 'new post';
  491. $entity->body = 'new body';
  492. $query = new Query(compact('entity') + ['type' => 'update']);
  493. $result = $this->_db->update($query);
  494. $this->assertTrue($result);
  495. $this->assertEqual(1, $query->entity()->id);
  496. $expected = "UPDATE {mock_database_posts} SET";
  497. $expected .= " {title} = 'new post', {body} = 'new body' WHERE {id} = 1;";
  498. $this->assertEqual($expected, $this->_db->sql);
  499. $entity = new Record([
  500. 'model' => $this->_model,
  501. 'data' => ['id' => 2, 'count' => 10],
  502. 'exists' => true
  503. ]);
  504. $entity->count = (object) '{count} + 1';
  505. $query = new Query(compact('entity') + ['type' => 'update']);
  506. $result = $this->_db->update($query);
  507. $this->assertTrue($result);
  508. $this->assertEqual(2, $query->entity()->id);
  509. $expected = "UPDATE {mock_database_posts} SET";
  510. $expected .= " {count} = {count} + 1 WHERE {id} = 2;";
  511. $this->assertEqual($expected, $this->_db->sql);
  512. $query = new Query([
  513. 'type' => 'update',
  514. 'data' => ['modified' => (object) 'NOW()'],
  515. 'model' => $this->_model
  516. ]);
  517. $sql = "UPDATE {mock_database_posts} SET {modified} = NOW();";
  518. $this->assertEqual($sql, $this->_db->renderCommand($query));
  519. }
  520. public function testDelete() {
  521. $entity = new Record([
  522. 'model' => $this->_model,
  523. 'data' => ['id' => 1, 'title' => 'new post', 'body' => 'the body'],
  524. 'exists' => true
  525. ]);
  526. $query = new Query(compact('entity') + ['type' => 'delete']);
  527. $this->assertTrue($entity->exists());
  528. $this->assertTrue($this->_db->delete($query));
  529. $this->assertEqual(1, $query->entity()->id);
  530. $expected = "DELETE FROM {mock_database_posts} WHERE {id} = 1;";
  531. $this->assertEqual($expected, $this->_db->sql);
  532. $this->assertFalse($entity->exists());
  533. }
  534. public function testOrder() {
  535. $query = new Query(['model' => $this->_model]);
  536. $result = $this->_db->order("foo_bar", $query);
  537. $expected = 'ORDER BY {foo_bar} ASC';
  538. $this->assertEqual($expected, $result);
  539. $result = $this->_db->order("title", $query);
  540. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC';
  541. $this->assertEqual($expected, $result);
  542. $result = $this->_db->order("title", $query);
  543. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC';
  544. $this->assertEqual($expected, $result);
  545. $result = $this->_db->order(["title"], $query);
  546. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC';
  547. $this->assertEqual($expected, $result);
  548. $result = $this->_db->order(["title" => "desc"], $query);
  549. $expected = 'ORDER BY {MockDatabasePost}.{title} DESC';
  550. $this->assertEqual($expected, $result);
  551. $result = $this->_db->order(["title" => "dasc"], $query);
  552. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC';
  553. $this->assertEqual($expected, $result);
  554. $result = $this->_db->order(["title" => []], $query);
  555. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC';
  556. $this->assertEqual($expected, $result);
  557. $result = $this->_db->order(['author_id', "title" => "DESC"], $query);
  558. $expected = 'ORDER BY {MockDatabasePost}.{author_id} ASC, {MockDatabasePost}.{title} DESC';
  559. $this->assertEqual($expected, $result);
  560. $result = $this->_db->order([], $query);
  561. $expected = '';
  562. $this->assertEqual($expected, $result);
  563. }
  564. public function testOrderOnRelated() {
  565. $query = new Query([
  566. 'model' => $this->_model,
  567. 'with' => ['MockDatabaseComment']
  568. ]);
  569. $result = $this->_db->order('MockDatabaseComment.created DESC', $query);
  570. $expected = 'ORDER BY {MockDatabaseComment}.{created} DESC';
  571. $this->assertEqual($expected, $result);
  572. $result = $this->_db->order(['MockDatabaseComment.created' => 'DESC'], $query);
  573. $expected = 'ORDER BY {MockDatabaseComment}.{created} DESC';
  574. $this->assertEqual($expected, $result);
  575. $result = $this->_db->order(
  576. [
  577. 'MockDatabasePost.title' => 'ASC',
  578. 'MockDatabaseComment.created' => 'DESC'
  579. ],
  580. $query
  581. );
  582. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC, {MockDatabaseComment}.{created} DESC';
  583. $this->assertEqual($expected, $result);
  584. $result = $this->_db->order(
  585. [
  586. 'title' => 'ASC',
  587. 'MockDatabaseComment.created' => 'DESC'
  588. ],
  589. $query
  590. );
  591. $expected = 'ORDER BY {MockDatabasePost}.{title} ASC, {MockDatabaseComment}.{created} DESC';
  592. $this->assertEqual($expected, $result);
  593. }
  594. public function testScopedDelete() {
  595. $query = new Query([
  596. 'type' => 'delete',
  597. 'conditions' => ['published' => false],
  598. 'model' => $this->_model
  599. ]);
  600. $sql = 'DELETE FROM {mock_database_posts} WHERE {published} = 0;';
  601. $this->assertEqual($sql, $this->_db->renderCommand($query));
  602. }
  603. public function testScopedUpdate() {
  604. $query = new Query([
  605. 'type' => 'update',
  606. 'conditions' => ['expires' => ['>=' => '2010-05-13']],
  607. 'data' => ['published' => false, 'comments' => null],
  608. 'model' => $this->_model
  609. ]);
  610. $sql = "UPDATE {mock_database_posts} SET {published} = 0, {comments} = NULL WHERE ";
  611. $sql .= "({expires} >= '2010-05-13');";
  612. $this->assertEqual($sql, $this->_db->renderCommand($query));
  613. }
  614. public function testQueryOperators() {
  615. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  616. 'score' => ['between' => [90, 100]]
  617. ]]);
  618. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ({score} ";
  619. $sql .= "BETWEEN 90 AND 100);";
  620. $this->assertEqual($sql, $this->_db->renderCommand($query));
  621. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  622. 'score' => ['not between' => [90, 100]]
  623. ]]);
  624. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ({score} ";
  625. $sql .= "NOT BETWEEN 90 AND 100);";
  626. $this->assertEqual($sql, $this->_db->renderCommand($query));
  627. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  628. 'score' => ['>' => 90, '<' => 100]
  629. ]]);
  630. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ";
  631. $sql .= "({score} > 90 AND {score} < 100);";
  632. $this->assertEqual($sql, $this->_db->renderCommand($query));
  633. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  634. 'score' => ['!=' => [98, 99, 100]]
  635. ]]);
  636. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} ";
  637. $sql .= "WHERE ({score} NOT IN (98, 99, 100));";
  638. $this->assertEqual($sql, $this->_db->renderCommand($query));
  639. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  640. 'scorer' => ['like' => '%howard%']
  641. ]]);
  642. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} ";
  643. $sql .= "WHERE ({scorer} LIKE '%howard%');";
  644. $this->assertEqual($sql, $this->_db->renderCommand($query));
  645. $conditions = "custom conditions string";
  646. $query = new Query(compact('conditions') + [
  647. 'type' => 'read', 'model' => $this->_model
  648. ]);
  649. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE {$conditions};";
  650. $this->assertEqual($sql, $this->_db->renderCommand($query));
  651. $query = new Query([
  652. 'type' => 'read', 'model' => $this->_model,
  653. 'conditions' => [
  654. 'field' => ['like' => '%value%', 'not like' => '%value2%']
  655. ]
  656. ]);
  657. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ";
  658. $sql .= "({field} LIKE '%value%' AND {field} NOT LIKE '%value2%');";
  659. $this->assertEqual($sql, $this->_db->renderCommand($query));
  660. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  661. 'scorer' => ['is' => null]
  662. ]]);
  663. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} ";
  664. $sql .= "WHERE ({scorer} IS NULL);";
  665. $this->assertEqual($sql, $this->_db->renderCommand($query));
  666. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => [
  667. 'scorer' => ['is not' => null]
  668. ]]);
  669. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} ";
  670. $sql .= "WHERE ({scorer} IS NOT NULL);";
  671. $this->assertEqual($sql, $this->_db->renderCommand($query));
  672. }
  673. public function testConditions() {
  674. $query = new Query([
  675. 'type' => 'read', 'model' => $this->_model,
  676. 'conditions' => [
  677. 'or' => [
  678. 'id' => 'value1',
  679. 'title' => 'value2',
  680. 'and' => [
  681. 'author_id' => '1',
  682. 'created' => '2012-05-25 23:41:00'
  683. ],
  684. ['title' => 'value2'],
  685. ['title' => null]
  686. ],
  687. 'id' => '3',
  688. 'author_id' => false
  689. ]
  690. ]);
  691. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ";
  692. $sql .= "({MockDatabasePost}.{id} = 0 OR {MockDatabasePost}.{title} = 'value2' OR ";
  693. $sql .= "({MockDatabasePost}.{author_id} = 1 AND {MockDatabasePost}.{created} = ";
  694. $sql .= "'2012-05-25 23:41:00') OR ({MockDatabasePost}.{title} = 'value2') OR ";
  695. $sql .= "({MockDatabasePost}.{title} IS NULL)) AND {MockDatabasePost}.{id} = 3 AND ";
  696. $sql .= "{MockDatabasePost}.{author_id} = 0;";
  697. $this->assertEqual($sql, $this->_db->renderCommand($query));
  698. $query = new Query([
  699. 'type' => 'read', 'model' => $this->_model,
  700. 'conditions' => ['title' => ['0900']]
  701. ]);
  702. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost}';
  703. $sql .= ' WHERE {title} IN (\'0900\');';
  704. $this->assertEqual($sql, $this->_db->renderCommand($query));
  705. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ';
  706. $sql .= 'lower(title) = \'test\';';
  707. $query = new Query([
  708. 'type' => 'read', 'model' => $this->_model,
  709. 'conditions' => ['lower(title)' => 'test']
  710. ]);
  711. $this->assertEqual($sql, $this->_db->renderCommand($query));
  712. $query = new Query([
  713. 'type' => 'read', 'model' => $this->_model,
  714. 'conditions' => [(object) 'lower(title) = \'test\'']
  715. ]);
  716. $this->assertEqual($sql, $this->_db->renderCommand($query));
  717. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} WHERE ';
  718. $sql .= 'lower(title) = REGEXP \'^test$\';';
  719. $query = new Query([
  720. 'type' => 'read', 'model' => $this->_model,
  721. 'conditions' => [(object) 'lower(title) = REGEXP \'^test$\'']
  722. ]);
  723. $this->assertEqual($sql, $this->_db->renderCommand($query));
  724. $query = new Query([
  725. 'type' => 'read', 'model' => $this->_model,
  726. 'conditions' => ['lower(title)' => (object) 'REGEXP \'^test$\'']
  727. ]);
  728. $this->assertEqual($sql, $this->_db->renderCommand($query));
  729. }
  730. public function testHaving() {
  731. $query = new Query([
  732. 'type' => 'read', 'model' => $this->_model,
  733. 'having' => [
  734. 'or' => [
  735. 'id' => 'value1',
  736. 'title' => 'value2',
  737. 'and' => [
  738. 'author_id' => '1',
  739. 'created' => '2012-05-25 23:41:00'
  740. ],
  741. ['title' => 'value2'],
  742. ['title' => null]
  743. ],
  744. 'id' => '3',
  745. 'author_id' => false
  746. ]
  747. ]);
  748. $sql = "SELECT * FROM {mock_database_posts} AS {MockDatabasePost} HAVING ";
  749. $sql .= "({MockDatabasePost}.{id} = 0 OR {MockDatabasePost}.{title} = 'value2' OR ";
  750. $sql .= "({MockDatabasePost}.{author_id} = 1 AND {MockDatabasePost}.{created} = ";
  751. $sql .= "'2012-05-25 23:41:00') OR ({MockDatabasePost}.{title} = 'value2') OR ";
  752. $sql .= "({MockDatabasePost}.{title} IS NULL)) AND {MockDatabasePost}.{id} = 3 AND ";
  753. $sql .= "{MockDatabasePost}.{author_id} = 0;";
  754. $this->assertEqual($sql, $this->_db->renderCommand($query));
  755. $query = new Query([
  756. 'type' => 'read', 'model' => $this->_model,
  757. 'having' => ['title' => ['0900']]
  758. ]);
  759. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost}';
  760. $sql .= ' HAVING {title} IN (\'0900\');';
  761. $this->assertEqual($sql, $this->_db->renderCommand($query));
  762. }
  763. public function testConstraints() {
  764. $model = $this->_model;
  765. $query = new Query([
  766. 'type' => 'read',
  767. 'model' => $this->_model,
  768. 'with' => [
  769. 'MockDatabaseComment' => [
  770. 'constraints' => [
  771. 'or' => [
  772. ['custom_id' => 'MockDatabasePost.value_id'],
  773. ['custom_id' => 'id'],
  774. 'and' => [
  775. 'id' => 'MockDatabasePost.id',
  776. 'title' => 'MockDatabasePost.title'
  777. ],
  778. ['title' => (object) $this->_db->value('value2')],
  779. ['title' => null]
  780. ],
  781. 'id' => 5
  782. ]
  783. ]
  784. ]
  785. ]);
  786. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LEFT JOIN ';
  787. $sql .= '{mock_database_comments} AS {MockDatabaseComment} ON ';
  788. $sql .= '(({MockDatabasePost}.{custom_id} = {MockDatabasePost}.{value_id}) OR ';
  789. $sql .= '({MockDatabasePost}.{custom_id} = {MockDatabaseComment}.{id}) OR ';
  790. $sql .= '({MockDatabasePost}.{id} = {MockDatabasePost}.{id} ';
  791. $sql .= 'AND {MockDatabasePost}.{title} = {MockDatabasePost}.{title}) ';
  792. $sql .= 'OR ({MockDatabasePost}.{title} = \'value2\') ';
  793. $sql .= 'OR ({MockDatabasePost}.{title} IS NULL)) AND {MockDatabasePost}.{id} = 5;';
  794. $this->assertEqual($sql, $this->_db->renderCommand($query));
  795. }
  796. public function testReadConditionsWithModel() {
  797. $model = $this->_model;
  798. $options = [
  799. 'type' => 'read',
  800. 'model' => $this->_model,
  801. 'conditions' => ['id' => 1, 'MockDatabaseComment.id' => 2],
  802. 'with' => ['MockDatabaseComment']
  803. ];
  804. $result = $this->_db->read(new Query($options), $options);
  805. $expected = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LEFT JOIN ';
  806. $expected .= '{mock_database_comments} AS {MockDatabaseComment} ON ';
  807. $expected .= '{MockDatabasePost}.{id} = {MockDatabaseComment}.{mock_database_post_id} ';
  808. $expected .= 'WHERE {MockDatabasePost}.{id} = 1 AND {MockDatabaseComment}.{id} = 2;';
  809. $this->assertEqual($expected, $this->_db->sql);
  810. }
  811. public function testFields() {
  812. $query = new Query([
  813. 'type' => 'read',
  814. 'model' => $this->_model,
  815. 'with' => ['MockDatabaseComment']
  816. ]);
  817. $fields = ['id', 'title'];
  818. $result = $this->_db->fields($fields, $query);
  819. $expected = '{MockDatabasePost}.{id}, {MockDatabasePost}.{title}';
  820. $this->assertEqual($expected,$result);
  821. $fields = [
  822. 'MockDatabasePost' => ['id', 'title', 'created'],
  823. 'MockDatabaseComment' => ['body']
  824. ];
  825. $result = $this->_db->fields($fields, $query);
  826. $expected = '{MockDatabasePost}.{id}, {MockDatabasePost}.{title},';
  827. $expected .= ' {MockDatabasePost}.{created}, {MockDatabaseComment}.{body}';
  828. $this->assertEqual($expected,$result);
  829. $fields = ['MockDatabasePost', 'MockDatabaseComment'];
  830. $result = $this->_db->fields($fields, $query);
  831. $expected = '{MockDatabasePost}.*, {MockDatabaseComment}.*';
  832. $this->assertEqual($expected, $result);
  833. $fields = ['MockDatabasePost.id as idPost', 'MockDatabaseComment.id AS idComment'];
  834. $result = $this->_db->fields($fields, $query);
  835. $expected = '{MockDatabasePost}.{id} as idPost, {MockDatabaseComment}.{id} as idComment';
  836. $this->assertEqual($expected, $result);
  837. $expected = ['' => ['idPost'], 'MockDatabaseComment' => ['idComment']];
  838. $this->assertEqual($expected, $query->map());
  839. $fields = [['count(MockDatabasePost.id)']];
  840. $expected = 'count(MockDatabasePost.id)';
  841. $result = $this->_db->fields($fields, $query);
  842. $this->assertEqual($expected, $result);
  843. $fields = [[(object) 'count(MockDatabasePost.id)']];
  844. $expected = 'count(MockDatabasePost.id)';
  845. $result = $this->_db->fields($fields, $query);
  846. $this->assertEqual($expected, $result);
  847. }
  848. public function testFieldsWithEmptyAlias() {
  849. $query = new Query();
  850. $result = $this->_db->fields(['id', 'name', 'created'], $query);
  851. $expected = '{id}, {name}, {created}';
  852. $this->assertEqual($expected, $result);
  853. }
  854. public function testRawConditions() {
  855. $query = new Query(['type' => 'read', 'model' => $this->_model, 'conditions' => null]);
  856. $this->assertEmpty($this->_db->conditions(5, $query));
  857. $this->assertEmpty($this->_db->conditions(null, $query));
  858. $this->assertEqual("WHERE CUSTOM", $this->_db->conditions("CUSTOM", $query));
  859. }
  860. public function testRawHaving() {
  861. $query = new Query(['type' => 'read', 'model' => $this->_model, 'having' => null]);
  862. $this->assertEmpty($this->_db->having(5, $query));
  863. $this->assertEmpty($this->_db->having(null, $query));
  864. $this->assertEqual("HAVING CUSTOM", $this->_db->having("CUSTOM", $query));
  865. }
  866. /**
  867. * Verifies that setting options using a raw SQL string works, when
  868. * the operation returns no result.
  869. *
  870. * @link https://github.com/UnionOfRAD/lithium/issues/1210
  871. */
  872. public function testRawOptionSettingWithNoResultResource() {
  873. $expected = [];
  874. $result = $this->_db->read('SET SESSION group_concat_max_len = 100000;');
  875. $this->assertEqual($expected, $result);
  876. }
  877. public function testRelationshipGeneration() {
  878. $comment = 'lithium\tests\mocks\data\model\MockDatabaseComment';
  879. $hasMany = $this->_db->relationship($this->_model, 'hasMany', 'Comments', [
  880. 'to' => $comment
  881. ]);
  882. $this->assertEqual(['id' => 'mock_database_post_id'], $hasMany->key());
  883. $this->assertEqual('comments', $hasMany->fieldName());
  884. $belongsTo = $this->_db->relationship($comment, 'belongsTo', 'Posts', [
  885. 'to' => $this->_model
  886. ]);
  887. $this->assertEqual(['post_id' => 'id'], $belongsTo->key());
  888. $this->assertEqual('post', $belongsTo->fieldName());
  889. }
  890. public function testRelationshipGenerationWithNullConstraint() {
  891. $postRevision = 'lithium\tests\mocks\data\model\MockDatabasePostRevision';
  892. $hasMany = $this->_db->relationship($this->_model, 'hasMany', 'PostRevisions', [
  893. 'to' => $postRevision,
  894. 'constraints' => ['MockDatabasePostRevision.deleted' => null]
  895. ]);
  896. $this->assertEqual(['id' => 'mock_database_post_id'], $hasMany->key());
  897. $this->assertEqual('post_revisions', $hasMany->fieldName());
  898. $expected = [
  899. 'MockDatabasePostRevision.deleted' => null,
  900. 'MockDatabasePost.id' => 'PostRevisions.mock_database_post_id'
  901. ];
  902. $result = $this->_db->on($hasMany);
  903. $this->assertEqual($expected, $result);
  904. $belongsTo = $this->_db->relationship($postRevision, 'belongsTo', 'Posts', [
  905. 'to' => $this->_model
  906. ]);
  907. $this->assertEqual(['post_id' => 'id'], $belongsTo->key());
  908. $this->assertEqual('post', $belongsTo->fieldName());
  909. }
  910. public function testInvalidQueryType() {
  911. $db = $this->_db;
  912. $this->assertException('Invalid query type `fakeType`.', function() use ($db) {
  913. $db->read(new Query(['type' => 'fakeType']));
  914. });
  915. }
  916. public function testReadWithRelationship() {
  917. $options = [
  918. 'type' => 'read',
  919. 'model' => $this->_model,
  920. 'with' => ['MockDatabaseComment']
  921. ];
  922. $result = $this->_db->read(new Query($options), $options);
  923. $expected = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LEFT JOIN ';
  924. $expected .= '{mock_database_comments} AS {MockDatabaseComment} ON ';
  925. $expected .= '{MockDatabasePost}.{id} = {MockDatabaseComment}.{mock_database_post_id};';
  926. $this->assertEqual($expected, $this->_db->sql);
  927. }
  928. public function testReadWithRelationshipWithNullConstraint() {
  929. $options = [
  930. 'type' => 'read',
  931. 'model' => $this->_model,
  932. 'with' => ['MockDatabasePostRevision']
  933. ];
  934. $result = $this->_db->read(new Query($options), $options);
  935. $expected = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LEFT JOIN ';
  936. $expected .= '{mock_database_post_revisions} AS {MockDatabasePostRevision} ON ';
  937. $expected .= '{MockDatabasePostRevision}.{deleted} IS NULL AND ';
  938. $expected .= '{MockDatabasePost}.{id} = {MockDatabasePostRevision}.';
  939. $expected .= '{mock_database_post_id};';
  940. $this->assertEqual($expected, $this->_db->sql);
  941. }
  942. public function testReadWithHasManyAndLimit() {
  943. $options = [
  944. 'type' => 'read',
  945. 'model' => $this->_model,
  946. 'with' => ['MockDatabaseComment'],
  947. 'limit' => 1
  948. ];
  949. $result = $this->_db->read(new Query($options), $options);
  950. $this->assertNotInstanceOf('lithium\data\collection\RecordSet', $result);
  951. }
  952. public function testGroup() {
  953. $query = new Query([
  954. 'type' => 'read', 'model' => $this->_model
  955. ]);
  956. $result = $this->_db->group(['id'], $query);
  957. $expected = 'GROUP BY {MockDatabasePost}.{id}';
  958. $this->assertEqual($expected, $result);
  959. }
  960. public function testGroupWithAlias() {
  961. $query = new Query([
  962. 'type' => 'read', 'model' => $this->_model, 'alias' => 'MyModel'
  963. ]);
  964. $result = $this->_db->group('id', $query);
  965. $expected = 'GROUP BY {MyModel}.{id}';
  966. $this->assertEqual($expected, $result);
  967. }
  968. public function testGroupOnRelation() {
  969. $query = new Query([
  970. 'type' => 'read',
  971. 'model' => $this->_comment,
  972. 'with' => 'MockDatabasePost',
  973. 'group' => ['MockDatabaseComment.id']
  974. ]);
  975. $sql = 'SELECT * FROM {mock_database_comments} AS {MockDatabaseComment} LEFT JOIN ';
  976. $sql .= '{mock_database_posts} AS {MockDatabasePost} ON {MockDatabaseComment}.';
  977. $sql .= '{mock_database_post_id} = {MockDatabasePost}.{id} GROUP BY ';
  978. $sql .= '{MockDatabaseComment}.{id};';
  979. $this->assertEqual($sql, $this->_db->renderCommand($query));
  980. }
  981. public function testLimit() {
  982. MockDatabasePost::find('all', ['limit' => 15]);
  983. $result = $this->_db->sql;
  984. $expected = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LIMIT 15;';
  985. $this->assertEqual($expected, $result);
  986. MockDatabasePost::find('all', ['limit' => 10, 'page' => 3]);
  987. $result = $this->_db->sql;
  988. $expected = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} LIMIT 10 OFFSET 20;';
  989. $this->assertEqual($expected, $result);
  990. }
  991. /**
  992. * Tests that various syntaxes for the `'order'` key of the query object produce the correct
  993. * SQL.
  994. */
  995. public function testQueryOrderSyntaxes() {
  996. $query = new Query([
  997. 'type' => 'read', 'model' => $this->_model, 'order' => ['created' => 'ASC']
  998. ]);
  999. $sql = 'SELECT * FROM {mock_database_posts} AS {MockDatabasePost} ';
  1000. $sql .= 'ORDER BY {MockDatabasePost}.{created} ASC;';
  1001. $this->assertEqual($sql, $this->_db->renderCommand($query));
  1002. }
  1003. /**
  1004. * Tests that complex model constraints with custom operators render correct constraint strings.
  1005. */
  1006. public function testRenderArrayJoinConstraintComplex() {
  1007. $model = 'lithium\tests\mocks\data\model\MockQueryComment';
  1008. $query = new Query(compact('model') + [
  1009. 'type' => 'read',
  1010. 'source' => 'comments',
  1011. 'alias' => 'Comments',
  1012. 'conditions' => ['Comment.id' => 1],
  1013. 'joins' => [[
  1014. 'mode' => 'INNER',
  1015. 'source' => 'posts',
  1016. 'alias' => 'Post',
  1017. 'constraints' => ['Comment.post_id' => ['<=' => 'Post.id']]
  1018. ]]
  1019. ]);
  1020. $expected = "SELECT * FROM {comments} AS {Comments} INNER JOIN {posts} AS {Post} ON ";
  1021. $expected .= "({Comment}.{post_id} <= {Post}.{id}) WHERE {Comment}.{id} = 1;";
  1022. $result = $this->_db->renderCommand($query);
  1023. $this->assertEqual($expected, $result);
  1024. }
  1025. /**
  1026. * Tests that complex model constraints with custom operators render correct constraint strings.
  1027. */
  1028. public function testRenderArrayJoinConstraintComplexArray() {
  1029. $model = 'lithium\tests\mocks\data\model\MockQueryComment';
  1030. $query = new Query(compact('model') + [
  1031. 'type' => 'read',
  1032. 'source' => 'comments',
  1033. 'alias' => 'Comments',
  1034. 'conditions' => ['Comment.id' => 1],
  1035. 'joins' => [[
  1036. 'mode' => 'LEFT',
  1037. 'source' => 'posts',
  1038. 'alias' => 'Post',
  1039. 'constraints' => [
  1040. "Comment.post_id" => [
  1041. '<=' => 'Post.id',
  1042. '>=' => 'Post.id'
  1043. ]
  1044. ]
  1045. ]]
  1046. ]);
  1047. $expected = "SELECT * FROM {comments} AS {Comments} LEFT JOIN {posts} AS {Post} ON ";
  1048. $expected .= "({Comment}.{post_id} <= {Post}.{id} AND {Comment}.{post_id} >= {Post}.{id}) ";
  1049. $expected .= "WHERE {Comment}.{id} = 1;";
  1050. $result = $this->_db->renderCommand($query);
  1051. $this->assertEqual($expected, $result);
  1052. $query = new Query(compact('model') + [
  1053. 'type' => 'read',
  1054. 'source' => 'comments',
  1055. 'alias' => 'Comments',
  1056. 'joins' => [[
  1057. 'mode' => 'LEFT',
  1058. 'source' => 'posts',
  1059. 'alias' => 'Post',
  1060. 'constraints' => [
  1061. 'Comment.post_id' => ['=>' => 'Post.id']
  1062. ]
  1063. ]]
  1064. ]);
  1065. $db = $this->_db;
  1066. $this->assertException("Unsupported operator `=>`.", function() use ($db, $query) {
  1067. $db->renderCommand($query);
  1068. });
  1069. }
  1070. public function testRenderArrayJoin() {
  1071. $model = 'lithium\tests\mocks\data\model\MockQueryComment';
  1072. $query = new Query(compact('model') + [
  1073. 'type' => 'read',
  1074. 'source' => 'comments',
  1075. 'alias' => 'Comment',
  1076. 'conditions' => ['Comment.id' => 1],
  1077. 'joins' => [[
  1078. 'mode' => 'INNER',
  1079. 'source' => 'posts',
  1080. 'alias' => 'Post',
  1081. 'constraints' => ['Comment.post_id' => 'Post.id']
  1082. ]]
  1083. ]);
  1084. $expected = "SELECT * FROM {comments} AS {Comment} INNER JOIN {posts} AS {Post} ON ";
  1085. $expected .= "{Comment}.{post_id} = {Post}.{id} WHERE {Comment}.{id} = 1;";
  1086. $result = $this->_db->renderCommand($query);
  1087. $this->assertEqual($expected, $result);
  1088. }
  1089. public function testModelFindBy() {
  1090. $this->_db->log = true;
  1091. MockDatabasePost::findById(5, ['with' => 'MockDatabaseComment']);
  1092. $this->_db->log = false;
  1093. $result = $this->_db->logs[0];
  1094. $expected = "SELECT DISTINCT({MockDatabasePost}.{id}) AS _ID_ FROM {mock_database_posts}";
  1095. $expected .= " AS {MockDatabasePost} LEFT JOIN {mock_database_comments} AS ";
  1096. $expected .= "{MockDatabaseComment} ON {MockDatabasePost}.{id} = ";
  1097. $expected .= "{MockDatabaseComment}.{mock_database_post_id} WHERE ";
  1098. $expected .= "{MockDatabasePost}.{id} = 5 LIMIT 1;";
  1099. $this->assertEqual($expected, $result);
  1100. }
  1101. /**
  1102. * Tests that when using LIMIT together with relation conditions and relationship,
  1103. * relation conditions are passed into the subsequent query issued.
  1104. *
  1105. * @link https://github.com/UnionOfRAD/lithium/pull/1099
  1106. */
  1107. public function testModelFindFirstPassesConditionsIntoSubsequent() {
  1108. $this->_db->log = true;
  1109. $this->_db->return['_execute'] = new MockResult([
  1110. 'records' => [
  1111. [0 => 5]
  1112. ]
  1113. ]);
  1114. MockDatabasePost::find('first', [
  1115. 'conditions' => [
  1116. 'id' => 5,
  1117. 'is_published' => true,
  1118. 'MockDatabaseComment.is_spam' => false
  1119. ],
  1120. 'with' => 'MockDatabaseComment'
  1121. ]);
  1122. $this->_db->log = false;
  1123. $result = $this->_db->logs;
  1124. $expected[0] = <<<SQL
  1125. SELECT DISTINCT({MockDatabasePost}.{id}) AS _ID_
  1126. FROM {mock_database_posts} AS {MockDatabasePost}
  1127. LEFT JOIN {mock_database_comments} AS {MockDatabaseComment}
  1128. ON {MockDatabasePost}.{id} = {MockDatabaseComment}.{mock_database_post_id}
  1129. WHERE
  1130. {MockDatabasePost}.{id} = 5
  1131. AND {MockDatabasePost}.{is_published} = 1
  1132. AND {MockDatabaseComment}.{is_spam} = 0
  1133. LIMIT 1;
  1134. SQL;
  1135. $expected[1] = <<<SQL
  1136. SELECT * FROM {mock_database_posts} AS {MockDatabasePost}
  1137. LEFT JOIN {mock_database_comments} AS {MockDatabaseComment}
  1138. ON {MockDatabasePost}.{id} = {MockDatabaseComment}.{mock_database_post_id}
  1139. WHERE
  1140. {MockDatabasePost}.{id} IN (5)
  1141. AND {MockDatabaseComment}.{is_spam} = 0;
  1142. SQL;
  1143. $expected = array_map(function($v) {
  1144. return preg_replace('/[\t\n]+/', ' ', $v);
  1145. }, $expected);
  1146. $this->assertEqual($expected, $result);
  1147. }
  1148. public function testSplitFieldname() {
  1149. $result = $this->_db->splitFieldname('Alias.fieldname');
  1150. $this->assertEqual(['Alias', 'fieldname'], $result);
  1151. $result = $this->_db->splitFieldname('fieldname');
  1152. $this->assertEqual([null, 'fieldname'], $result);
  1153. $result = $this->_db->splitFieldname('fieldname');
  1154. $this->assertEqual([null, 'fieldname'], $result);
  1155. $result = $this->_db->splitFieldname('lower(Alias.fieldname)');
  1156. $this->assertEqual([null, 'lower(Alias.fieldname)'], $result);
  1157. $result = $this->_db->splitFieldname('Alias.*');
  1158. $this->assertEqual(['Alias', '*'], $result);
  1159. }
  1160. public function testOn() {
  1161. $conn = MockDatabasePost::connection();
  1162. $expected = [
  1163. 'MockDatabasePost.id' => 'MockDatabaseComment.mock_database_post_id'
  1164. ];
  1165. $result = $conn->on(MockDatabasePost::relations('MockDatabaseComment'));
  1166. $this->assertEqual($expected, $result);
  1167. $expected = [
  1168. 'MockDatabaseComment.mock_database_post_id' => 'MockDatabasePost.id'
  1169. ];
  1170. $result = $conn->on(MockDatabaseComment::relations('MockDatabasePost'));
  1171. $this->assertEqual($expected, $result);
  1172. $expected = [
  1173. 'MockDatabasePost.id' => 'MockDatabaseComment.mock_database_post_id',
  1174. 'MockDatabasePost.published' => (object) "'yes'"
  1175. ];
  1176. $rel = MockDatabasePost::relations('MockDatabaseComment');
  1177. $result = $conn->on($rel, null, null, ['published' => (object) "'yes'"]);
  1178. $this->assertEqual($expected, $result);
  1179. $expected = [
  1180. 'CustomPost.id' => 'CustomComment.mock_database_post_id',
  1181. 'CustomPost.published' => (object) "'no'"
  1182. ];
  1183. $constraints = ['published' => (object) "'no'"];
  1184. $result = $conn->on($rel, 'CustomPost', 'CustomComment', $constraints);
  1185. $this->assertEqual($expected, $result);
  1186. $expected = [
  1187. 'CustomPost.id' => 'CustomComment.post_id'
  1188. ];
  1189. $constraints = ['CustomPost.id' => 'CustomComment.post_id'];
  1190. $result = $conn->on($rel, 'CustomPost', 'CustomComment', $constraints);
  1191. $this->assertEqual($expected, $result);
  1192. }
  1193. public function testWithGeneration() {
  1194. $model = $this->_gallery;
  1195. $options = [
  1196. 'type' => 'read',
  1197. 'model' => $model,
  1198. 'with' => ['Image.ImageTag.Tag']
  1199. ];
  1200. $result = $this->_db->read(new Query($options));
  1201. $expected = 'SELECT * FROM {mock_gallery} AS {Gallery} LEFT JOIN {mock_image} AS {Image} ';
  1202. $expected .= 'ON {Gallery}.{id} = {Image}.{gallery_id} LEFT JOIN {mock_image_tag} AS ';
  1203. $expected .= '{ImageTag} ON {Image}.{id} = {ImageTag}.{image_id} LEFT JOIN {mock_tag} ';
  1204. $expected .= 'AS {Tag} ON {ImageTag}.{tag_id} = {Tag}.{id};';
  1205. $this->assertEqual($expected, $this->_db->sql);
  1206. $model = $this->_imageTag;
  1207. $options = [
  1208. 'type' => 'read',
  1209. 'model' => $model,
  1210. 'with' => ['Image', 'Tag']
  1211. ];
  1212. $result = $this->_db->read(new Query($options));
  1213. $expected = 'SELECT * FROM {mock_image_tag} AS {ImageTag} LEFT JOIN {mock_image} AS ';
  1214. $expected .= '{Image} ON {ImageTag}.{image_id} = {Image}.{id} LEFT JOIN {mock_tag} AS ';
  1215. $expected .= '{Tag} ON {ImageTag}.{tag_id} = {Tag}.{id};';
  1216. $this->assertEqual($expected, $this->_db->sql);
  1217. }
  1218. public function testWithOptionAndInlineConstraint() {
  1219. $model = $this->_gallery;
  1220. $options = [
  1221. 'type' => 'read',
  1222. 'model' => $model,
  1223. 'with' => [
  1224. 'Image' => [
  1225. 'constraints' => [
  1226. 'Image.title' => (object) "'MyImage'"
  1227. ]
  1228. ],
  1229. 'Image.ImageTag.Tag' => [
  1230. 'constraints' => [
  1231. 'Tag.name' => (object) "'MyTag'"
  1232. ]
  1233. ]
  1234. ]
  1235. ];
  1236. $result = $this->_db->read(new Query($options));
  1237. $expected = 'SELECT * FROM {mock_gallery} AS {Gallery} ';
  1238. $expected .= 'LEFT JOIN {mock_image} AS {Image} ON {Image}.{title} = \'MyImage\' ';
  1239. $expected .= 'AND {Gallery}.{id} = {Image}.{gallery_id} LEFT JOIN ';
  1240. $expected .= '{mock_image_tag} AS {ImageTag} ON ';
  1241. $expected .= '{Image}.{id} = {ImageTag}.{image_id} LEFT JOIN {mock_tag} AS {Tag} ON ';
  1242. $expected .= '{Tag}.{name} = \'MyTag\' AND {ImageTag}.{tag_id} = {Tag}.{id};';
  1243. $this->assertEqual($expected, $this->_db->sql);
  1244. $to = 'lithium\tests\mocks\data\model\MockImage';
  1245. $model::bind('hasMany', 'Image', ['to' => $to]);
  1246. $to::bind('belongsTo', 'Gallery', ['to' => $model]);
  1247. $result = $this->_db->read(new Query([
  1248. 'type' => 'read',
  1249. 'model' => $model,
  1250. 'with' => [
  1251. 'Image.Gallery' => [
  1252. 'alias' => 'Gallery2',
  1253. 'constraints' => [
  1254. 'Gallery.custom_id' => 'Gallery2.id'
  1255. ]
  1256. ]
  1257. ]
  1258. ]));
  1259. $expected = 'SELECT * FROM {mock_gallery} AS {Gallery} LEFT JOIN {mock_image} AS {Image}';
  1260. $expected .= ' ON {Gallery}.{id} = {Image}.{gallery_id} LEFT JOIN {mock_gallery} AS ';
  1261. $expected .= '{Gallery2} ON {Gallery}.{custom_id} = {Gallery2}.{id} AND ';
  1262. $expected .= '{Image}.{gallery_id} = {Gallery2}.{id};';
  1263. $this->assertEqual($expected, $this->_db->sql);
  1264. $model::reset();
  1265. }
  1266. public function testWithOptionAndConstraintInRelation() {
  1267. $model = 'lithium\tests\mocks\data\model\MockGallery';
  1268. $to = 'lithium\tests\mocks\data\model\MockImage';
  1269. $model::bind('hasMany', 'Image', [
  1270. 'to' => $to,
  1271. 'constraints' => [
  1272. 'Image.title' => (object) "'MyImage'"
  1273. ]
  1274. ]);
  1275. $result = $this->_db->read(new Query([
  1276. 'type' => 'read',
  1277. 'model' => $model,
  1278. 'with' => [
  1279. 'Image.ImageTag.Tag' => [
  1280. 'constrain…

Large files files are truncated, but you can click here to view the full file