DataSource.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <?php
  2. /**
  3. * DataSource base class
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2012, 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-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package Cake.Model.Datasource
  16. * @since CakePHP(tm) v 0.10.5.1790
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. /**
  20. * DataSource base class
  21. *
  22. * @package Cake.Model.Datasource
  23. */
  24. class DataSource extends Object {
  25. /**
  26. * Are we connected to the DataSource?
  27. *
  28. * @var boolean
  29. */
  30. public $connected = false;
  31. /**
  32. * The default configuration of a specific DataSource
  33. *
  34. * @var array
  35. */
  36. protected $_baseConfig = array();
  37. /**
  38. * Holds references to descriptions loaded by the DataSource
  39. *
  40. * @var array
  41. */
  42. protected $_descriptions = array();
  43. /**
  44. * Holds a list of sources (tables) contained in the DataSource
  45. *
  46. * @var array
  47. */
  48. protected $_sources = null;
  49. /**
  50. * The DataSource configuration
  51. *
  52. * @var array
  53. */
  54. public $config = array();
  55. /**
  56. * Whether or not this DataSource is in the middle of a transaction
  57. *
  58. * @var boolean
  59. */
  60. protected $_transactionStarted = false;
  61. /**
  62. * Whether or not source data like available tables and schema descriptions
  63. * should be cached
  64. *
  65. * @var boolean
  66. */
  67. public $cacheSources = true;
  68. /**
  69. * Constructor.
  70. *
  71. * @param array $config Array of configuration information for the datasource.
  72. */
  73. public function __construct($config = array()) {
  74. parent::__construct();
  75. $this->setConfig($config);
  76. }
  77. /**
  78. * Caches/returns cached results for child instances
  79. *
  80. * @param mixed $data
  81. * @return array Array of sources available in this datasource.
  82. */
  83. public function listSources($data = null) {
  84. if ($this->cacheSources === false) {
  85. return null;
  86. }
  87. if ($this->_sources !== null) {
  88. return $this->_sources;
  89. }
  90. $key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list';
  91. $key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key);
  92. $sources = Cache::read($key, '_cake_model_');
  93. if (empty($sources)) {
  94. $sources = $data;
  95. Cache::write($key, $data, '_cake_model_');
  96. }
  97. return $this->_sources = $sources;
  98. }
  99. /**
  100. * Returns a Model description (metadata) or null if none found.
  101. *
  102. * @param Model|string $model
  103. * @return array Array of Metadata for the $model
  104. */
  105. public function describe($model) {
  106. if ($this->cacheSources === false) {
  107. return null;
  108. }
  109. if (is_string($model)) {
  110. $table = $model;
  111. } else {
  112. $table = $model->tablePrefix . $model->table;
  113. }
  114. if (isset($this->_descriptions[$table])) {
  115. return $this->_descriptions[$table];
  116. }
  117. $cache = $this->_cacheDescription($table);
  118. if ($cache !== null) {
  119. $this->_descriptions[$table] =& $cache;
  120. return $cache;
  121. }
  122. return null;
  123. }
  124. /**
  125. * Begin a transaction
  126. *
  127. * @return boolean Returns true if a transaction is not in progress
  128. */
  129. public function begin() {
  130. return !$this->_transactionStarted;
  131. }
  132. /**
  133. * Commit a transaction
  134. *
  135. * @return boolean Returns true if a transaction is in progress
  136. */
  137. public function commit() {
  138. return $this->_transactionStarted;
  139. }
  140. /**
  141. * Rollback a transaction
  142. *
  143. * @return boolean Returns true if a transaction is in progress
  144. */
  145. public function rollback() {
  146. return $this->_transactionStarted;
  147. }
  148. /**
  149. * Converts column types to basic types
  150. *
  151. * @param string $real Real column type (i.e. "varchar(255)")
  152. * @return string Abstract column type (i.e. "string")
  153. */
  154. public function column($real) {
  155. return false;
  156. }
  157. /**
  158. * Used to create new records. The "C" CRUD.
  159. *
  160. * To-be-overridden in subclasses.
  161. *
  162. * @param Model $model The Model to be created.
  163. * @param array $fields An Array of fields to be saved.
  164. * @param array $values An Array of values to save.
  165. * @return boolean success
  166. */
  167. public function create(Model $model, $fields = null, $values = null) {
  168. return false;
  169. }
  170. /**
  171. * Used to read records from the Datasource. The "R" in CRUD
  172. *
  173. * To-be-overridden in subclasses.
  174. *
  175. * @param Model $model The model being read.
  176. * @param array $queryData An array of query data used to find the data you want
  177. * @param integer $recursive Number of levels of association
  178. * @return mixed
  179. */
  180. public function read(Model $model, $queryData = array(), $recursive = null) {
  181. return false;
  182. }
  183. /**
  184. * Update a record(s) in the datasource.
  185. *
  186. * To-be-overridden in subclasses.
  187. *
  188. * @param Model $model Instance of the model class being updated
  189. * @param array $fields Array of fields to be updated
  190. * @param array $values Array of values to be update $fields to.
  191. * @param mixed $conditions
  192. * @return boolean Success
  193. */
  194. public function update(Model $model, $fields = null, $values = null, $conditions = null) {
  195. return false;
  196. }
  197. /**
  198. * Delete a record(s) in the datasource.
  199. *
  200. * To-be-overridden in subclasses.
  201. *
  202. * @param Model $model The model class having record(s) deleted
  203. * @param mixed $conditions The conditions to use for deleting.
  204. * @return boolean Success
  205. */
  206. public function delete(Model $model, $id = null) {
  207. return false;
  208. }
  209. /**
  210. * Returns the ID generated from the previous INSERT operation.
  211. *
  212. * @param mixed $source
  213. * @return mixed Last ID key generated in previous INSERT
  214. */
  215. public function lastInsertId($source = null) {
  216. return false;
  217. }
  218. /**
  219. * Returns the number of rows returned by last operation.
  220. *
  221. * @param mixed $source
  222. * @return integer Number of rows returned by last operation
  223. */
  224. public function lastNumRows($source = null) {
  225. return false;
  226. }
  227. /**
  228. * Returns the number of rows affected by last query.
  229. *
  230. * @param mixed $source
  231. * @return integer Number of rows affected by last query.
  232. */
  233. public function lastAffected($source = null) {
  234. return false;
  235. }
  236. /**
  237. * Check whether the conditions for the Datasource being available
  238. * are satisfied. Often used from connect() to check for support
  239. * before establishing a connection.
  240. *
  241. * @return boolean Whether or not the Datasources conditions for use are met.
  242. */
  243. public function enabled() {
  244. return true;
  245. }
  246. /**
  247. * Sets the configuration for the DataSource.
  248. * Merges the $config information with the _baseConfig and the existing $config property.
  249. *
  250. * @param array $config The configuration array
  251. * @return void
  252. */
  253. public function setConfig($config = array()) {
  254. $this->config = array_merge($this->_baseConfig, $this->config, $config);
  255. }
  256. /**
  257. * Cache the DataSource description
  258. *
  259. * @param string $object The name of the object (model) to cache
  260. * @param mixed $data The description of the model, usually a string or array
  261. * @return mixed
  262. */
  263. protected function _cacheDescription($object, $data = null) {
  264. if ($this->cacheSources === false) {
  265. return null;
  266. }
  267. if ($data !== null) {
  268. $this->_descriptions[$object] =& $data;
  269. }
  270. $key = ConnectionManager::getSourceName($this) . '_' . $object;
  271. $cache = Cache::read($key, '_cake_model_');
  272. if (empty($cache)) {
  273. $cache = $data;
  274. Cache::write($key, $cache, '_cake_model_');
  275. }
  276. return $cache;
  277. }
  278. /**
  279. * Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data.
  280. *
  281. * @param string $query Query string needing replacements done.
  282. * @param array $data Array of data with values that will be inserted in placeholders.
  283. * @param string $association Name of association model being replaced
  284. * @param array $assocData
  285. * @param Model $model Instance of the model to replace $__cakeID__$
  286. * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
  287. * @param array $stack
  288. * @return string String of query data with placeholders replaced.
  289. */
  290. public function insertQueryData($query, $data, $association, $assocData, Model $model, Model $linkModel, $stack) {
  291. $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
  292. foreach ($keys as $key) {
  293. $val = null;
  294. $type = null;
  295. if (strpos($query, $key) !== false) {
  296. switch ($key) {
  297. case '{$__cakeID__$}':
  298. if (isset($data[$model->alias]) || isset($data[$association])) {
  299. if (isset($data[$model->alias][$model->primaryKey])) {
  300. $val = $data[$model->alias][$model->primaryKey];
  301. } elseif (isset($data[$association][$model->primaryKey])) {
  302. $val = $data[$association][$model->primaryKey];
  303. }
  304. } else {
  305. $found = false;
  306. foreach (array_reverse($stack) as $assoc) {
  307. if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
  308. $val = $data[$assoc][$model->primaryKey];
  309. $found = true;
  310. break;
  311. }
  312. }
  313. if (!$found) {
  314. $val = '';
  315. }
  316. }
  317. $type = $model->getColumnType($model->primaryKey);
  318. break;
  319. case '{$__cakeForeignKey__$}':
  320. foreach ($model->associations() as $name) {
  321. foreach ($model->$name as $assocName => $assoc) {
  322. if ($assocName === $association) {
  323. if (isset($assoc['foreignKey'])) {
  324. $foreignKey = $assoc['foreignKey'];
  325. $assocModel = $model->$assocName;
  326. $type = $assocModel->getColumnType($assocModel->primaryKey);
  327. if (isset($data[$model->alias][$foreignKey])) {
  328. $val = $data[$model->alias][$foreignKey];
  329. } elseif (isset($data[$association][$foreignKey])) {
  330. $val = $data[$association][$foreignKey];
  331. } else {
  332. $found = false;
  333. foreach (array_reverse($stack) as $assoc) {
  334. if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
  335. $val = $data[$assoc][$foreignKey];
  336. $found = true;
  337. break;
  338. }
  339. }
  340. if (!$found) {
  341. $val = '';
  342. }
  343. }
  344. }
  345. break 3;
  346. }
  347. }
  348. }
  349. break;
  350. }
  351. if (empty($val) && $val !== '0') {
  352. return false;
  353. }
  354. $query = str_replace($key, $this->value($val, $type), $query);
  355. }
  356. }
  357. return $query;
  358. }
  359. /**
  360. * To-be-overridden in subclasses.
  361. *
  362. * @param Model $model Model instance
  363. * @param string $key Key name to make
  364. * @return string Key name for model.
  365. */
  366. public function resolveKey(Model $model, $key) {
  367. return $model->alias . $key;
  368. }
  369. /**
  370. * Returns the schema name. Override this in subclasses.
  371. *
  372. * @return string schema name
  373. * @access public
  374. */
  375. public function getSchemaName() {
  376. return null;
  377. }
  378. /**
  379. * Closes a connection. Override in subclasses
  380. *
  381. * @return boolean
  382. * @access public
  383. */
  384. public function close() {
  385. return $this->connected = false;
  386. }
  387. /**
  388. * Closes the current datasource.
  389. *
  390. */
  391. public function __destruct() {
  392. if ($this->_transactionStarted) {
  393. $this->rollback();
  394. }
  395. if ($this->connected) {
  396. $this->close();
  397. }
  398. }
  399. }