DatabaseTest.php 54 KB

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