source: Dev/branches/cakephp/cake/libs/model/datasources/datasource.php @ 201

Last change on this file since 201 was 126, checked in by fpvanagthoven, 14 years ago

Cakephp branch.

File size: 13.3 KB
Line 
1<?php
2/**
3 * DataSource base class
4 *
5 * PHP versions 4 and 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
16 * @subpackage    cake.cake.libs.model.datasources
17 * @since         CakePHP(tm) v 0.10.5.1790
18 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
19 */
20
21/**
22 * DataSource base class
23 *
24 * @package       cake
25 * @subpackage    cake.cake.libs.model.datasources
26 */
27class DataSource extends Object {
28
29/**
30 * Are we connected to the DataSource?
31 *
32 * @var boolean
33 * @access public
34 */
35        var $connected = false;
36
37/**
38 * Print full query debug info?
39 *
40 * @var boolean
41 * @access public
42 */
43        var $fullDebug = false;
44
45/**
46 * Error description of last query
47 *
48 * @var unknown_type
49 * @access public
50 */
51        var $error = null;
52
53/**
54 * String to hold how many rows were affected by the last SQL operation.
55 *
56 * @var string
57 * @access public
58 */
59        var $affected = null;
60
61/**
62 * Number of rows in current resultset
63 *
64 * @var int
65 * @access public
66 */
67        var $numRows = null;
68
69/**
70 * Time the last query took
71 *
72 * @var int
73 * @access public
74 */
75        var $took = null;
76
77/**
78 * The starting character that this DataSource uses for quoted identifiers.
79 *
80 * @var string
81 * @access public
82 */
83        var $startQuote = null;
84
85/**
86 * The ending character that this DataSource uses for quoted identifiers.
87 *
88 * @var string
89 * @access public
90 */
91        var $endQuote = null;
92
93/**
94 * Result
95 *
96 * @var array
97 * @access protected
98 */
99        var $_result = null;
100
101/**
102 * Queries count.
103 *
104 * @var int
105 * @access protected
106 */
107        var $_queriesCnt = 0;
108
109/**
110 * Total duration of all queries.
111 *
112 * @var unknown_type
113 * @access protected
114 */
115        var $_queriesTime = null;
116
117/**
118 * Log of queries executed by this DataSource
119 *
120 * @var unknown_type
121 * @access protected
122 */
123        var $_queriesLog = array();
124
125/**
126 * Maximum number of items in query log
127 *
128 * This is to prevent query log taking over too much memory.
129 *
130 * @var int Maximum number of queries in the queries log.
131 * @access protected
132 */
133        var $_queriesLogMax = 200;
134
135/**
136 * Caches serialzed results of executed queries
137 *
138 * @var array Maximum number of queries in the queries log.
139 * @access protected
140 */
141        var $_queryCache = array();
142
143/**
144 * The default configuration of a specific DataSource
145 *
146 * @var array
147 * @access protected
148 */
149        var $_baseConfig = array();
150
151/**
152 * Holds references to descriptions loaded by the DataSource
153 *
154 * @var array
155 * @access private
156 */
157        var $__descriptions = array();
158
159/**
160 * Holds a list of sources (tables) contained in the DataSource
161 *
162 * @var array
163 * @access protected
164 */
165        var $_sources = null;
166
167/**
168 * A reference to the physical connection of this DataSource
169 *
170 * @var array
171 * @access public
172 */
173        var $connection = null;
174
175/**
176 * The DataSource configuration
177 *
178 * @var array
179 * @access public
180 */
181        var $config = array();
182
183/**
184 * The DataSource configuration key name
185 *
186 * @var string
187 * @access public
188 */
189        var $configKeyName = null;
190
191/**
192 * Whether or not this DataSource is in the middle of a transaction
193 *
194 * @var boolean
195 * @access protected
196 */
197        var $_transactionStarted = false;
198
199/**
200 * Whether or not source data like available tables and schema descriptions
201 * should be cached
202 *
203 * @var boolean
204 * @access public
205 */
206        var $cacheSources = true;
207
208/**
209 * Constructor.
210 *
211 * @param array $config Array of configuration information for the datasource.
212 * @return void.
213 */
214        function __construct($config = array()) {
215                parent::__construct();
216                $this->setConfig($config);
217        }
218
219/**
220 * Caches/returns cached results for child instances
221 *
222 * @param mixed $data
223 * @return array Array of sources available in this datasource.
224 * @access public
225 */
226        function listSources($data = null) {
227                if ($this->cacheSources === false) {
228                        return null;
229                }
230
231                if ($this->_sources !== null) {
232                        return $this->_sources;
233                }
234
235                $key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list';
236                $key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key);
237                $sources = Cache::read($key, '_cake_model_');
238
239                if (empty($sources)) {
240                        $sources = $data;
241                        Cache::write($key, $data, '_cake_model_');
242                }
243
244                $this->_sources = $sources;
245                return $sources;
246        }
247
248/**
249 * Convenience method for DboSource::listSources().  Returns source names in lowercase.
250 *
251 * @param boolean $reset Whether or not the source list should be reset.
252 * @return array Array of sources available in this datasource
253 * @access public
254 */
255        function sources($reset = false) {
256                if ($reset === true) {
257                        $this->_sources = null;
258                }
259                return array_map('strtolower', $this->listSources());
260        }
261
262/**
263 * Returns a Model description (metadata) or null if none found.
264 *
265 * @param Model $model
266 * @return array Array of Metadata for the $model
267 * @access public
268 */
269        function describe(&$model) {
270                if ($this->cacheSources === false) {
271                        return null;
272                }
273                $table = $model->tablePrefix . $model->table;
274
275                if (isset($this->__descriptions[$table])) {
276                        return $this->__descriptions[$table];
277                }
278                $cache = $this->__cacheDescription($table);
279
280                if ($cache !== null) {
281                        $this->__descriptions[$table] =& $cache;
282                        return $cache;
283                }
284                return null;
285        }
286
287/**
288 * Begin a transaction
289 *
290 * @return boolean Returns true if a transaction is not in progress
291 * @access public
292 */
293        function begin(&$model) {
294                return !$this->_transactionStarted;
295        }
296
297/**
298 * Commit a transaction
299 *
300 * @return boolean Returns true if a transaction is in progress
301 * @access public
302 */
303        function commit(&$model) {
304                return $this->_transactionStarted;
305        }
306
307/**
308 * Rollback a transaction
309 *
310 * @return boolean Returns true if a transaction is in progress
311 * @access public
312 */
313        function rollback(&$model) {
314                return $this->_transactionStarted;
315        }
316
317/**
318 * Converts column types to basic types
319 *
320 * @param string $real Real  column type (i.e. "varchar(255)")
321 * @return string Abstract column type (i.e. "string")
322 * @access public
323 */
324        function column($real) {
325                return false;
326        }
327
328/**
329 * Used to create new records. The "C" CRUD.
330 *
331 * To-be-overridden in subclasses.
332 *
333 * @param Model $model The Model to be created.
334 * @param array $fields An Array of fields to be saved.
335 * @param array $values An Array of values to save.
336 * @return boolean success
337 * @access public
338 */
339        function create(&$model, $fields = null, $values = null) {
340                return false;
341        }
342
343/**
344 * Used to read records from the Datasource. The "R" in CRUD
345 *
346 * To-be-overridden in subclasses.
347 *
348 * @param Model $model The model being read.
349 * @param array $queryData An array of query data used to find the data you want
350 * @return mixed
351 * @access public
352 */
353        function read(&$model, $queryData = array()) {
354                return false;
355        }
356
357/**
358 * Update a record(s) in the datasource.
359 *
360 * To-be-overridden in subclasses.
361 *
362 * @param Model $model Instance of the model class being updated
363 * @param array $fields Array of fields to be updated
364 * @param array $values Array of values to be update $fields to.
365 * @return boolean Success
366 * @access public
367 */
368        function update(&$model, $fields = null, $values = null) {
369                return false;
370        }
371
372/**
373 * Delete a record(s) in the datasource.
374 *
375 * To-be-overridden in subclasses.
376 *
377 * @param Model $model The model class having record(s) deleted
378 * @param mixed $id Primary key of the model
379 * @access public
380 */
381        function delete(&$model, $id = null) {
382                if ($id == null) {
383                        $id = $model->id;
384                }
385        }
386
387/**
388 * Returns the ID generated from the previous INSERT operation.
389 *
390 * @param unknown_type $source
391 * @return mixed Last ID key generated in previous INSERT
392 * @access public
393 */
394        function lastInsertId($source = null) {
395                return false;
396        }
397
398/**
399 * Returns the number of rows returned by last operation.
400 *
401 * @param unknown_type $source
402 * @return integer Number of rows returned by last operation
403 * @access public
404 */
405        function lastNumRows($source = null) {
406                return false;
407        }
408
409/**
410 * Returns the number of rows affected by last query.
411 *
412 * @param unknown_type $source
413 * @return integer Number of rows affected by last query.
414 * @access public
415 */
416        function lastAffected($source = null) {
417                return false;
418        }
419
420/**
421 * Check whether the conditions for the Datasource being available
422 * are satisfied.  Often used from connect() to check for support
423 * before establishing a connection.
424 *
425 * @return boolean Whether or not the Datasources conditions for use are met.
426 * @access public
427 */
428        function enabled() {
429                return true;
430        }
431
432/**
433 * Returns true if the DataSource supports the given interface (method)
434 *
435 * @param string $interface The name of the interface (method)
436 * @return boolean True on success
437 * @access public
438 */
439        function isInterfaceSupported($interface) {
440                static $methods = false;
441                if ($methods === false) {
442                        $methods = array_map('strtolower', get_class_methods($this));
443                }
444                return in_array(strtolower($interface), $methods);
445        }
446
447/**
448 * Sets the configuration for the DataSource.
449 * Merges the $config information with the _baseConfig and the existing $config property.
450 *
451 * @param array $config The configuration array
452 * @return void
453 * @access public
454 */
455        function setConfig($config = array()) {
456                $this->config = array_merge($this->_baseConfig, $this->config, $config);
457        }
458
459/**
460 * Cache the DataSource description
461 *
462 * @param string $object The name of the object (model) to cache
463 * @param mixed $data The description of the model, usually a string or array
464 * @return mixed
465 * @access private
466 */
467        function __cacheDescription($object, $data = null) {
468                if ($this->cacheSources === false) {
469                        return null;
470                }
471
472                if ($data !== null) {
473                        $this->__descriptions[$object] =& $data;
474                }
475
476                $key = ConnectionManager::getSourceName($this) . '_' . $object;
477                $cache = Cache::read($key, '_cake_model_');
478
479                if (empty($cache)) {
480                        $cache = $data;
481                        Cache::write($key, $cache, '_cake_model_');
482                }
483
484                return $cache;
485        }
486
487/**
488 * Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data.
489 *
490 * @param string $query Query string needing replacements done.
491 * @param array $data Array of data with values that will be inserted in placeholders.
492 * @param string $association Name of association model being replaced
493 * @param unknown_type $assocData
494 * @param Model $model Instance of the model to replace $__cakeID__$
495 * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
496 * @param array $stack
497 * @return string String of query data with placeholders replaced.
498 * @access public
499 * @todo Remove and refactor $assocData, ensure uses of the method have the param removed too.
500 */
501        function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
502                $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
503
504                foreach ($keys as $key) {
505                        $val = null;
506                        $type = null;
507
508                        if (strpos($query, $key) !== false) {
509                                switch ($key) {
510                                        case '{$__cakeID__$}':
511                                                if (isset($data[$model->alias]) || isset($data[$association])) {
512                                                        if (isset($data[$model->alias][$model->primaryKey])) {
513                                                                $val = $data[$model->alias][$model->primaryKey];
514                                                        } elseif (isset($data[$association][$model->primaryKey])) {
515                                                                $val = $data[$association][$model->primaryKey];
516                                                        }
517                                                } else {
518                                                        $found = false;
519                                                        foreach (array_reverse($stack) as $assoc) {
520                                                                if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
521                                                                        $val = $data[$assoc][$model->primaryKey];
522                                                                        $found = true;
523                                                                        break;
524                                                                }
525                                                        }
526                                                        if (!$found) {
527                                                                $val = '';
528                                                        }
529                                                }
530                                                $type = $model->getColumnType($model->primaryKey);
531                                        break;
532                                        case '{$__cakeForeignKey__$}':
533                                                foreach ($model->__associations as $id => $name) {
534                                                        foreach ($model->$name as $assocName => $assoc) {
535                                                                if ($assocName === $association) {
536                                                                        if (isset($assoc['foreignKey'])) {
537                                                                                $foreignKey = $assoc['foreignKey'];
538                                                                                $assocModel = $model->$assocName;
539                                                                                $type = $assocModel->getColumnType($assocModel->primaryKey);
540
541                                                                                if (isset($data[$model->alias][$foreignKey])) {
542                                                                                        $val = $data[$model->alias][$foreignKey];
543                                                                                } elseif (isset($data[$association][$foreignKey])) {
544                                                                                        $val = $data[$association][$foreignKey];
545                                                                                } else {
546                                                                                        $found = false;
547                                                                                        foreach (array_reverse($stack) as $assoc) {
548                                                                                                if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
549                                                                                                        $val = $data[$assoc][$foreignKey];
550                                                                                                        $found = true;
551                                                                                                        break;
552                                                                                                }
553                                                                                        }
554                                                                                        if (!$found) {
555                                                                                                $val = '';
556                                                                                        }
557                                                                                }
558                                                                        }
559                                                                        break 3;
560                                                                }
561                                                        }
562                                                }
563                                        break;
564                                }
565                                if (empty($val) && $val !== '0') {
566                                        return false;
567                                }
568                                $query = str_replace($key, $this->value($val, $type), $query);
569                        }
570                }
571                return $query;
572        }
573
574/**
575 * To-be-overridden in subclasses.
576 *
577 * @param Model $model Model instance
578 * @param string $key Key name to make
579 * @return string Key name for model.
580 * @access public
581 */
582        function resolveKey(&$model, $key) {
583                return $model->alias . $key;
584        }
585
586/**
587 * Closes the current datasource.
588 *
589 * @return void
590 * @access public
591 */
592        function __destruct() {
593                if ($this->_transactionStarted) {
594                        $null = null;
595                        $this->rollback($null);
596                }
597                if ($this->connected) {
598                        $this->close();
599                }
600        }
601}
Note: See TracBrowser for help on using the repository browser.