DataSource.php 11 KB

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