DataSource.php 11 KB

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