DataSource.php 11 KB

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