DataSource.php 10 KB

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