model_php4.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: model__php4_8php-source.html 675 2008-12-26 00:27:14Z gwoo $ */
00003 /**
00004  * Object-relational mapper.
00005  *
00006  * DBO-backed object data model, for mapping database tables to Cake objects.
00007  *
00008  * PHP versions 4
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.model
00023  * @since           CakePHP(tm) v 0.10.0.0
00024  * @version         $Revision: 675 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-12-25 18:27:14 -0600 (Thu, 25 Dec 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Included libs
00031  */
00032 uses('class_registry', 'validators', 'model' . DS . 'connection_manager', 'set');
00033 /**
00034  * Object-relational mapper.
00035  *
00036  * DBO-backed object data model.
00037  * Automatically selects a database table name based on a pluralized lowercase object class name
00038  * (i.e. class 'User' => table 'users'; class 'Man' => table 'men')
00039  * The table is required to have at least 'id auto_increment', 'created datetime',
00040  * and 'modified datetime' fields.
00041  *
00042  * @package     cake
00043  * @subpackage  cake.cake.libs.model
00044  */
00045 class Model extends Object{
00046 /**
00047  * The name of the DataSource connection that this Model uses
00048  *
00049  * @var string
00050  * @access public
00051  */
00052     var $useDbConfig = 'default';
00053 /**
00054  * Custom database table name.
00055  *
00056  * @var string
00057  * @access public
00058  */
00059     var $useTable = null;
00060 /**
00061  * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements.
00062  *
00063  * @var string
00064  * @access public
00065  */
00066     var $displayField = null;
00067 
00068 /**
00069  * Value of the primary key ID of the record that this model is currently pointing to
00070  *
00071  * @var string
00072  * @access public
00073  */
00074     var $id = false;
00075 /**
00076  * Container for the data that this model gets from persistent storage (the database).
00077  *
00078  * @var array
00079  * @access public
00080  */
00081     var $data = array();
00082 /**
00083  * Table name for this Model.
00084  *
00085  * @var string
00086  * @access public
00087  */
00088     var $table = false;
00089 /**
00090  * The name of the ID field for this Model.
00091  *
00092  * @var string
00093  * @access public
00094  */
00095     var $primaryKey = null;
00096 /**
00097  * Table metadata
00098  *
00099  * @var array
00100  * @access protected
00101  */
00102     var $_tableInfo = null;
00103 /**
00104  * List of validation rules. Append entries for validation as ('field_name' => '/^perl_compat_regexp$/')
00105  * that have to match with preg_match(). Use these rules with Model::validate()
00106  *
00107  * @var array
00108  * @access public
00109  */
00110     var $validate = array();
00111 /**
00112  * Errors in validation
00113  * @var array
00114  * @access public
00115  */
00116     var $validationErrors = array();
00117 /**
00118  * Database table prefix for tables in model.
00119  *
00120  * @var string
00121  * @access public
00122  */
00123     var $tablePrefix = null;
00124 /**
00125  * Name of the model.
00126  *
00127  * @var string
00128  * @access public
00129  */
00130     var $name = null;
00131 /**
00132  * Name of the current model.
00133  *
00134  * @var string
00135  * @access public
00136  */
00137     var $currentModel = null;
00138 /**
00139  * List of table names included in the Model description. Used for associations.
00140  *
00141  * @var array
00142  * @access public
00143  */
00144     var $tableToModel = array();
00145 /**
00146  * List of Model names by used tables. Used for associations.
00147  *
00148  * @var array
00149  * @access public
00150  */
00151     var $modelToTable = array();
00152 /**
00153  * List of Foreign Key names to used tables. Used for associations.
00154  *
00155  * @var array
00156  * @access public
00157  */
00158     var $keyToTable = array();
00159 /**
00160  * Alias name for model.
00161  *
00162  * @var array
00163  * @access public
00164  */
00165     var $alias = null;
00166 /**
00167  * Whether or not transactions for this model should be logged
00168  *
00169  * @var boolean
00170  * @access public
00171  */
00172     var $logTransactions = false;
00173 /**
00174  * Whether or not to enable transactions for this model (i.e. BEGIN/COMMIT/ROLLBACK)
00175  *
00176  * @var boolean
00177  * @access public
00178  */
00179     var $transactional = false;
00180 /**
00181  * Whether or not to cache queries for this model.  This enables in-memory
00182  * caching only, the results are not stored beyond this execution.
00183  *
00184  * @var boolean
00185  * @access public
00186  */
00187     var $cacheQueries = true;
00188 /**
00189  * belongsTo association
00190  *
00191  * @var array
00192  * @access public
00193  */
00194     var $belongsTo = array();
00195 /**
00196  * hasOne association
00197  *
00198  * @var array
00199  * @access public
00200  */
00201     var $hasOne = array();
00202 /**
00203  * hasMany association
00204  *
00205  * @var array
00206  * @access public
00207  */
00208     var $hasMany = array();
00209 /**
00210  * hasAndBelongsToMany association
00211  *
00212  * @var array
00213  * @access public
00214  */
00215     var $hasAndBelongsToMany = array();
00216 /**
00217  * Depth of recursive association
00218  *
00219  * @var int
00220  * @access public
00221  */
00222     var $recursive = 1;
00223 /**
00224  * Whitelist of fields allowed to be saved
00225  *
00226  * @var array
00227  */
00228     var $whitelist = array();
00229 /**
00230  * Enter description here...
00231  *
00232  * @var boolean
00233  */
00234     var $cacheSources = true;
00235 /**
00236  * Default association keys
00237  *
00238  * @var array
00239  * @access private
00240  */
00241     var $__associationKeys = array('belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'),
00242                                                 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'),
00243                                                 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'),
00244                                                 'hasAndBelongsToMany' => array('className', 'joinTable', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery'));
00245 /**
00246  * Holds provided/generated association key names and other data for all associations
00247  *
00248  * @var array
00249  * @access private
00250  */
00251     var $__associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
00252 /**
00253  * The last inserted ID of the data that this model created
00254  *
00255  * @var int
00256  * @access private
00257  */
00258     var $__insertID = null;
00259 /**
00260  * The number of records returned by the last query
00261  *
00262  * @var int
00263  * @access private
00264  */
00265     var $__numRows = null;
00266 /**
00267  * The number of records affected by the last query
00268  *
00269  * @var int
00270  * @access private
00271  */
00272     var $__affectedRows = null;
00273 /**
00274  * Holds model associations temporarily to allow for dynamic (un)binding
00275  *
00276  * @var array
00277  * @access private
00278  */
00279     var $__backAssociation = array();
00280 /**
00281  * Constructor. Binds the Model's database table to the object.
00282  *
00283  * @param integer $id
00284  * @param string $table Name of database table to use.
00285  * @param DataSource $ds DataSource connection object.
00286  */
00287     function __construct($id = false, $table = null, $ds = null) {
00288         parent::__construct();
00289 
00290         if (is_array($id) && isset($id['name'])) {
00291             $options = array_merge(array('id' => false, 'table' => null, 'ds' => null, 'alias' => null), $id);
00292             list($id, $table, $ds) = array($options['id'], $options['table'], $options['ds']);
00293             $this->name = $options['name'];
00294         }
00295 
00296         if ($this->name === null) {
00297             $this->name = get_class($this);
00298         }
00299 
00300         if ($this->primaryKey === null) {
00301             $this->primaryKey = 'id';
00302         }
00303 
00304         if (isset($options['alias']) || !empty($options['alias'])) {
00305             $this->alias = $options['alias'];
00306             unset($options);
00307         } else {
00308             $this->alias = $this->name;
00309         }
00310         ClassRegistry::addObject($this->alias, $this);
00311 
00312         $this->id = $id;
00313         unset($id);
00314 
00315         if ($table === false) {
00316             $this->useTable = false;
00317         } elseif ($table) {
00318             $this->useTable = $table;
00319         }
00320 
00321         if ($this->useTable !== false) {
00322             $this->setDataSource($ds);
00323 
00324             if ($this->useTable === null) {
00325                 $this->useTable = Inflector::tableize($this->name);
00326             }
00327 
00328             if (in_array('settableprefix', get_class_methods($this))) {
00329                 $this->setTablePrefix();
00330             }
00331 
00332             $this->setSource($this->useTable);
00333             $this->__createLinks();
00334 
00335             if ($this->displayField == null) {
00336                 if ($this->hasField('title')) {
00337                     $this->displayField = 'title';
00338                 }
00339 
00340                 if ($this->hasField('name')) {
00341                     $this->displayField = 'name';
00342                 }
00343 
00344                 if ($this->displayField == null) {
00345                     $this->displayField = $this->primaryKey;
00346                 }
00347             }
00348         }
00349     }
00350 
00351 /**
00352  * PHP4 Only
00353  *
00354  * Handles custom method calls, like findBy<field> for DB models,
00355  * and custom RPC calls for remote data sources
00356  *
00357  * @param unknown_type $method
00358  * @param unknown_type $params
00359  * @param unknown_type $return
00360  * @return unknown
00361  * @access protected
00362  */
00363     function __call($method, $params, &$return) {
00364         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00365         $return = $db->query($method, $params, $this);
00366         if (isset($this->__backAssociation)) {
00367             $this->__resetAssociations();
00368         }
00369         return true;
00370     }
00371 /**
00372  * Bind model associations on the fly.
00373  *
00374  * @param array $params
00375  * @return true
00376  * @access public
00377  */
00378     function bindModel($params) {
00379         foreach ($params as $assoc => $model) {
00380             if(!isset($this->__backAssociation[$assoc])) {
00381                 $this->__backAssociation[$assoc] = $this->{$assoc};
00382             }
00383 
00384             foreach ($model as $key => $value) {
00385                 $assocName = $key;
00386 
00387                 if (is_numeric($key)) {
00388                     $assocName = $value;
00389                     $value = array();
00390                 }
00391                 $modelName = $assocName;
00392                 $this->{$assoc}[$assocName] = $value;
00393             }
00394         }
00395         $this->__createLinks();
00396         return true;
00397     }
00398 /**
00399  * Turn off associations on the fly.
00400  *
00401  * @param array $params
00402  * @return true
00403  * @access public
00404  */
00405     function unbindModel($params) {
00406         foreach ($params as $assoc => $models) {
00407             if(!isset($this->__backAssociation[$assoc])) {
00408                 $this->__backAssociation[$assoc] = $this->{$assoc};
00409             }
00410 
00411             foreach ($models as $model) {
00412                 $this->__backAssociation = array_merge($this->__backAssociation, $this->{$assoc});
00413                 unset ($this->{$assoc}[$model]);
00414             }
00415         }
00416         return true;
00417     }
00418 /**
00419  * Private helper method to create a set of associations.
00420  *
00421  * @access private
00422  */
00423     function __createLinks() {
00424 
00425         foreach ($this->__associations as $type) {
00426             if (!is_array($this->{$type})) {
00427                 $this->{$type} = explode(',', $this->{$type});
00428 
00429                 foreach ($this->{$type} as $i => $className) {
00430                     $className = trim($className);
00431                     unset ($this->{$type}[$i]);
00432                     $this->{$type}[$className] = array();
00433                 }
00434             }
00435 
00436             foreach ($this->{$type} as $assoc => $value) {
00437                 if (is_numeric($assoc)) {
00438                     unset ($this->{$type}[$assoc]);
00439                     $assoc = $value;
00440                     $value = array();
00441                     $this->{$type}[$assoc] = $value;
00442                 }
00443 
00444                 $className = $assoc;
00445 
00446                 if (isset($value['className']) && !empty($value['className'])) {
00447                     $className = $value['className'];
00448                 }
00449                 $this->__constructLinkedModel($assoc, $className);
00450             }
00451         }
00452 
00453         foreach ($this->__associations as $type) {
00454             $this->__generateAssociation($type);
00455         }
00456     }
00457 
00458 /**
00459  * Private helper method to create associated models of given class.
00460  * @param string $assoc
00461  * @param string $className Class name
00462  * @param string $type Type of assocation
00463  * @access private
00464  */
00465     function __constructLinkedModel($assoc, $className) {
00466         if(empty($className)) {
00467             $className = $assoc;
00468         }
00469 
00470         if (!class_exists($className)) {
00471             loadModel($className);
00472         }
00473         $colKey = Inflector::underscore($className);
00474         $model = array('name' => $className, 'alias' => $assoc);
00475 
00476         if (ClassRegistry::isKeySet($colKey)) {
00477             $this->{$assoc} =& ClassRegistry::getObject($colKey);
00478             $this->{$className} =& $this->{$assoc};
00479         } else {
00480             $this->{$assoc} =& new $className($model);
00481             $this->{$className} =& $this->{$assoc};
00482         }
00483         $this->tableToModel[$this->{$assoc}->table] = $className;
00484         $this->modelToTable[$assoc] = $this->{$assoc}->table;
00485     }
00486 /**
00487  * Build array-based association from string.
00488  *
00489  * @param string $type "Belongs", "One", "Many", "ManyTo"
00490  * @access private
00491  */
00492     function __generateAssociation($type) {
00493         foreach ($this->{$type}as $assocKey => $assocData) {
00494             $class = $assocKey;
00495 
00496             foreach ($this->__associationKeys[$type] as $key) {
00497                 if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] == null) {
00498                     $data = '';
00499 
00500                     switch($key) {
00501                         case 'fields':
00502                             $data = '';
00503                         break;
00504 
00505                         case 'foreignKey':
00506                             $data = Inflector::singularize($this->table) . '_id';
00507 
00508                             if ($type == 'belongsTo') {
00509                                 $data = Inflector::singularize($this->{$class}->table) . '_id';
00510                             }
00511                         break;
00512 
00513                         case 'associationForeignKey':
00514                             $data = Inflector::singularize($this->{$class}->table) . '_id';
00515                         break;
00516 
00517                         case 'joinTable':
00518                             $tables = array($this->table, $this->{$class}->table);
00519                             sort ($tables);
00520                             $data = $tables[0] . '_' . $tables[1];
00521                         break;
00522 
00523                         case 'className':
00524                             $data = $class;
00525                         break;
00526                     }
00527 
00528                     $this->{$type}[$assocKey][$key] = $data;
00529                 }
00530 
00531                 if ($key == 'foreignKey' && !isset($this->keyToTable[$this->{$type}[$assocKey][$key]])) {
00532                     $this->keyToTable[$this->{$type}[$assocKey][$key]][0] = $this->{$class}->table;
00533                     $this->keyToTable[$this->{$type}[$assocKey][$key]][1] = $this->{$class}->alias;
00534                 }
00535             }
00536         }
00537     }
00538 /**
00539  * Sets a custom table for your controller class. Used by your controller to select a database table.
00540  *
00541  * @param string $tableName Name of the custom table
00542  * @access public
00543  */
00544     function setSource($tableName) {
00545         $this->setDataSource($this->useDbConfig);
00546         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00547         $db->cacheSources = $this->cacheSources;
00548 
00549         if ($db->isInterfaceSupported('listSources')) {
00550             $sources = $db->listSources();
00551             if (is_array($sources) && !in_array(low($this->tablePrefix . $tableName), array_map('low', $sources))) {
00552                 return $this->cakeError('missingTable', array(array(
00553                                                 'className' => $this->alias,
00554                                                 'table' => $this->tablePrefix . $tableName)));
00555 
00556             }
00557             $this->_tableInfo = null;
00558         }
00559         $this->table = $this->useTable = $tableName;
00560         $this->tableToModel[$this->table] = $this->alias;
00561         $this->loadInfo();
00562     }
00563 /**
00564  * This function does two things: 1) it scans the array $one for the primary key,
00565  * and if that's found, it sets the current id to the value of $one[id].
00566  * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object.
00567  * 2) Returns an array with all of $one's keys and values.
00568  * (Alternative indata: two strings, which are mangled to
00569  * a one-item, two-dimensional array using $one for a key and $two as its value.)
00570  *
00571  * @param mixed $one Array or string of data
00572  * @param string $two Value string for the alternative indata method
00573  * @return array
00574  * @access public
00575  */
00576     function set($one, $two = null) {
00577         if (is_array($one)) {
00578             if (countdim($one) == 1) {
00579                 $data = array($this->alias => $one);
00580             } else {
00581                 $data = $one;
00582             }
00583         } else {
00584             $data = array($this->alias => array($one => $two));
00585         }
00586 
00587         foreach ($data as $n => $v) {
00588             if (is_array($v)) {
00589 
00590                 foreach ($v as $x => $y) {
00591                     if (empty($this->whitelist) || (in_array($x, $this->whitelist) || $n !== $this->alias)) {
00592                         if (isset($this->validationErrors[$x])) {
00593                             unset ($this->validationErrors[$x]);
00594                         }
00595 
00596                         if ($n == $this->name || is_array($y)) {
00597                             if ($x === $this->primaryKey) {
00598                                 $this->id = $y;
00599                             }
00600                             $this->data[$n][$x] = $y;
00601                         }
00602                     }
00603                 }
00604             }
00605         }
00606         return $data;
00607     }
00608 /**
00609  * Returns an array of table metadata (column names and types) from the database.
00610  *
00611  * @return array Array of table metadata
00612  * @access public
00613  */
00614     function loadInfo() {
00615         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00616         $db->cacheSources = $this->cacheSources;
00617 
00618         if (!is_object($this->_tableInfo) && $db->isInterfaceSupported('describe') && $this->useTable !== false) {
00619             $info = new Set($db->describe($this));
00620 
00621             foreach($info->value as $field => $value) {
00622                 $fields[] = am(array('name'=> $field), $value);
00623             }
00624             unset($info);
00625             $this->_tableInfo = new Set($fields);
00626         } elseif ($this->useTable === false) {
00627             $this->_tableInfo = new Set();
00628         }
00629         return $this->_tableInfo;
00630     }
00631 /**
00632  * Returns an associative array of field names and column types.
00633  *
00634  * @return array
00635  * @access public
00636  */
00637     function getColumnTypes() {
00638         $columns = $this->loadInfo();
00639         $columns = $columns->value;
00640         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00641         $cols = array();
00642 
00643         foreach ($columns as $col) {
00644             $cols[$col['name']] = $col['type'];
00645         }
00646         return $cols;
00647     }
00648 /**
00649  * Returns the column type of a column in the model
00650  *
00651  * @param string $column The name of the model column
00652  * @return string
00653  * @access public
00654  */
00655     function getColumnType($column) {
00656         $columns = $this->loadInfo();
00657         $columns = $columns->value;
00658         $cols = array();
00659 
00660         foreach ($columns as $col) {
00661             if ($col['name'] == $column) {
00662                 return $col['type'];
00663             }
00664         }
00665         return null;
00666     }
00667 /**
00668  * Returns true if this Model has given field in its database table.
00669  *
00670  * @param string $name Name of field to look for
00671  * @return boolean
00672  * @access public
00673  */
00674     function hasField($name) {
00675         if (is_array($name)) {
00676             foreach ($name as $n) {
00677                 if ($this->hasField($n)) {
00678                     return $n;
00679                 }
00680             }
00681             return false;
00682         }
00683 
00684         if (empty($this->_tableInfo)) {
00685             $this->loadInfo();
00686         }
00687 
00688         if ($this->_tableInfo != null) {
00689             return in_array($name, $this->_tableInfo->extract('{n}.name'));
00690         }
00691         return false;
00692     }
00693 /**
00694  * Initializes the model for writing a new record.
00695  *
00696  * @return boolean True
00697  * @access public
00698  */
00699     function create() {
00700         $this->id = false;
00701         unset ($this->data);
00702         $this->data = $this->validationErrors = array();
00703         return true;
00704     }
00705 /**
00706  * @deprecated
00707  */
00708     function setId($id) {
00709         $this->id = $id;
00710     }
00711 /**
00712  * Use query() instead.
00713  * @deprecated
00714  */
00715     function findBySql($sql) {
00716         return $this->query($sql);
00717     }
00718 /**
00719  * Returns a list of fields from the database
00720  *
00721  * @param mixed $id The ID of the record to read
00722  * @param mixed $fields String of single fieldname, or an array of fieldnames.
00723  * @return array Array of database fields
00724  * @access public
00725  */
00726     function read($fields = null, $id = null) {
00727         $this->validationErrors = array();
00728 
00729         if ($id != null) {
00730             $this->id = $id;
00731         }
00732 
00733         $id = $this->id;
00734 
00735         if (is_array($this->id)) {
00736             $id = $this->id[0];
00737         }
00738 
00739         if ($this->id !== null && $this->id !== false) {
00740             $db =& ConnectionManager::getDataSource($this->useDbConfig);
00741             $field = $db->name($this->alias) . '.' . $db->name($this->primaryKey);
00742             return $this->find($field . ' = ' . $db->value($id, $this->getColumnType($this->primaryKey)), $fields);
00743         } else {
00744             return false;
00745         }
00746     }
00747 /**
00748  * Returns contents of a field in a query matching given conditions.
00749  *
00750  * @param string $name Name of field to get
00751  * @param array $conditions SQL conditions (defaults to NULL)
00752  * @param string $order SQL ORDER BY fragment
00753  * @return field contents
00754  * @access public
00755  */
00756     function field($name, $conditions = null, $order = null) {
00757         if ($conditions === null && $this->id !== false) {
00758             $conditions = array($this->alias . '.' . $this->primaryKey => $this->id);
00759         }
00760 
00761         if ($data = $this->find($conditions, $name, $order, 0)) {
00762 
00763             if (strpos($name, '.') === false) {
00764                 if (isset($data[$this->alias][$name])) {
00765                     return $data[$this->alias][$name];
00766                 } else {
00767                     return false;
00768                 }
00769             } else {
00770                 $name = explode('.', $name);
00771 
00772                 if (isset($data[$name[0]][$name[1]])) {
00773                     return $data[$name[0]][$name[1]];
00774                 } else {
00775                     return false;
00776                 }
00777             }
00778         } else {
00779             return false;
00780         }
00781     }
00782 /**
00783  * Saves a single field to the database.
00784  *
00785  * @param string $name Name of the table field
00786  * @param mixed $value Value of the field
00787  * @param boolean $validate Whether or not this model should validate before saving (defaults to false)
00788  * @return boolean True on success save
00789  * @access public
00790  */
00791     function saveField($name, $value, $validate = false) {
00792         return $this->save(array($this->alias => array($name => $value)), $validate);
00793     }
00794 /**
00795  * Saves model data to the database.
00796  * By default, validation occurs before save.
00797  *
00798  * @param array $data Data to save.
00799  * @param boolean $validate If set, validation will be done before the save
00800  * @param array $fieldList List of fields to allow to be written
00801  * @return boolean success
00802  * @access public
00803  */
00804     function save($data = null, $validate = true, $fieldList = array()) {
00805         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00806         $_whitelist = $this->whitelist;
00807 
00808         if (!empty($fieldList)) {
00809             $this->whitelist = $fieldList;
00810         } elseif ($fieldList === null) {
00811             $this->whitelist = array();
00812         }
00813 
00814         if ($data) {
00815             if (countdim($data) == 1) {
00816                 $this->set(array($this->alias => $data));
00817             } else {
00818                 $this->set($data);
00819             }
00820         }
00821 
00822         if ($validate && !$this->validates()) {
00823             $this->whitelist = $_whitelist;
00824             return false;
00825         }
00826 
00827         if (!$this->beforeSave()) {
00828             $this->whitelist = $_whitelist;
00829             return false;
00830         }
00831         $fields = $values = array();
00832 
00833         if (isset($this->data[$this->alias][$this->primaryKey]) && empty($this->data[$this->alias][$this->primaryKey])) {
00834             unset($this->data[$this->alias][$this->primaryKey]);
00835         }
00836 
00837         if (count($this->data) > 1) {
00838             $weHaveMulti = true;
00839             $joined = false;
00840         } else {
00841             $weHaveMulti = false;
00842         }
00843 
00844         foreach ($this->data as $n => $v) {
00845             if (isset($weHaveMulti) && isset($v[$n]) && in_array($n, array_keys($this->hasAndBelongsToMany))) {
00846                 $joined[] = $v;
00847             } else {
00848                 if ($n === $this->alias) {
00849                     foreach (array('created', 'updated', 'modified') as $field) {
00850                         if (array_key_exists($field, $v) && (empty($v[$field]) || $v[$field] === null)) {
00851                             unset($v[$field]);
00852                         }
00853                     }
00854 
00855                     foreach ($v as $x => $y) {
00856                         if ($this->hasField($x)) {
00857                             $fields[] = $x;
00858                             $values[] = $y;
00859                         }
00860                     }
00861                 }
00862             }
00863         }
00864         $exists = $this->exists();
00865 
00866         if (!$exists && $this->hasField('created') && !in_array('created', $fields)) {
00867             $fields[] = 'created';
00868             $values[] = date('Y-m-d H:i:s');
00869         }
00870 
00871         if ($this->hasField('modified') && !in_array('modified', $fields)) {
00872             $fields[] = 'modified';
00873             $values[] = date('Y-m-d H:i:s');
00874         }
00875 
00876         if ($this->hasField('updated') && !in_array('updated', $fields)) {
00877             $fields[] = 'updated';
00878             $values[] = date('Y-m-d H:i:s');
00879         }
00880 
00881         if (!$exists) {
00882             $this->id = false;
00883         }
00884         $this->whitelist = $_whitelist;
00885 
00886         if (count($fields)) {
00887             if (!empty($this->id)) {
00888                 if ($db->update($this, $fields, $values)) {
00889                     if (!empty($joined)) {
00890                         $this->__saveMulti($joined, $this->id);
00891                     }
00892 
00893                     $this->afterSave();
00894                     $this->data = false;
00895                     $this->_clearCache();
00896                     return true;
00897                 } else {
00898                     return false;
00899                 }
00900             } else {
00901                 if ($db->create($this, $fields, $values)) {
00902                     if (!empty($joined)) {
00903                         $this->__saveMulti($joined, $this->id);
00904                     }
00905 
00906                     $this->afterSave();
00907                     $this->data = false;
00908                     $this->_clearCache();
00909                     $this->validationErrors = array();
00910                     return true;
00911                 } else {
00912                     return false;
00913                 }
00914             }
00915         } else {
00916             return false;
00917         }
00918     }
00919 /**
00920  * Saves model hasAndBelongsToMany data to the database.
00921  *
00922  * @param array $joined Data to save.
00923  * @param string $id
00924  * @return void
00925  * @access private
00926  */
00927     function __saveMulti($joined, $id) {
00928         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00929         foreach ($joined as $x => $y) {
00930             foreach ($y as $assoc => $value) {
00931                 if (isset($this->hasAndBelongsToMany[$assoc])) {
00932                     $joinTable[$assoc] = $this->hasAndBelongsToMany[$assoc]['joinTable'];
00933                     $mainKey[$assoc] = $this->hasAndBelongsToMany[$assoc]['foreignKey'];
00934                     $keys[] = $this->hasAndBelongsToMany[$assoc]['foreignKey'];
00935                     $keys[] = $this->hasAndBelongsToMany[$assoc]['associationForeignKey'];
00936                     $fields[$assoc]  = join(',', $keys);
00937                     unset($keys);
00938 
00939                     foreach ($value as $update) {
00940                         if (!empty($update)) {
00941                             $values[]  = $db->value($id, $this->getColumnType($this->primaryKey));
00942                             $values[]  = $db->value($update);
00943                             $values    = join(',', $values);
00944                             $newValues[] = "({$values})";
00945                             unset ($values);
00946                         }
00947                     }
00948 
00949                     if (!empty($newValues)) {
00950                         $newValue[$assoc] = $newValues;
00951                         unset($newValues);
00952                     } else {
00953                         $newValue[$assoc] = array();
00954                     }
00955                 }
00956             }
00957         }
00958 
00959         if (isset($joinTable)) {
00960             $total = count($joinTable);
00961 
00962             if (is_array($newValue)) {
00963                 foreach ($newValue as $loopAssoc => $val) {
00964                     $db =& ConnectionManager::getDataSource($this->useDbConfig);
00965                     $table = $db->name($db->fullTableName($joinTable[$loopAssoc]));
00966                     $db->query("DELETE FROM {$table} WHERE {$mainKey[$loopAssoc]} = '{$id}'");
00967 
00968                     if (!empty($newValue[$loopAssoc])) {
00969                         $secondCount = count($newValue[$loopAssoc]);
00970                         for ($x = 0; $x < $secondCount; $x++) {
00971                             $db->query("INSERT INTO {$table} ({$fields[$loopAssoc]}) VALUES {$newValue[$loopAssoc][$x]}");
00972                         }
00973                     }
00974                 }
00975             }
00976         }
00977     }
00978 /**
00979  * Synonym for del().
00980  *
00981  * @param mixed $id
00982  * @see function del
00983  * @return boolean True on success
00984  * @access public
00985  */
00986     function remove($id = null, $cascade = true) {
00987         return $this->del($id, $cascade);
00988     }
00989 /**
00990  * Removes record for given id. If no id is given, the current id is used. Returns true on success.
00991  *
00992  * @param mixed $id Id of record to delete
00993  * @return boolean True on success
00994  * @access public
00995  */
00996     function del($id = null, $cascade = true) {
00997         if ($id) {
00998             $this->id = $id;
00999         }
01000         $id = $this->id;
01001 
01002         if ($this->exists() && $this->beforeDelete()) {
01003             $db =& ConnectionManager::getDataSource($this->useDbConfig);
01004 
01005             $this->_deleteMulti($id);
01006             $this->_deleteHasMany($id, $cascade);
01007             $this->_deleteHasOne($id, $cascade);
01008             $this->id = $id;
01009 
01010             if ($db->delete($this)) {
01011                 $this->afterDelete();
01012                 $this->_clearCache();
01013                 $this->id = false;
01014                 return true;
01015             }
01016         }
01017 
01018         return false;
01019     }
01020 /**
01021  * Alias for del()
01022  *
01023  * @param mixed $id Id of record to delete
01024  * @return boolean True on success
01025  * @access public
01026  */
01027     function delete($id = null, $cascade = true) {
01028         return $this->del($id, $cascade);
01029     }
01030 /**
01031  * Cascades model deletes to hasMany relationships.
01032  *
01033  * @param string $id
01034  * @return null
01035  * @access protected
01036  */
01037     function _deleteHasMany($id, $cascade) {
01038         if (!empty($this->__backAssociation)) {
01039             $savedAssociatons = $this->__backAssociation;
01040             $this->__backAssociation = array();
01041         }
01042         foreach ($this->hasMany as $assoc => $data) {
01043             if ($data['dependent'] === true && $cascade === true) {
01044                 $model =& $this->{$data['className']};
01045                 $field = $model->escapeField($data['foreignKey']);
01046                 $model->recursive = 0;
01047                 $records = $model->findAll("$field = '$id'", $model->primaryKey, null, null);
01048 
01049                 if ($records != false) {
01050                     foreach ($records as $record) {
01051                         $model->del($record[$data['className']][$model->primaryKey]);
01052                     }
01053                 }
01054             }
01055         }
01056         if (isset($savedAssociatons)) {
01057             $this->__backAssociation = $savedAssociatons;
01058         }
01059     }
01060 /**
01061  * Cascades model deletes to hasOne relationships.
01062  *
01063  * @param string $id
01064  * @return null
01065  * @access protected
01066  */
01067     function _deleteHasOne($id, $cascade) {
01068         if (!empty($this->__backAssociation)) {
01069             $savedAssociatons = $this->__backAssociation;
01070             $this->__backAssociation = array();
01071         }
01072         foreach ($this->hasOne as $assoc => $data) {
01073             if ($data['dependent'] === true && $cascade === true) {
01074                 $model =& $this->{$data['className']};
01075                 $field = $model->escapeField($data['foreignKey']);
01076                 $model->recursive = 0;
01077                 $records = $model->findAll("$field = '$id'", $model->primaryKey, null, null);
01078 
01079                 if ($records != false) {
01080                     foreach ($records as $record) {
01081                         $model->del($record[$data['className']][$model->primaryKey]);
01082                     }
01083                 }
01084             }
01085         }
01086         if (isset($savedAssociatons)) {
01087             $this->__backAssociation = $savedAssociatons;
01088         }
01089     }
01090 /**
01091  * Cascades model deletes to HABTM join keys.
01092  *
01093  * @param string $id
01094  * @return null
01095  * @access protected
01096  */
01097     function _deleteMulti($id) {
01098         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01099         foreach ($this->hasAndBelongsToMany as $assoc => $data) {
01100             $db->execute("DELETE FROM " . $db->name($db->fullTableName($data['joinTable'])) . " WHERE " . $db->name($data['foreignKey']) . " = '{$id}'");
01101         }
01102     }
01103 /**
01104  * Returns true if a record with set id exists.
01105  *
01106  * @return boolean True if such a record exists
01107  * @access public
01108  */
01109     function exists() {
01110         if ($this->id) {
01111             $id = $this->id;
01112 
01113             if (is_array($id)) {
01114                 $id = $id[0];
01115             }
01116 
01117             $db =& ConnectionManager::getDataSource($this->useDbConfig);
01118             return $db->hasAny($this, array($this->primaryKey => $id));
01119         }
01120         return false;
01121     }
01122 /**
01123  * Returns true if a record that meets given conditions exists
01124  *
01125  * @param array $conditions SQL conditions array
01126  * @return boolean True if such a record exists
01127  * @access public
01128  */
01129     function hasAny($conditions = null) {
01130         return ($this->findCount($conditions) != false);
01131     }
01132 /**
01133  * Return a single row as a resultset array.
01134  * By using the $recursive parameter, the call can access further "levels of association" than
01135  * the ones this model is directly associated to.
01136  *
01137  * @param array $conditions SQL conditions array
01138  * @param mixed $fields Either a single string of a field name, or an array of field names
01139  * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
01140  * @param int $recursive The number of levels deep to fetch associated records
01141  * @return array Array of records
01142  * @access public
01143  */
01144     function find($conditions = null, $fields = null, $order = null, $recursive = null) {
01145         $data = $this->findAll($conditions, $fields, $order, 1, null, $recursive);
01146 
01147         if (empty($data[0])) {
01148             return false;
01149         }
01150 
01151         return $data[0];
01152     }
01153 /**
01154  * Returns a resultset array with specified fields from database matching given conditions.
01155  * By using the $recursive parameter, the call can access further "levels of association" than
01156  * the ones this model is directly associated to.
01157  *
01158  * @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...)
01159  * @param mixed $fields Either a single string of a field name, or an array of field names
01160  * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
01161  * @param int $limit SQL LIMIT clause, for calculating items per page.
01162  * @param int $page Page number, for accessing paged data
01163  * @param int $recursive The number of levels deep to fetch associated records
01164  * @return array Array of records
01165  * @access public
01166  */
01167     function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
01168 
01169         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01170         $this->id = $this->getID();
01171         $offset = null;
01172 
01173         if ($page > 1 && $limit != null) {
01174             $offset = ($page - 1) * $limit;
01175         }
01176 
01177         if ($order == null) {
01178             $order = array();
01179         } else {
01180             $order = array($order);
01181         }
01182 
01183         $queryData = array('conditions' => $conditions,
01184                             'fields'    => $fields,
01185                             'joins'     => array(),
01186                             'limit'     => $limit,
01187                             'offset'    => $offset,
01188                             'order'     => $order
01189         );
01190 
01191         $ret = $this->beforeFind($queryData);
01192         if (is_array($ret)) {
01193             $queryData = $ret;
01194         } elseif ($ret === false) {
01195             return null;
01196         }
01197 
01198         $return = $this->afterFind($db->read($this, $queryData, $recursive));
01199 
01200         if (!empty($this->__backAssociation)) {
01201             $this->__resetAssociations();
01202         }
01203 
01204         return $return;
01205     }
01206 /**
01207  * Method is called only when bindTo<ModelName>() is used.
01208  * This resets the association arrays for the model back
01209  * to the original as set in the model.
01210  *
01211  * @return boolean
01212  * @access private
01213  */
01214     function __resetAssociations() {
01215         foreach ($this->__associations as $type) {
01216             if (isset($this->__backAssociation[$type])) {
01217                 $this->{$type} = $this->__backAssociation[$type];
01218             }
01219         }
01220 
01221         $this->__backAssociation = array();
01222         return true;
01223     }
01224 /**
01225  * Runs a direct query against the bound DataSource, and returns the result.
01226  *
01227  * @param string $data Query data
01228  * @return array
01229  * @access public
01230  */
01231     function execute($data) {
01232         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01233         $data = $db->fetchAll($data, $this->cacheQueries);
01234 
01235         foreach ($data as $key => $value) {
01236             foreach ($this->tableToModel as $key1 => $value1) {
01237                 if (isset($data[$key][$key1])) {
01238                     $newData[$key][$value1] = $data[$key][$key1];
01239                 }
01240             }
01241         }
01242 
01243         if (!empty($newData)) {
01244             return $newData;
01245         }
01246 
01247         return $data;
01248     }
01249 /**
01250  * Returns number of rows matching given SQL condition.
01251  *
01252  * @param array $conditions SQL conditions array for findAll
01253  * @param int $recursize The number of levels deep to fetch associated records
01254  * @return int Number of matching rows
01255  * @see Model::findAll
01256  * @access public
01257  */
01258     function findCount($conditions = null, $recursive = 0) {
01259         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01260 
01261         list($data) = $this->findAll($conditions, 'COUNT(*) AS ' . $db->name('count'), null, null, 1, $recursive);
01262 
01263         if (isset($data[0]['count'])) {
01264             return $data[0]['count'];
01265         } elseif (isset($data[$this->alias]['count'])) {
01266             return $data[$this->alias]['count'];
01267         }
01268 
01269         return false;
01270     }
01271 /**
01272  * Special findAll variation for tables joined to themselves.
01273  * The table needs the fields id and parent_id to work.
01274  *
01275  * @param array $conditions Conditions for the findAll() call
01276  * @param array $fields Fields for the findAll() call
01277  * @param string $sort SQL ORDER BY statement
01278  * @return array
01279  * @access public
01280  * @todo Perhaps create a Component with this logic
01281  */
01282     function findAllThreaded($conditions = null, $fields = null, $sort = null) {
01283         return $this->__doThread(Model::findAll($conditions, $fields, $sort), null);
01284     }
01285 /**
01286  * Private, recursive helper method for findAllThreaded.
01287  *
01288  * @param array $data
01289  * @param string $root NULL or id for root node of operation
01290  * @return array
01291  * @access private
01292  * @see findAllThreaded
01293  */
01294     function __doThread($data, $root) {
01295         $out = array();
01296         $sizeOf = sizeof($data);
01297 
01298         for ($ii = 0; $ii < $sizeOf; $ii++) {
01299             if (($data[$ii][$this->alias]['parent_id'] == $root) || (($root === null) && ($data[$ii][$this->alias]['parent_id'] == '0'))) {
01300                 $tmp = $data[$ii];
01301 
01302                 if (isset($data[$ii][$this->alias][$this->primaryKey])) {
01303                     $tmp['children'] = $this->__doThread($data, $data[$ii][$this->alias][$this->primaryKey]);
01304                 } else {
01305                     $tmp['children'] = null;
01306                 }
01307 
01308                 $out[] = $tmp;
01309             }
01310         }
01311 
01312         return $out;
01313     }
01314 /**
01315  * Returns an array with keys "prev" and "next" that holds the id's of neighbouring data,
01316  * which is useful when creating paged lists.
01317  *
01318  * @param string $conditions SQL conditions for matching rows
01319  * @param string $field Field name (parameter for findAll)
01320  * @param unknown_type $value
01321  * @return array Array with keys "prev" and "next" that holds the id's
01322  * @access public
01323  */
01324     function findNeighbours($conditions = null, $field, $value) {
01325         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01326 
01327         if (!is_null($conditions)) {
01328                 $conditions = $conditions . ' AND ';
01329         }
01330 
01331         @list($prev) = Model::findAll($conditions . $field . ' < ' . $db->value($value), $field, $field . ' DESC', 1, null, 0);
01332         @list($next) = Model::findAll($conditions . $field . ' > ' . $db->value($value), $field, $field . ' ASC', 1, null, 0);
01333 
01334         if (!isset($prev)) {
01335             $prev = null;
01336         }
01337 
01338         if (!isset($next)) {
01339             $next = null;
01340         }
01341 
01342         return array('prev' => $prev, 'next' => $next);
01343     }
01344 /**
01345  * Returns a resultset for given SQL statement. Generic SQL queries should be made with this method.
01346  *
01347  * @param string $sql SQL statement
01348  * @return array Resultset
01349  * @access public
01350  */
01351     function query() {
01352         $params = func_get_args();
01353         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01354         return call_user_func_array(array(&$db, 'query'), $params);
01355     }
01356 /**
01357  * Returns true if all fields pass validation, otherwise false.
01358  *
01359  * @param array $data POST data
01360  * @return boolean True if there are no errors
01361  * @access public
01362  */
01363     function validates($data = array()) {
01364         $errors = $this->invalidFields($data);
01365         return count($errors) == 0;
01366     }
01367 /**
01368  * Returns an array of invalid fields.
01369  *
01370  * @param array $data
01371  * @return array Array of invalid fields or boolean case any error occurs
01372  * @access public
01373  */
01374     function invalidFields($data = array()) {
01375         if (empty($data)) {
01376             $data = $this->data;
01377         }
01378 
01379         if (!$this->beforeValidate()) {
01380             return $this->validationErrors;
01381         }
01382 
01383         if (!isset($this->validate)) {
01384             return $this->validationErrors;
01385         }
01386 
01387         if (!empty($data)) {
01388             $data = $data;
01389         } elseif (isset($this->data)) {
01390             $data = $this->data;
01391         }
01392 
01393         if (isset($data[$this->alias])) {
01394             $data = $data[$this->alias];
01395         }
01396 
01397         foreach ($this->validate as $field_name => $validator) {
01398             if (isset($data[$field_name]) && !preg_match($validator, $data[$field_name])) {
01399                 $this->invalidate($field_name);
01400             }
01401         }
01402         return $this->validationErrors;
01403     }
01404 /**
01405  * Sets a field as invalid
01406  *
01407  * @param string $field The name of the field to invalidate
01408  * @return void
01409  * @access public
01410  */
01411     function invalidate($field) {
01412         if (!is_array($this->validationErrors)) {
01413             $this->validationErrors = array();
01414         }
01415         $this->validationErrors[$field] = 1;
01416     }
01417 /**
01418  * Returns true if given field name is a foreign key in this Model.
01419  *
01420  * @param string $field Returns true if the input string ends in "_id"
01421  * @return True if the field is a foreign key listed in the belongsTo array.
01422  * @access public
01423  */
01424     function isForeignKey($field) {
01425         $foreignKeys = array();
01426 
01427         if (count($this->belongsTo)) {
01428             foreach ($this->belongsTo as $assoc => $data) {
01429                 $foreignKeys[] = $data['foreignKey'];
01430             }
01431         }
01432         return (bool)(in_array($field, $foreignKeys));
01433     }
01434 /**
01435  * Gets the display field for this model
01436  *
01437  * @return string The name of the display field for this Model (i.e. 'name', 'title').
01438  * @access public
01439  */
01440     function getDisplayField() {
01441         return $this->displayField;
01442     }
01443 /**
01444  * Returns a resultset array with specified fields from database matching given conditions.
01445  * Method can be used to generate option lists for SELECT elements.
01446  *
01447  * @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...)
01448  * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
01449  * @param int $limit SQL LIMIT clause, for calculating items per page
01450  * @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
01451  * @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
01452  * @return array An associative array of records, where the id is the key, and the display field is the value
01453  * @access public
01454  */
01455     function generateList($conditions = null, $order = null, $limit = null, $keyPath = null, $valuePath = null) {
01456         if ($keyPath == null && $valuePath == null && $this->hasField($this->displayField)) {
01457             $fields = array($this->primaryKey, $this->displayField);
01458         } else {
01459             $fields = null;
01460         }
01461         $recursive = $this->recursive;
01462 
01463         if ($recursive >= 1) {
01464             $this->recursive = -1;
01465         }
01466         $result = $this->findAll($conditions, $fields, $order, $limit);
01467         $this->recursive = $recursive;
01468 
01469         if (!$result) {
01470             return false;
01471         }
01472 
01473         if ($keyPath == null) {
01474             $keyPath = '{n}.' . $this->alias . '.' . $this->primaryKey;
01475         }
01476 
01477         if ($valuePath == null) {
01478             $valuePath = '{n}.' . $this->alias . '.' . $this->displayField;
01479         }
01480 
01481         $keys = Set::extract($result, $keyPath);
01482         $vals = Set::extract($result, $valuePath);
01483 
01484         if (!empty($keys) && !empty($vals)) {
01485             $return = array_combine($keys, $vals);
01486             return $return;
01487         }
01488         return null;
01489     }
01490 /**
01491  * Escapes the field name and prepends the model name. Escaping will be done according to the current database driver's rules.
01492  *
01493  * @param unknown_type $field
01494  * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
01495  * @access public
01496  */
01497     function escapeField($field) {
01498         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01499         return $db->name($this->alias) . '.' . $db->name($field);
01500     }
01501 /**
01502  * Returns the current record's ID
01503  *
01504  * @param unknown_type $list
01505  * @return mixed The ID of the current record
01506  * @access public
01507  */
01508     function getID($list = 0) {
01509         if (!is_array($this->id)) {
01510             return $this->id;
01511         }
01512 
01513         if (count($this->id) == 0) {
01514             return false;
01515         }
01516 
01517         if (isset($this->id[$list])) {
01518             return $this->id[$list];
01519         }
01520 
01521         foreach ($this->id as $id) {
01522             return $id;
01523         }
01524 
01525         return false;
01526     }
01527 /**
01528  * Returns the ID of the last record this Model inserted
01529  *
01530  * @return mixed
01531  * @access public
01532  */
01533     function getLastInsertID() {
01534         return $this->getInsertID();
01535     }
01536 /**
01537  * Returns the ID of the last record this Model inserted
01538  *
01539  * @return mixed
01540  * @access public
01541  */
01542     function getInsertID() {
01543         return $this->__insertID;
01544     }
01545 /**
01546  * Sets the ID of the last record this Model inserted
01547  *
01548  * @param mixed $id
01549  * @return void
01550  */
01551     function setInsertID($id) {
01552         $this->__insertID = $id;
01553     }
01554 /**
01555  * Returns the number of rows returned from the last query
01556  *
01557  * @return int
01558  * @access public
01559  */
01560     function getNumRows() {
01561         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01562         return $db->lastNumRows();
01563     }
01564 /**
01565  * Returns the number of rows affected by the last query
01566  *
01567  * @return int
01568  * @access public
01569  */
01570     function getAffectedRows() {
01571         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01572         return $db->lastAffected();
01573     }
01574 /**
01575  * Sets the DataSource to which this model is bound
01576  *
01577  * @param string $dataSource The name of the DataSource, as defined in Connections.php
01578  * @return boolean True on success
01579  * @access public
01580  */
01581     function setDataSource($dataSource = null) {
01582         if ($dataSource != null) {
01583             $this->useDbConfig = $dataSource;
01584         }
01585         $db =& ConnectionManager::getDataSource($this->useDbConfig);
01586 
01587         if (!empty($db->config['prefix']) && $this->tablePrefix === null) {
01588             $this->tablePrefix = $db->config['prefix'];
01589         }
01590 
01591         if (empty($db) || $db == null || !is_object($db)) {
01592             return $this->cakeError('missingConnection', array(array('className' => $this->alias)));
01593         }
01594     }
01595 /**
01596  * Before find callback
01597  *
01598  * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
01599  * @return boolean True if the operation should continue, false if it should abort
01600  * @access public
01601  */
01602     function beforeFind(&$queryData) {
01603         return true;
01604     }
01605 /**
01606  * After find callback. Can be used to modify any results returned by find and findAll.
01607  *
01608  * @param mixed $results The results of the find operation
01609  * @return mixed Result of the find operation
01610  * @access public
01611  */
01612     function afterFind($results) {
01613         return $results;
01614     }
01615 /**
01616  * Before save callback
01617  *
01618  * @return boolean True if the operation should continue, false if it should abort
01619  * @access public
01620  */
01621     function beforeSave() {
01622         return true;
01623     }
01624 /**
01625  * After save callback
01626  *
01627  * @return boolean
01628  * @access public
01629  */
01630     function afterSave() {
01631         return true;
01632     }
01633 /**
01634  * Before delete callback
01635  *
01636  * @return boolean True if the operation should continue, false if it should abort
01637  * @access public
01638  */
01639     function beforeDelete() {
01640         return true;
01641     }
01642 /**
01643  * After delete callback
01644  *
01645  * @return boolean
01646  * @access public
01647  */
01648     function afterDelete() {
01649         return true;
01650     }
01651 /**
01652  * Before validate callback
01653  *
01654  * @return boolean
01655  * @access public
01656  */
01657     function beforeValidate() {
01658         return true;
01659     }
01660 /**
01661  * DataSource error callback
01662  *
01663  * @return void
01664  */
01665     function onError() {
01666     }
01667 /**
01668  * Private method.  Clears cache for this model
01669  *
01670  * @param string $type If null this deletes cached views if CACHE_CHECK is true
01671  *                     Will be used to allow deleting query cache also
01672  * @return boolean true on delete
01673  * @access protected
01674  */
01675     function _clearCache($type = null) {
01676         if ($type === null) {
01677             if (defined('CACHE_CHECK') && CACHE_CHECK === true) {
01678                 $assoc[] = strtolower(Inflector::pluralize($this->alias));
01679 
01680                 foreach ($this->__associations as $key => $association) {
01681                     foreach ($this->$association as $key => $className) {
01682                         $check = strtolower(Inflector::pluralize($className['className']));
01683 
01684                         if (!in_array($check, $assoc)) {
01685                             $assoc[] = strtolower(Inflector::pluralize($className['className']));
01686                         }
01687                     }
01688                 }
01689                 clearCache($assoc);
01690                 return true;
01691             }
01692         } else {
01693             //Will use for query cache deleting
01694         }
01695     }
01696 /**
01697  * Called when serializing a model
01698  *
01699  * @return array
01700  * @access public
01701  */
01702     function __sleep() {
01703         $return = array_keys(get_object_vars($this));
01704         return $return;
01705     }
01706 /**
01707  * Called when unserializing a model
01708  *
01709  * @return void
01710  * @access public
01711  */
01712     function __wakeup() {
01713     }
01714 }
01715 // --- PHP4 Only
01716 overload ('Model');
01717 // --- PHP4 Only
01718 
01719 ?>