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