PageRenderTime 22ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/models/datasources/couchdb_source.php

https://github.com/radig/datasources
PHP | 375 lines | 182 code | 35 blank | 158 comment | 40 complexity | b409b863001d5e66a573b9d1b82ed02d MD5 | raw file
  1. <?php
  2. /**
  3. * CouchDB Datasource
  4. *
  5. * PHP version 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package datasources
  16. * @subpackage datasources.models.datasources
  17. * @since CakePHP Datasources v 0.3
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. App::import('Core', 'HttpSocket');
  21. /**
  22. * CouchDB Datasource
  23. *
  24. * @package datasources
  25. * @subpackage datasources.models.datasources
  26. */
  27. class CouchdbSource extends DataSource{
  28. /**
  29. * Constructor
  30. *
  31. * @param array $config Connection setup for CouchDB.
  32. * @param integer $autoConnect Autoconnect
  33. * @return boolean
  34. */
  35. public function __construct($config = null, $autoConnect = true){
  36. if (!isset($config['request'])) {
  37. $config['request']['uri'] = $config;
  38. $config['request']['header']['Content-Type'] = 'application/json';
  39. }
  40. parent::__construct($config);
  41. $this->fullDebug = Configure::read() > 1;
  42. if ($autoConnect) {
  43. return $this->connect();
  44. } else {
  45. return true;
  46. }
  47. }
  48. /**
  49. * Reconnects to the database with optional new settings
  50. *
  51. * @param array $config New settings
  52. * @return boolean Success
  53. */
  54. public function reconnect($config = null) {
  55. $this->disconnect();
  56. $this->setConfig($config);
  57. $this->_sources = null;
  58. return $this->connect();
  59. }
  60. /**
  61. * Connects to the database. Options are specified in the $config instance variable
  62. *
  63. * @return boolean Connected
  64. */
  65. public function connect() {
  66. if($this->connected !== true){
  67. $this->Socket = new HttpSocket($this->config);
  68. if(strpos($this->Socket->get(), 'couchdb') !== false){
  69. $this->connected = true;
  70. }
  71. }
  72. return $this->connected;
  73. }
  74. /**
  75. * Disconnects from the database, kills the connection and advises that the
  76. * connection is closed, and if DEBUG is turned on (equal to 2) displays the
  77. * log of stored data.
  78. *
  79. * @return boolean Disconnected
  80. */
  81. public function close() {
  82. if (Configure::read() > 1) {
  83. // $this->showLog();
  84. }
  85. $this->disconnect();
  86. }
  87. /**
  88. * Disconnect from the database
  89. *
  90. * @return boolean Disconnected
  91. */
  92. public function disconnect() {
  93. if (isset($this->results) && is_resource($this->results)) {
  94. $this->results = null;
  95. }
  96. $this->connected = false;
  97. return !$this->connected;
  98. }
  99. /**
  100. * List of databases
  101. *
  102. * @return array Databases
  103. */
  104. public function listSources() {
  105. return $this->__decode($this->Socket->get($this->__uri('_all_dbs')), true);
  106. }
  107. /**
  108. * Convenience method for DboSource::listSources().
  109. * Returns the names of databases in lowercase.
  110. *
  111. * @return array Lowercase databases
  112. */
  113. public function sources($reset = false){
  114. if ($reset === true) {
  115. $this->_sources = null;
  116. }
  117. return array_map('strtolower', $this->listSources());
  118. }
  119. /**
  120. * Returns a description of the model (metadata)
  121. *
  122. * @param Model $model
  123. * @return array
  124. */
  125. public function describe($model) {
  126. return $model->schema;
  127. }
  128. /**
  129. * Creates a new document in the database.
  130. * If the primaryKey is declared, creates the document with the specified ID.
  131. * To create a new database: $this->__decode($this->Socket->put($this->__uri('databaseName')));
  132. *
  133. * @param Model $model
  134. * @param array $fields An array of field names to insert. If null, $model->data will be used to generate the field names.
  135. * @param array $values An array with key values of the fields. If null, $model->data will be used to generate the field names.
  136. * @return boolean Success
  137. */
  138. public function create($model, $fields = null, $values = null) {
  139. $data = $model->data;
  140. if ($fields !== null && $values !== null) {
  141. $data = array_combine($fields, $values);
  142. }
  143. $params = null;
  144. if (isset($data[$model->primaryKey]) && !empty($data[$model->primaryKey])) {
  145. $params = $data[$model->primaryKey];
  146. }
  147. $result = $this->__decode($this->Socket->post($this->__uri($model, $params), $this->__encode($data)));
  148. if ($this->__checkOk($result)) {
  149. $model->id = $result->id;
  150. $model->rev = $result->rev;
  151. return true;
  152. }
  153. return false;
  154. }
  155. /**
  156. * Reads data from a document.
  157. *
  158. * @param Model $model
  159. * @param array $queryData An array of information containing $queryData keys, similar to Model::find()
  160. * @param integer $recursive Level number of associations.
  161. * @return mixed False if an error occurred, otherwise an array of results.
  162. */
  163. public function read($model, $queryData = array(), $recursive = null) {
  164. if ($recursive === null && isset($queryData['recursive'])) {
  165. $recursive = $queryData['recursive'];
  166. }
  167. if (!is_null($recursive)) {
  168. $model->recursive = $recursive;
  169. }
  170. $params = null;
  171. if (empty($queryData['conditions'])) {
  172. $params = $params . '_all_docs?include_docs=true';
  173. if (!empty($queryData['limit'])) {
  174. $params = $params . '&limit=' . $queryData['limit'];
  175. }
  176. } else {
  177. if (isset($queryData['conditions'][$model->alias . '.' . $model->primaryKey])) {
  178. $params = $queryData['conditions'][$model->alias . '.' . $model->primaryKey];
  179. } else {
  180. $params = $queryData['conditions'][$model->primaryKey];
  181. }
  182. if ($model->recursive > -1) {
  183. $params = $params . '?revs_info=true';
  184. }
  185. }
  186. $result = array();
  187. $result[0][$model->alias] = $this->__decode($this->Socket->get($this->__uri($model, $params)), true);
  188. return $this->readResult($model, $queryData, $result);
  189. }
  190. /**
  191. * Applies the rules to the document read.
  192. *
  193. * @param Model $model
  194. * @param array $queryData An array of information containing $queryData keys, similar to Model::find()
  195. * @param array $result Data read from the document.
  196. * @return mixed False if an error occurred, otherwise an array of results.
  197. */
  198. private function readResult($model, $queryData, $result) {
  199. if (isset($result[0][$model->alias]['_id'])) {
  200. if (isset($queryData['fields']) && $queryData['fields'] === true) {
  201. $result[0][0]['count'] = 1;
  202. }
  203. $result[0][$model->alias]['id'] = $result[0][$model->alias]['_id'];
  204. $result[0][$model->alias]['rev'] = $result[0][$model->alias]['_rev'];
  205. unset($result[0][$model->alias]['_id']);
  206. unset($result[0][$model->alias]['_rev']);
  207. return $result;
  208. } else if (isset($result[0][$model->alias]['rows'])) {
  209. $docs = array();
  210. foreach ($result[0][$model->alias]['rows'] as $k => $doc) {
  211. $docs[$k][$model->alias]['id'] = $doc['doc']['_id'];
  212. $docs[$k][$model->alias]['rev'] = $doc['doc']['_rev'];
  213. unset($doc['doc']['_id']);
  214. unset($doc['doc']['_rev']);
  215. unset($doc['doc']['id']);
  216. unset($doc['doc']['rev']);
  217. foreach ($doc['doc'] as $field => $value) {
  218. $docs[$k][$model->alias][$field] = $value;
  219. }
  220. }
  221. return $docs;
  222. }
  223. return false;
  224. }
  225. /**
  226. * Generates and executes an UPDATE statement for a given model, fields and values.
  227. *
  228. * @param Model $model
  229. * @param array $fields
  230. * @param array $values
  231. * @param mixed $conditions
  232. * @return boolean Success
  233. */
  234. public function update($model, $fields = null, $values = null, $conditions = null) {
  235. $id = $model->id;
  236. $data = $model->data[$model->alias];
  237. if ($fields !== null && $values !== null) {
  238. $data = array_combine($fields, $values);
  239. }
  240. $data['_rev'] = $model->rev;
  241. if (!empty($id)) {
  242. $result = $this->__decode($this->Socket->put($this->__uri($model, $id), $this->__encode($data)));
  243. if ($this->__checkOk($result)) {
  244. $model->rev = $result->rev;
  245. return true;
  246. }
  247. }
  248. return false;
  249. }
  250. /**
  251. * Generates and executes a DELETE statement
  252. *
  253. * @param Model $model
  254. * @param mixed $conditions
  255. * @return boolean Success
  256. */
  257. public function delete($model, $conditions = null) {
  258. $id = $model->id;
  259. $rev = $model->rev;
  260. if (!empty($id) && !empty($rev)) {
  261. $id_rev = $id . '/?rev=' . $rev;
  262. $result = $this->__decode($this->Socket->delete($this->__uri($model, $id_rev)));
  263. return $this->__checkOk($result);
  264. }
  265. return false;
  266. }
  267. /**
  268. * Returns an instruction to count data. (SQL, i.e. COUNT() or MAX())
  269. *
  270. * @param model $model
  271. * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'
  272. * @param array $params Function parameters (any values must be quoted manually)
  273. * @return string An SQL calculation function
  274. */
  275. public function calculate($model, $func, $params = array()) {
  276. return true;
  277. }
  278. /**
  279. * Gets full table name including prefix
  280. *
  281. * @param mixed $model
  282. * @param boolean $quote
  283. * @return string Full name of table
  284. */
  285. public function fullTableName($model = null, $quote = true) {
  286. $table = null;
  287. if (is_object($model)) {
  288. $table = $model->tablePrefix . $model->table;
  289. } else if (isset($this->config['prefix'])) {
  290. $table = $this->config['prefix'] . strval($model);
  291. } else {
  292. $table = strval($model);
  293. }
  294. return $table;
  295. }
  296. /**
  297. * Get a URI
  298. *
  299. * @param mixed $model
  300. * @param string $params
  301. * @return string URI
  302. * @access private
  303. */
  304. private function __uri($model = null, $params = null) {
  305. if (!is_null($params)) {
  306. $params = '/' . $params;
  307. }
  308. return '/' . $this->fullTableName($model) . $params;
  309. }
  310. /**
  311. * JSON encode
  312. *
  313. * @param string json $data
  314. * @return string JSON
  315. * @access private
  316. */
  317. private function __encode($data) {
  318. return json_encode($data);
  319. }
  320. /**
  321. * JSON decode
  322. *
  323. * @param string json $data
  324. * @param boolean $assoc If true, returns array. If false, returns object.
  325. * @return mixed Object or Array.
  326. * @access private
  327. */
  328. private function __decode($data, $assoc = false) {
  329. return json_decode($data, $assoc);
  330. }
  331. /**
  332. * Checks if the result returned ok = true
  333. *
  334. * @param object $object
  335. * @return boolean
  336. */
  337. private function __checkOk($object = null) {
  338. return isset($object->ok) && $object->ok === true;
  339. }
  340. }