2006-01-12 02:10:47 +00:00
|
|
|
<?php
|
|
|
|
/* SVN FILE: $Id$ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Object-relational mapper.
|
|
|
|
*
|
|
|
|
* DBO-backed object data model, for mapping database tables to Cake objects.
|
|
|
|
*
|
|
|
|
* PHP versions 4 and 5
|
|
|
|
*
|
|
|
|
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
|
2006-01-20 07:46:14 +00:00
|
|
|
* Copyright (c) 2006, Cake Software Foundation, Inc.
|
2006-01-12 02:10:47 +00:00
|
|
|
* 1785 E. Sahara Avenue, Suite 490-204
|
|
|
|
* Las Vegas, Nevada 89104
|
|
|
|
*
|
|
|
|
* Licensed under The MIT License
|
|
|
|
* Redistributions of files must retain the above copyright notice.
|
|
|
|
*
|
|
|
|
* @filesource
|
2006-01-20 07:46:14 +00:00
|
|
|
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
|
2006-01-12 02:10:47 +00:00
|
|
|
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
|
|
|
|
* @package cake
|
|
|
|
* @subpackage cake.cake.libs.model
|
|
|
|
* @since CakePHP v 0.10.0.0
|
|
|
|
* @version $Revision$
|
|
|
|
* @modifiedby $LastChangedBy$
|
|
|
|
* @lastmodified $Date$
|
|
|
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Included libs
|
|
|
|
*/
|
|
|
|
uses('class_registry', 'validators');
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Object-relational mapper.
|
|
|
|
*
|
|
|
|
* DBO-backed object data model.
|
|
|
|
* Automatically selects a database table name based on a pluralized lowercase object class name
|
|
|
|
* (i.e. class 'User' => table 'users'; class 'Man' => table 'men')
|
|
|
|
* The table is required to have at least 'id auto_increment', 'created datetime',
|
|
|
|
* and 'modified datetime' fields.
|
|
|
|
*
|
|
|
|
* @package cake
|
|
|
|
* @subpackage cake.cake.libs.model
|
|
|
|
* @since CakePHP v 0.2.9
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
class Model extends Object
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The name of the DataSource connection that this Model uses
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
*/
|
2006-01-12 03:56:59 +00:00
|
|
|
var $useDbConfig = 'default';
|
2006-01-12 02:10:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The DataSource connection object that this Model uses
|
|
|
|
*
|
|
|
|
* @var unknown_type
|
|
|
|
* @access public
|
|
|
|
*/
|
2006-01-12 03:56:59 +00:00
|
|
|
var $db = null;
|
2006-01-12 02:10:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Enter description here... Still used?
|
|
|
|
*
|
|
|
|
* @var unknown_type
|
|
|
|
* @access public
|
2006-01-20 07:46:14 +00:00
|
|
|
* @todo Is this still used? -OJ 22 nov 2006
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
var $parent = false;
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Custom database table name.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $useTable = null;
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $displayField = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
*Value of the primary key ID of the record that this model is currently pointing to
|
|
|
|
*
|
|
|
|
* @var unknown_type
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $id = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Container for the data that this model gets from persistent storage (the database).
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $data = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Table name for this Model.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
*/
|
2006-01-12 03:56:59 +00:00
|
|
|
var $table = false;
|
2006-01-12 02:10:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The name of the ID field for this Model.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $primaryKey = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Table metadata
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
* @access private
|
|
|
|
*/
|
|
|
|
var $_tableInfo = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of validation rules. Append entries for validation as ('field_name' => '/^perl_compat_regexp$/')
|
|
|
|
* that have to match with preg_match(). Use these rules with Model::validate()
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $validate = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Errors in validation
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $validationErrors = null;
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Database table prefix for tables in model.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $tablePrefix = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Name of the model.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $name = null;
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Name of the current model.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $currentModel = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of table names included in the Model description. Used for associations.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $tableToModel = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of Model names by used tables. Used for associations.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $modelToTable = array();
|
|
|
|
|
2006-01-22 05:12:28 +00:00
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* List of Foreign Key names to used tables. Used for associations.
|
2006-01-22 05:12:28 +00:00
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $keyToTable = array();
|
|
|
|
|
2006-01-12 02:10:47 +00:00
|
|
|
/**
|
|
|
|
* Alias table names for model, for use in SQL JOIN statements.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $alias = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not transactions for this model should be logged
|
|
|
|
*
|
|
|
|
* @var boolean
|
|
|
|
*/
|
|
|
|
var $logTransactions = false;
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Whether or not to enable transactions for this model (i.e. BEGIN/COMMIT/ROLLBACK)
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @var boolean
|
|
|
|
*/
|
|
|
|
var $transactional = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* belongsTo association
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $belongsTo = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hasOne association
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $hasOne = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hasMany association
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $hasMany = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hasAndBelongsToMany association
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $hasAndBelongsToMany = array();
|
|
|
|
|
2006-01-18 06:46:28 +00:00
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Depth of recursive association
|
2006-01-18 06:46:28 +00:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
var $recursive = 1;
|
2006-01-12 02:10:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default association keys
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $__associationKeys = array('belongsTo' => array('className', 'conditions', 'order', 'foreignKey', 'counterCache'),
|
|
|
|
'hasOne' => array('className', 'conditions', 'order', 'foreignKey', 'dependent'),
|
|
|
|
'hasMany' => array('className', 'conditions', 'order', 'foreignKey', 'fields', 'dependent', 'exclusive', 'finder_query', 'counter_query'),
|
|
|
|
'hasAndBelongsToMany' => array('className', 'joinTable', 'fields', 'foreignKey', 'associationForeignKey', 'conditions', 'order', 'uniq', 'finderQuery', 'deleteQuery', 'insertQuery')
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Holds provided/generated association key names and other data for all associations
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $__associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The last inserted ID of the data that this model created
|
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
* @access private
|
|
|
|
*/
|
|
|
|
var $__insertID = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of records returned by the last query
|
|
|
|
*
|
|
|
|
* @access private
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
var $__numRows = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of records affected by the last query
|
|
|
|
*
|
|
|
|
* @access private
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
var $__affectedRows = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor. Binds the Model's database table to the object.
|
|
|
|
*
|
|
|
|
* @param integer $id
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param string $table Name of database table to use.
|
|
|
|
* @param DataSource $ds DataSource connection object.
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function __construct ($id=false, $table=null, $ds=null)
|
|
|
|
{
|
|
|
|
parent::__construct();
|
|
|
|
|
|
|
|
if ($this->name === null)
|
|
|
|
{
|
|
|
|
$this->name = get_class($this);
|
|
|
|
}
|
|
|
|
if ($this->primaryKey === null)
|
|
|
|
{
|
|
|
|
$this->primaryKey = 'id';
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->currentModel = Inflector::underscore($this->name);
|
|
|
|
|
|
|
|
ClassRegistry::addObject($this->currentModel, $this);
|
|
|
|
$this->id = $id;
|
|
|
|
|
|
|
|
if($this->useTable !== false)
|
|
|
|
{
|
2006-01-22 05:12:28 +00:00
|
|
|
$this->setDataSource($ds);
|
|
|
|
if ($table)
|
|
|
|
{
|
|
|
|
$tableName = $table;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ($this->useTable)
|
|
|
|
{
|
|
|
|
$tableName = $this->useTable;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$tableName = Inflector::tableize($this->name);
|
|
|
|
}
|
|
|
|
}
|
2006-01-12 02:10:47 +00:00
|
|
|
|
2006-01-20 18:43:05 +00:00
|
|
|
if (in_array('settableprefix', get_class_methods($this)))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
|
|
|
$this->setTablePrefix();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->tablePrefix)
|
|
|
|
{
|
|
|
|
$this->setSource($this->tablePrefix.$tableName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->setSource($tableName);
|
|
|
|
}
|
2006-01-22 05:12:28 +00:00
|
|
|
|
2006-01-12 02:10:47 +00:00
|
|
|
$this->__createLinks();
|
|
|
|
|
2006-01-22 05:12:28 +00:00
|
|
|
if ($this->displayField == null)
|
|
|
|
{
|
|
|
|
if ($this->hasField('title'))
|
|
|
|
{
|
|
|
|
$this->displayField = 'title';
|
|
|
|
}
|
|
|
|
if ($this->hasField('name'))
|
|
|
|
{
|
|
|
|
$this->displayField = 'name';
|
|
|
|
}
|
|
|
|
if ($this->displayField == null)
|
|
|
|
{
|
|
|
|
$this->displayField = $this->primaryKey;
|
|
|
|
}
|
|
|
|
}
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles custom method calls, like findBy<field> for DB models,
|
|
|
|
* and custom RPC calls for remote data sources.
|
|
|
|
*
|
2006-01-17 20:56:45 +00:00
|
|
|
* @param unknown_type $method
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param array $params
|
2006-01-17 20:56:45 +00:00
|
|
|
* @return unknown
|
2006-01-12 02:10:47 +00:00
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
function __call($method, $params)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
return $this->db->query($method, $params, $this);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private helper method to create a set of associations.
|
|
|
|
*
|
|
|
|
* @access private
|
|
|
|
*/
|
|
|
|
function __createLinks()
|
|
|
|
{
|
|
|
|
// Convert all string-based associations to array based
|
|
|
|
foreach($this->__associations as $type)
|
|
|
|
{
|
|
|
|
if(!is_array($this->{$type}))
|
|
|
|
{
|
|
|
|
$this->{$type} = explode(',', $this->{$type});
|
|
|
|
foreach ($this->{$type} as $i => $className)
|
|
|
|
{
|
|
|
|
$className = trim($className);
|
|
|
|
unset($this->{$type}[$i]);
|
|
|
|
$this->{$type}[$className] = array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->{$type} as $assoc => $value)
|
|
|
|
{
|
|
|
|
$className = $assoc;
|
|
|
|
if (isset($value['className']) && $value['className'] !== null)
|
|
|
|
{
|
|
|
|
$className = $value['className'];
|
|
|
|
}
|
|
|
|
$this->__constructLinkedModel($assoc, $className, $type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach($this->__associations as $type)
|
|
|
|
{
|
|
|
|
foreach ($this->{$type} as $assoc => $value)
|
|
|
|
{
|
|
|
|
$this->__generateAssociation($type, $assoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private helper method to create associated models of given class.
|
|
|
|
* @param string $assoc
|
|
|
|
* @param string $className Class name
|
|
|
|
* @param string $type Type of assocation
|
2006-02-01 13:26:23 +00:00
|
|
|
* @todo Is the third parameter in use at the moment? It is not referred to in the method OJ, 30. jan 2006
|
2006-01-12 02:10:47 +00:00
|
|
|
* @access private
|
|
|
|
*/
|
|
|
|
function __constructLinkedModel($assoc, $className, $type)
|
|
|
|
{
|
|
|
|
$colKey = Inflector::underscore($className);
|
|
|
|
if(ClassRegistry::isKeySet($colKey))
|
|
|
|
{
|
|
|
|
$this->{$className} = ClassRegistry::getObject($colKey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->{$className} = new $className();
|
|
|
|
}
|
|
|
|
|
2006-01-12 03:56:59 +00:00
|
|
|
$this->alias[$assoc] = $this->{$className}->table;
|
|
|
|
$this->tableToModel[$this->{$className}->table] = $className;
|
|
|
|
$this->modelToTable[$className] = $this->{$className}->table;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Build array-based association from string.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param string $type "Belongs", "One", "Many", "ManyTo"
|
|
|
|
* @param string $assoc
|
2006-02-01 13:26:23 +00:00
|
|
|
* @todo Is the second parameter in use at the moment? It is not referred to in the method OJ, 30. jan 2006
|
2006-01-17 20:56:45 +00:00
|
|
|
* @access private
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function __generateAssociation ($type, $assoc)
|
|
|
|
{
|
|
|
|
foreach ($this->{$type} as $assocKey => $assocData)
|
|
|
|
{
|
|
|
|
$class = $assocKey;
|
|
|
|
if (isset($this->{$type}[$assocKey]['className']) && $this->{$type}[$assocKey]['className'] !== null)
|
|
|
|
{
|
|
|
|
$class = $this->{$type}[$assocKey]['className'];
|
|
|
|
}
|
|
|
|
foreach($this->__associationKeys[$type] as $key)
|
|
|
|
{
|
|
|
|
if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] == null)
|
|
|
|
{
|
|
|
|
$data = '';
|
|
|
|
switch($key)
|
|
|
|
{
|
|
|
|
case 'fields':
|
2006-01-15 12:18:57 +00:00
|
|
|
$data = '';
|
2006-01-12 02:10:47 +00:00
|
|
|
break;
|
|
|
|
case 'foreignKey':
|
2006-01-12 03:56:59 +00:00
|
|
|
$data = Inflector::singularize($this->table).'_id';
|
2006-01-12 02:10:47 +00:00
|
|
|
if ($type == 'belongsTo')
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$data = Inflector::singularize($this->{$class}->table).'_id';
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'associationForeignKey':
|
2006-01-12 03:56:59 +00:00
|
|
|
$data = Inflector::singularize($this->{$class}->table) . '_id';
|
2006-01-12 02:10:47 +00:00
|
|
|
break;
|
|
|
|
case 'joinTable':
|
2006-01-12 03:56:59 +00:00
|
|
|
$tables = array($this->table, $this->{$class}->table);
|
2006-01-12 02:10:47 +00:00
|
|
|
sort($tables);
|
|
|
|
$data = $tables[0].'_'.$tables[1];
|
|
|
|
break;
|
|
|
|
case 'className':
|
|
|
|
$data = $class;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$this->{$type}[$assocKey][$key] = $data;
|
|
|
|
}
|
2006-01-22 05:12:28 +00:00
|
|
|
if($key == 'foreignKey' && !isset($this->keyToTable[$this->{$type}[$assocKey][$key]]))
|
|
|
|
{
|
|
|
|
$this->keyToTable[$this->{$type}[$assocKey][$key]][0] = $this->{$class}->table;
|
|
|
|
$this->keyToTable[$this->{$type}[$assocKey][$key]][1] = $this->{$class}->name;
|
|
|
|
}
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a custom table for your controller class. Used by your controller to select a database table.
|
|
|
|
*
|
|
|
|
* @param string $tableName Name of the custom table
|
|
|
|
*/
|
|
|
|
function setSource($tableName)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
if($this->db->isInterfaceSupported('listSources'))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
if (!in_array(strtolower($tableName), $this->db->listSources()))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
return $this->cakeError('missingTable',array(array('className' => $this->name,
|
|
|
|
'table' => $tableName)));
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$this->table = $tableName;
|
|
|
|
$this->tableToModel[$this->table] = $this->name;
|
2006-01-12 02:10:47 +00:00
|
|
|
$this->loadInfo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$this->table = $tableName;
|
|
|
|
$this->tableToModel[$this->table] = $this->name;
|
2006-01-12 02:10:47 +00:00
|
|
|
$this->loadInfo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function does two things: 1) it scans the array $one for the primary key,
|
|
|
|
* and if that's found, it sets the current id to the value of $one[id].
|
|
|
|
* For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object.
|
|
|
|
* 2) Returns an array with all of $one's keys and values.
|
|
|
|
* (Alternative indata: two strings, which are mangled to
|
|
|
|
* a one-item, two-dimensional array using $one for a key and $two as its value.)
|
|
|
|
*
|
|
|
|
* @param mixed $one Array or string of data
|
|
|
|
* @param string $two Value string for the alternative indata method
|
|
|
|
* @return unknown
|
|
|
|
*/
|
|
|
|
function set ($one, $two=null)
|
|
|
|
{
|
|
|
|
$this->validationErrors = null;
|
|
|
|
if (is_array($one))
|
|
|
|
{
|
|
|
|
$data = $one;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$data = array($one=>$two);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($data as $n => $v)
|
|
|
|
{
|
|
|
|
foreach ($v as $x => $y)
|
|
|
|
{
|
|
|
|
if($x == $this->primaryKey)
|
|
|
|
{
|
|
|
|
$this->id = $y;
|
|
|
|
}
|
|
|
|
$this->data[$n][$x] = $y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array of table metadata (column names and types) from the database.
|
|
|
|
*
|
|
|
|
* @return array Array of table metadata
|
|
|
|
*/
|
|
|
|
function loadInfo ()
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
if (!is_object($this->_tableInfo) && $this->db->isInterfaceSupported('describe'))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$this->_tableInfo = new NeatArray($this->db->describe($this));
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
return $this->_tableInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns true if this Model has given field in its database table.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param string $name Name of field to look for
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
function hasField ($name)
|
|
|
|
{
|
|
|
|
if (empty($this->_tableInfo))
|
|
|
|
{
|
|
|
|
$this->loadInfo();
|
|
|
|
}
|
|
|
|
if($this->_tableInfo != null)
|
|
|
|
{
|
|
|
|
return $this->_tableInfo->findIn('name', $name);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Initializes the model for writing a new record.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @return boolean True
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function create ()
|
|
|
|
{
|
|
|
|
$this->id = false;
|
|
|
|
unset($this->data);
|
|
|
|
$this->data = array();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deprecated
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function setId ($id)
|
|
|
|
{
|
|
|
|
$this->id = $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Deprecated. Use query() instead.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
function findBySql ($sql)
|
|
|
|
{
|
|
|
|
return $this->query($sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of fields from the database
|
|
|
|
*
|
|
|
|
* @param mixed $id The ID of the record to read
|
|
|
|
* @param mixed $fields String of single fieldname, or an array of fieldnames.
|
|
|
|
* @return array Array of database fields
|
|
|
|
*/
|
2006-01-17 05:13:38 +00:00
|
|
|
function read ($fields = null, $id = null)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
|
|
|
$this->validationErrors = null;
|
|
|
|
if ($id != null)
|
|
|
|
{
|
|
|
|
$this->id = $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
$id = $this->id;
|
|
|
|
if (is_array($this->id))
|
|
|
|
{
|
|
|
|
$id = $this->id[0];
|
|
|
|
}
|
|
|
|
|
2006-02-01 13:26:23 +00:00
|
|
|
if ($this->id !== null && $this->id !== false)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$field = $this->db->name($this->name).'.'.$this->db->name($this->primaryKey);
|
|
|
|
return $this->find($field . ' = ' . $this->db->value($id), $fields);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns contents of a field in a query matching given conditions.
|
|
|
|
*
|
|
|
|
* @param string $name Name of field to get
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param array $conditions SQL conditions (defaults to NULL)
|
|
|
|
* @param string $order SQL ORDER BY fragment
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return field contents
|
|
|
|
*/
|
|
|
|
function field ($name, $conditions = null, $order = null)
|
|
|
|
{
|
|
|
|
if (isset($this->data[$this->name][$name]))
|
|
|
|
{
|
|
|
|
return $this->data[$this->name][$name];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($conditions)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$conditions = $this->db->conditions($conditions);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($data = $this->find($conditions, $name, $order))
|
|
|
|
{
|
|
|
|
if (isset($data[$this->name][$name]))
|
|
|
|
{
|
|
|
|
return $data[$this->name][$name];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves a single field to the database.
|
|
|
|
*
|
|
|
|
* @param string $name Name of the table field
|
|
|
|
* @param mixed $value Value of the field
|
|
|
|
* @param boolean $validate Whether or not this model should validate before saving (defaults to false)
|
|
|
|
* @return boolean True on success save
|
|
|
|
*/
|
|
|
|
function saveField($name, $value, $validate = false)
|
|
|
|
{
|
|
|
|
return $this->save(array($this->name => array($name => $value)), $validate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves model data to the database.
|
2006-02-01 13:26:23 +00:00
|
|
|
* By default, validation occurs before save.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param array $data Data to save.
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param boolean $validate If set, validation will be done before the save
|
2006-01-12 02:10:47 +00:00
|
|
|
* @param array $fields
|
|
|
|
* @return boolean success
|
2006-01-12 16:55:46 +00:00
|
|
|
* @todo Implement $fields param as a whitelist of allowable fields
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function save ($data = null, $validate = true, $fields = null)
|
|
|
|
{
|
|
|
|
if ($data)
|
|
|
|
{
|
2006-02-01 13:26:23 +00:00
|
|
|
if ($this->countdim($data) == 1)
|
|
|
|
{
|
|
|
|
$this->set(array($this->name => $data));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->set($data);
|
|
|
|
}
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
2006-01-20 07:46:14 +00:00
|
|
|
if($this->beforeSave())
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-20 07:46:14 +00:00
|
|
|
if ($validate && !$this->validates())
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-20 07:46:14 +00:00
|
|
|
return false;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
2006-01-20 07:46:14 +00:00
|
|
|
$fields = $values = array();
|
|
|
|
$count = 0;
|
|
|
|
|
|
|
|
if(count($this->data) > 1)
|
|
|
|
{
|
|
|
|
$weHaveMulti = true;
|
|
|
|
$joined = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$weHaveMulti = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->data as $n => $v)
|
|
|
|
{
|
|
|
|
if(isset($weHaveMulti) && $count > 0 && count($this->hasAndBelongsToMany) > 0)
|
|
|
|
{
|
|
|
|
$joined[] = $v;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
foreach ($v as $x => $y)
|
|
|
|
{
|
|
|
|
if ($this->hasField($x))
|
|
|
|
{
|
|
|
|
$fields[] = $x;
|
|
|
|
$values[] = $y;
|
|
|
|
|
|
|
|
if($x == $this->primaryKey && !is_numeric($y))
|
|
|
|
{
|
|
|
|
$newID = $y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($this->id) && $this->hasField('created') && !in_array('created', $fields))
|
|
|
|
{
|
|
|
|
$fields[] = 'created';
|
|
|
|
$values[] = date('Y-m-d H:i:s');
|
|
|
|
}
|
|
|
|
if ($this->hasField('modified') && !in_array('modified', $fields))
|
|
|
|
{
|
|
|
|
$fields[] = 'modified';
|
|
|
|
$values[] = date('Y-m-d H:i:s');
|
|
|
|
}
|
|
|
|
if ($this->hasField('updated') && !in_array('updated', $fields))
|
|
|
|
{
|
|
|
|
$fields[] = 'updated';
|
|
|
|
$values[] = date('Y-m-d H:i:s');
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!$this->exists())
|
|
|
|
{
|
|
|
|
$this->id = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(count($fields))
|
|
|
|
{
|
|
|
|
if(!empty($this->id))
|
|
|
|
{
|
|
|
|
if ($this->db->update($this, $fields, $values))
|
|
|
|
{
|
|
|
|
if(!empty($joined))
|
|
|
|
{
|
|
|
|
$this->__saveMulti($joined, $this->id);
|
|
|
|
}
|
|
|
|
$this->afterSave();
|
|
|
|
$this->data = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return $this->hasAny($this->escapeField($this->primaryKey).' = '.$this->db->value($this->id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if($this->db->create($this, $fields, $values))
|
|
|
|
{
|
|
|
|
$this->__insertID = $this->db->lastInsertId($this->table, $this->primaryKey);
|
|
|
|
$this->id = $this->__insertID;
|
|
|
|
|
|
|
|
if(!empty($joined))
|
|
|
|
{
|
|
|
|
if(!$this->id > 0 && isset($newID))
|
|
|
|
{
|
|
|
|
$this->id = $newID;
|
|
|
|
}
|
|
|
|
$this->__saveMulti($joined, $this->id);
|
|
|
|
}
|
|
|
|
$this->afterSave();
|
|
|
|
$this->data = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves model hasAndBelongsToMany data to the database.
|
|
|
|
*
|
|
|
|
* @param array $joined Data to save.
|
|
|
|
* @param string $id
|
|
|
|
* @return
|
|
|
|
* @access private
|
|
|
|
*/
|
|
|
|
function __saveMulti ($joined, $id)
|
|
|
|
{
|
|
|
|
foreach ($joined as $x => $y)
|
|
|
|
{
|
|
|
|
foreach ($y as $assoc => $value)
|
|
|
|
{
|
|
|
|
$joinTable[] = $this->hasAndBelongsToMany[$assoc]['joinTable'];
|
|
|
|
$mainKey = $this->hasAndBelongsToMany[$assoc]['foreignKey'];
|
|
|
|
$keys[] = $mainKey;
|
|
|
|
$keys[] = $this->hasAndBelongsToMany[$assoc]['associationForeignKey'];
|
|
|
|
$fields[] = join(',', $keys);
|
|
|
|
unset($keys);
|
|
|
|
|
|
|
|
foreach ($value as $update)
|
|
|
|
{
|
|
|
|
if(!empty($update))
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$values[] = $this->db->value($id);
|
|
|
|
$values[] = $this->db->value($update);
|
2006-01-12 02:10:47 +00:00
|
|
|
$values = join(',', $values);
|
2006-01-12 16:55:46 +00:00
|
|
|
$newValues[] = "({$values})";
|
2006-01-12 02:10:47 +00:00
|
|
|
unset($values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!empty($newValues))
|
|
|
|
{
|
|
|
|
$newValue[] = join(',', $newValues);
|
|
|
|
unset($newValues);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$total = count($joinTable);
|
|
|
|
for ($count = 0; $count < $total; $count++)
|
|
|
|
{
|
2006-01-12 16:55:46 +00:00
|
|
|
$this->db->execute("DELETE FROM {$joinTable[$count]} WHERE $mainKey = '{$id}'");
|
2006-01-12 02:10:47 +00:00
|
|
|
if(!empty($newValue[$count]))
|
|
|
|
{
|
2006-01-12 16:55:46 +00:00
|
|
|
$this->db->execute("INSERT INTO {$joinTable[$count]} ({$fields[$count]}) VALUES {$newValue[$count]}");
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Synonym for del().
|
|
|
|
*
|
|
|
|
* @param mixed $id
|
|
|
|
* @see function del
|
|
|
|
* @return boolean True on success
|
|
|
|
*/
|
|
|
|
function remove ($id=null)
|
|
|
|
{
|
|
|
|
return $this->del($id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes record for given id. If no id is given, the current id is used. Returns true on success.
|
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param mixed $id Id of record to delete
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return boolean True on success
|
|
|
|
*/
|
|
|
|
function del ($id = null)
|
|
|
|
{
|
|
|
|
if ($id)
|
|
|
|
{
|
|
|
|
$this->id = $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($this->beforeDelete())
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
if ($this->id && $this->db->delete($this))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
|
|
|
$this->afterDelete();
|
|
|
|
$this->id = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if a record with set id exists.
|
|
|
|
*
|
|
|
|
* @return boolean True if such a record exists
|
|
|
|
*/
|
|
|
|
function exists ()
|
|
|
|
{
|
|
|
|
if ($this->id)
|
|
|
|
{
|
|
|
|
$id = $this->id;
|
2006-01-12 22:06:11 +00:00
|
|
|
if (is_array($id))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
|
|
|
$id = $id[0];
|
|
|
|
}
|
2006-01-12 22:06:11 +00:00
|
|
|
return $this->db->hasAny($this->table,$this->primaryKey.'='.$this->db->value($id));
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if a record that meets given conditions exists
|
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param array $conditions SQL conditions array
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return boolean True if such a record exists
|
|
|
|
*/
|
|
|
|
function hasAny ($conditions = null)
|
|
|
|
{
|
2006-01-20 09:37:15 +00:00
|
|
|
return ($this->findCount($conditions) != false);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a single row as a resultset array.
|
2006-02-01 13:26:23 +00:00
|
|
|
* By using the $recursive parameter, the call can access further "levels of association" than
|
|
|
|
* the ones this model is directly associated to.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param array $conditions SQL conditions array
|
2006-01-12 02:10:47 +00:00
|
|
|
* @param mixed $fields Either a single string of a field name, or an array of field names
|
|
|
|
* @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param int $recursive The number of levels deep to fetch associated records
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return array Array of records
|
|
|
|
*/
|
2006-01-18 06:46:28 +00:00
|
|
|
function find ($conditions = null, $fields = null, $order = null, $recursive = null)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-17 05:13:38 +00:00
|
|
|
$data = $this->findAll($conditions, $fields, $order, 1, null, $recursive);
|
2006-01-12 02:10:47 +00:00
|
|
|
if (empty($data[0]))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return $data[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a resultset array with specified fields from database matching given conditions.
|
2006-02-01 13:26:23 +00:00
|
|
|
* By using the $recursive parameter, the call can access further "levels of association" than
|
|
|
|
* the ones this model is directly associated to.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param mixed $conditions SQL conditions as a string or as an array('field'=>'value',...)
|
|
|
|
* @param mixed $fields Either a single string of a field name, or an array of field names
|
|
|
|
* @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param int $limit SQL LIMIT clause, for calculating items per page.
|
|
|
|
* @param int $page Page number, for accessing paged data
|
|
|
|
* @param int $recursive The number of levels deep to fetch associated records
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return array Array of records
|
|
|
|
*/
|
2006-01-18 06:46:28 +00:00
|
|
|
function findAll ($conditions = null, $fields = null, $order = null, $limit = 50, $page = 1, $recursive = null)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
|
|
|
if (!$this->beforeFind($conditions))
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->id = $this->getID();
|
|
|
|
$offset = 0;
|
|
|
|
if ($page > 1)
|
|
|
|
{
|
|
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
}
|
|
|
|
$limit_str = '';
|
|
|
|
if ($limit)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$limit_str = $this->db->limit($limit, $offset);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$queryData = array(
|
|
|
|
'conditions' => $conditions,
|
|
|
|
'fields' => $fields,
|
|
|
|
'joins' => array(),
|
|
|
|
'limit' => $limit_str,
|
|
|
|
'order' => $order
|
|
|
|
);
|
2006-01-12 03:56:59 +00:00
|
|
|
return $this->afterFind($this->db->read($this, $queryData, $recursive));
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Runs a direct query against the bound DataSource, and returns the result.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param string $data Query data
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
function execute ($data)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$data = $this->db->fetchAll($data);
|
2006-01-12 02:10:47 +00:00
|
|
|
foreach ($data as $key => $value)
|
|
|
|
{
|
|
|
|
foreach ($this->tableToModel as $key1 => $value1)
|
|
|
|
{
|
|
|
|
if (isset($data[$key][$key1]))
|
|
|
|
{
|
|
|
|
$newData[$key][$value1] = $data[$key][$key1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!empty($newData))
|
|
|
|
{
|
|
|
|
return $newData;
|
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns number of rows matching given SQL condition.
|
|
|
|
*
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param array $conditions SQL conditions array for findAll
|
2006-01-17 20:56:45 +00:00
|
|
|
* @param int $recursize The number of levels deep to fetch associated records
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return int Number of matching rows
|
2006-02-01 13:26:23 +00:00
|
|
|
* @see Model::findAll
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
2006-01-17 05:13:38 +00:00
|
|
|
function findCount ($conditions = null, $recursive = 0)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-17 05:13:38 +00:00
|
|
|
list($data) = $this->findAll($conditions, 'COUNT(*) AS count', null, null, 1, $recursive);
|
2006-01-12 02:10:47 +00:00
|
|
|
if (isset($data[0]['count']))
|
|
|
|
{
|
|
|
|
return $data[0]['count'];
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Special findAll variation for tables joined to themselves.
|
2006-02-01 13:26:23 +00:00
|
|
|
* The table needs the fields id and parent_id to work.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param array $conditions Conditions for the findAll() call
|
|
|
|
* @param array $fields Fields for the findAll() call
|
|
|
|
* @param string $sort SQL ORDER BY statement
|
2006-02-01 13:26:23 +00:00
|
|
|
* @return array
|
2006-01-17 20:56:45 +00:00
|
|
|
* @todo Perhaps create a Component with this logic
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function findAllThreaded ($conditions=null, $fields=null, $sort=null)
|
|
|
|
{
|
|
|
|
return $this->__doThread(Model::findAll($conditions, $fields, $sort), null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private, recursive helper method for findAllThreaded.
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param string $root NULL or id for root node of operation
|
|
|
|
* @return array
|
|
|
|
* @access private
|
2006-02-01 13:26:23 +00:00
|
|
|
* @see findAllThreaded
|
2006-01-12 02:10:47 +00:00
|
|
|
*/
|
|
|
|
function __doThread ($data, $root)
|
|
|
|
{
|
2006-02-01 13:26:23 +00:00
|
|
|
$out = array();
|
|
|
|
$sizeOf = sizeof($data);
|
|
|
|
for ($ii=0; $ii < $sizeOf; $ii++)
|
|
|
|
{
|
|
|
|
if ($data[$ii][$this->name]['parent_id'] == $root)
|
|
|
|
{
|
|
|
|
$tmp = $data[$ii];
|
|
|
|
if (isset($data[$ii][$this->name][$this->primaryKey]))
|
|
|
|
{
|
|
|
|
$tmp['children'] = $this->__doThread($data, $data[$ii][$this->name][$this->primaryKey]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$tmp['children'] = null;
|
|
|
|
}
|
|
|
|
$out[] = $tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $out;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array with keys "prev" and "next" that holds the id's of neighbouring data,
|
|
|
|
* which is useful when creating paged lists.
|
|
|
|
*
|
|
|
|
* @param string $conditions SQL conditions for matching rows
|
2006-02-01 13:26:23 +00:00
|
|
|
* @param string $field Field name (parameter for findAll)
|
2006-01-12 02:10:47 +00:00
|
|
|
* @param unknown_type $value
|
|
|
|
* @return array Array with keys "prev" and "next" that holds the id's
|
|
|
|
*/
|
2006-01-20 09:37:15 +00:00
|
|
|
function findNeighbours ($conditions = null, $field, $value)
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-20 09:37:15 +00:00
|
|
|
if(!is_null($conditions))
|
|
|
|
{
|
|
|
|
$conditions = $conditions.' AND ';
|
|
|
|
}
|
|
|
|
@list($prev) = Model::findAll($conditions. $field . ' < ' . $this->db->value($value), $field, $field . ' DESC', 1);
|
|
|
|
@list($next) = Model::findAll($conditions. $field . ' > ' . $this->db->value($value), $field, $field . ' ASC', 1);
|
2006-01-12 02:10:47 +00:00
|
|
|
|
2006-01-12 16:55:46 +00:00
|
|
|
if (!isset($prev))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 16:55:46 +00:00
|
|
|
$prev = null;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
2006-01-12 16:55:46 +00:00
|
|
|
if (!isset($next))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 16:55:46 +00:00
|
|
|
$next = null;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
return array('prev' => $prev, 'next' => $next);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns a resultset for given SQL statement. Generic SQL queries should be made with this method.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param string $sql SQL statement
|
|
|
|
* @return array Resultset
|
|
|
|
*/
|
|
|
|
function query ()
|
|
|
|
{
|
|
|
|
$params = func_get_args();
|
2006-01-12 03:56:59 +00:00
|
|
|
return call_user_func_array(array(&$this->db, 'query'), $params);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns true if all fields pass validation, otherwise false.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param array $data POST data
|
|
|
|
* @return boolean True if there are no errors
|
|
|
|
*/
|
|
|
|
function validates ($data = null)
|
|
|
|
{
|
|
|
|
if ($data == null)
|
|
|
|
{
|
|
|
|
$data = $this->data;
|
|
|
|
}
|
|
|
|
$errors = $this->invalidFields($data);
|
|
|
|
return count($errors) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array of invalid fields.
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @return array Array of invalid fields
|
|
|
|
*/
|
|
|
|
function invalidFields ($data=null)
|
|
|
|
{
|
|
|
|
if (!isset($this->validate) || is_array($this->validationErrors))
|
|
|
|
{
|
|
|
|
if (!isset($this->validate))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return $this->validationErrors;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($data == null)
|
|
|
|
{
|
|
|
|
if (isset($this->data))
|
|
|
|
{
|
|
|
|
$data = $this->data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$data = array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$errors = array();
|
|
|
|
foreach ($data as $table => $field)
|
|
|
|
{
|
|
|
|
foreach ($this->validate as $field_name => $validator)
|
|
|
|
{
|
|
|
|
if (isset($data[$table][$field_name]) && !preg_match($validator, $data[$table][$field_name]))
|
|
|
|
{
|
|
|
|
$errors[$field_name] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->validationErrors = $errors;
|
|
|
|
return $errors;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns true if given field name is a foreign key in this Model.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param string $field Returns true if the input string ends in "_id"
|
|
|
|
* @return True if the field is a foreign key listed in the belongsTo array.
|
|
|
|
*/
|
|
|
|
function isForeignKey($field)
|
|
|
|
{
|
|
|
|
$foreignKeys = array();
|
|
|
|
if(count($this->belongsTo))
|
|
|
|
{
|
|
|
|
foreach ($this->belongsTo as $assoc => $data)
|
|
|
|
{
|
|
|
|
$foreignKeys[] = $data['foreignKey'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (bool)(in_array($field, $foreignKeys));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the display field for this model
|
|
|
|
*
|
|
|
|
* @return string The name of the display field for this Model (i.e. 'name', 'title').
|
|
|
|
*/
|
|
|
|
function getDisplayField()
|
|
|
|
{
|
|
|
|
return $this->displayField;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns a resultset array with specified fields from database matching given conditions. Method can be used to generate option lists for SELECT elements.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param mixed $conditions SQL conditions as a string or as an array('field'=>'value',...)
|
|
|
|
* @param mixed $fields Either a single string of a field name, or an array of field names
|
|
|
|
* @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
|
|
|
|
* @param int $limit SQL LIMIT clause, for calculating items per page
|
|
|
|
* @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
|
|
|
|
* @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
|
|
|
|
* @return array An associative array of records, where the id is the key, and the display field is the value
|
|
|
|
*/
|
|
|
|
function generateList ($conditions = null, $order = null, $limit = null, $keyPath = null, $valuePath = null)
|
|
|
|
{
|
|
|
|
$fields = array($this->primaryKey, $this->displayField);
|
|
|
|
$result = $this->findAll($conditions, $fields, $order, $limit, 1, 0);
|
|
|
|
|
|
|
|
if ($keyPath == null)
|
|
|
|
{
|
|
|
|
$keyPath = '{n}.'.$this->name.'.'.$this->primaryKey;
|
|
|
|
}
|
|
|
|
if ($valuePath == null)
|
|
|
|
{
|
|
|
|
$valuePath = '{n}.'.$this->name.'.'.$this->displayField;
|
|
|
|
}
|
2006-01-12 03:56:59 +00:00
|
|
|
$keys = $this->db->getFieldValue($result, $keyPath);
|
|
|
|
$vals = $this->db->getFieldValue($result, $valuePath);
|
2006-01-12 02:10:47 +00:00
|
|
|
return array_combine($keys, $vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Escapes the field name and prepends the model name. Escaping will be done according to the current database driver's rules.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
2006-01-17 20:56:45 +00:00
|
|
|
* @param unknown_type $field
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
|
|
|
|
*/
|
|
|
|
function escapeField($field)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
return $this->db->name($this->name).'.'.$this->db->name($field);
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the current record's ID
|
|
|
|
*
|
2006-01-17 20:56:45 +00:00
|
|
|
* @param unknown_type $list
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return mixed The ID of the current record
|
|
|
|
*/
|
|
|
|
function getID($list = 0)
|
|
|
|
{
|
|
|
|
if (!is_array($this->id))
|
|
|
|
{
|
|
|
|
return $this->id;
|
|
|
|
}
|
|
|
|
if (count($this->id) == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isset($this->id[$list]))
|
|
|
|
{
|
|
|
|
return $this->id[$list];
|
|
|
|
}
|
|
|
|
foreach ($this->id as $id)
|
|
|
|
{
|
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns the ID of the last record this Model inserted
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
function getLastInsertID ()
|
|
|
|
{
|
|
|
|
return $this->getInsertID();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns the ID of the last record this Model inserted
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
function getInsertID ()
|
|
|
|
{
|
|
|
|
return $this->__insertID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns the number of rows returned from the last query
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function getNumRows ()
|
|
|
|
{
|
2006-01-15 12:18:57 +00:00
|
|
|
//return $this->__numRows;
|
|
|
|
return $this->db->lastNumRows();
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* Returns the number of rows affected by the last query
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function getAffectedRows ()
|
|
|
|
{
|
2006-01-15 12:18:57 +00:00
|
|
|
//return $this->__affectedRows;
|
|
|
|
return $this->db->lastAffected();
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the DataSource to which this model is bound
|
|
|
|
*
|
|
|
|
* @param string $dataSource The name of the DataSource, as defined in Connections.php
|
|
|
|
* @return boolean True on success
|
|
|
|
*/
|
|
|
|
function setDataSource($dataSource = null)
|
|
|
|
{
|
|
|
|
if ($dataSource == null)
|
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
$dataSource = $this->useDbConfig;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
2006-01-12 03:56:59 +00:00
|
|
|
$this->db =& ConnectionManager::getDataSource($dataSource);
|
|
|
|
if(empty($this->db) || $this->db == null || !is_object($this->db))
|
2006-01-12 02:10:47 +00:00
|
|
|
{
|
2006-01-12 03:56:59 +00:00
|
|
|
return $this->cakeError('missingConnection',array(array('className' => $this->name)));
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Before find callback
|
|
|
|
*
|
2006-01-17 20:56:45 +00:00
|
|
|
* @param unknown_type $conditions
|
2006-01-12 02:10:47 +00:00
|
|
|
* @return boolean True if the operation should continue, false if it should abort
|
|
|
|
*/
|
|
|
|
function beforeFind($conditions)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-02-01 13:26:23 +00:00
|
|
|
* After find callback. Can be used to modify any results returned by find and findAll.
|
2006-01-12 02:10:47 +00:00
|
|
|
*
|
|
|
|
* @param mixed $results The results of the find operation
|
|
|
|
* @return mixed Result of the find operation
|
|
|
|
*/
|
|
|
|
function afterFind($results)
|
|
|
|
{
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Before save callback
|
|
|
|
*
|
|
|
|
* @return boolean True if the operation should continue, false if it should abort
|
|
|
|
*/
|
|
|
|
function beforeSave()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* After save callback
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
function afterSave()
|
|
|
|
{
|
2006-01-20 07:46:14 +00:00
|
|
|
return true;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Before delete callback
|
|
|
|
*
|
|
|
|
* @return boolean True if the operation should continue, false if it should abort
|
|
|
|
*/
|
|
|
|
function beforeDelete()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* After save callback
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
function afterDelete()
|
|
|
|
{
|
2006-01-20 07:46:14 +00:00
|
|
|
return true;
|
2006-01-12 02:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
?>
|