PageRenderTime 519ms CodeModel.GetById 193ms app.highlight 119ms RepoModel.GetById 88ms app.codeStats 3ms

/application/libraries/datamapper.php

https://bitbucket.org/Spelljack/datamapper
PHP | 6822 lines | 3355 code | 809 blank | 2658 comment | 588 complexity | 0cbdfb3c710e49ebaf5b671910c42984 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3/**
   4 * Data Mapper ORM Class
   5 *
   6 * Transforms database tables into objects.
   7 *
   8 * @license 	MIT License
   9 * @package		DataMapper ORM
  10 * @category	DataMapper ORM
  11 * @author  	Harro Verton, James Wardlaw
  12 * @author	Gökhan Öztürk (Spelljack)
  13 * @author  	Phil DeJarnett (up to v1.7.1)
  14 * @author  	Simon Stenhouse (up to v1.6.0)
  15 * @link		http://datamapper.wanwizard.eu/
  16 * @version 	1.8.1-dev
  17 */
  18
  19/**
  20 * Key for storing pre-converted classnames
  21 */
  22define('DMZ_CLASSNAMES_KEY', '_dmz_classnames');
  23
  24/**
  25 * DMZ version
  26 */
  27define('DMZ_VERSION', '1.8.1');
  28
  29/**
  30 * Data Mapper Class
  31 *
  32 * Transforms database tables into objects.
  33 *
  34 * @package		DataMapper ORM
  35 *
  36 * Properties (for code completion)
  37 * @property CI_DB_driver $db The CodeIgniter Database Library
  38 * @property CI_Loader $load The CodeIgnter Loader Library
  39 * @property CI_Language $lang The CodeIgniter Language Library
  40 * @property CI_Config $config The CodeIgniter Config Library
  41 * @property CI_Form_validation $form_validation The CodeIgniter Form Validation Library
  42 *
  43 *
  44 * Define some of the magic methods:
  45 *
  46 * Get By:
  47 * @method DataMapper get_by_id() get_by_id(int $value) Looks up an item by its ID.
  48 * @method DataMapper get_by_FIELD() get_by_FIELD(mixed $value) Looks up an item by a specific FIELD. Ex: get_by_name($user_name);
  49 * @method DataMapper get_by_related() get_by_related(mixed $related, string $field = NULL, string $value = NULL) Get results based on a related item.
  50 * @method DataMapper get_by_related_RELATEDFIELD() get_by_related_RELATEDFIELD(string $field = NULL, string $value = NULL) Get results based on a RELATEDFIELD. Ex: get_by_related_user('id', $userid);
  51 *
  52 * Save and Delete
  53 * @method DataMapper save_RELATEDFIELD() save_RELATEDFIELD(mixed $object) Saves relationship(s) using the specified RELATEDFIELD. Ex: save_user($user);
  54 * @method DataMapper delete_RELATEDFIELD() delete_RELATEDFIELD(mixed $object) Deletes relationship(s) using the specified RELATEDFIELD. Ex: delete_user($user);
  55 *
  56 * Related:
  57 * @method DataMapper where_related() where_related(mixed $related, string $field = NULL, string $value = NULL) Limits results based on a related field.
  58 * @method DataMapper where_between_related() where_related(mixed $related, string $field = NULL, string $value1 = NULL, string $value2 = NULL) Limits results based on a related field, via BETWEEN.
  59 * @method DataMapper or_where_related() or_where_related(mixed $related, string $field = NULL, string $value = NULL) Limits results based on a related field, via OR.
  60 * @method DataMapper where_in_related() where_in_related(mixed $related, string $field, array $values) Limits results by comparing a related field to a range of values.
  61 * @method DataMapper or_where_in_related() or_where_in_related(mixed $related, string $field, array $values) Limits results by comparing a related field to a range of values.
  62 * @method DataMapper where_not_in_related() where_not_in_related(mixed $related, string $field, array $values) Limits results by comparing a related field to a range of values.
  63 * @method DataMapper or_where_not_in_related() or_where_not_in_related(mixed $related, string $field, array $values) Limits results by comparing a related field to a range of values.
  64 * @method DataMapper like_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value.
  65 * @method DataMapper or_like_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value.
  66 * @method DataMapper not_like_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value.
  67 * @method DataMapper or_not_like_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value.
  68 * @method DataMapper ilike_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value (case insensitive).
  69 * @method DataMapper or_ilike_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value (case insensitive).
  70 * @method DataMapper not_ilike_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value (case insensitive).
  71 * @method DataMapper or_not_ilike_related() like_related(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a related field to a value (case insensitive).
  72 * @method DataMapper group_by_related() group_by_related(mixed $related, string $field) Groups the query by a related field.
  73 * @method DataMapper having_related() having_related(mixed $related, string $field, string $value) Groups the querying using a HAVING clause.
  74 * @method DataMapper or_having_related() having_related(mixed $related, string $field, string $value) Groups the querying using a HAVING clause, via OR.
  75 * @method DataMapper order_by_related() order_by_related(mixed $related, string $field, string $direction) Orders the query based on a related field.
  76 *
  77 *
  78 * Join Fields:
  79 * @method DataMapper where_join_field() where_join_field(mixed $related, string $field = NULL, string $value = NULL) Limits results based on a join field.
  80 * @method DataMapper where_between_join_field() where_related(mixed $related, string $field = NULL, string $value1 = NULL, string $value2 = NULL) Limits results based on a join field, via BETWEEN.
  81 * @method DataMapper or_where_join_field() or_where_join_field(mixed $related, string $field = NULL, string $value = NULL) Limits results based on a join field, via OR.
  82 * @method DataMapper where_in_join_field() where_in_join_field(mixed $related, string $field, array $values) Limits results by comparing a join field to a range of values.
  83 * @method DataMapper or_where_in_join_field() or_where_in_join_field(mixed $related, string $field, array $values) Limits results by comparing a join field to a range of values.
  84 * @method DataMapper where_not_in_join_field() where_not_in_join_field(mixed $related, string $field, array $values) Limits results by comparing a join field to a range of values.
  85 * @method DataMapper or_where_not_in_join_field() or_where_not_in_join_field(mixed $related, string $field, array $values) Limits results by comparing a join field to a range of values.
  86 * @method DataMapper like_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value.
  87 * @method DataMapper or_like_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value.
  88 * @method DataMapper not_like_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value.
  89 * @method DataMapper or_not_like_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value.
  90 * @method DataMapper ilike_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value (case insensitive).
  91 * @method DataMapper or_ilike_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value (case insensitive).
  92 * @method DataMapper not_ilike_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value (case insensitive).
  93 * @method DataMapper or_not_ilike_join_field() like_join_field(mixed $related, string $field, string $value, string $match = 'both') Limits results by matching a join field to a value (case insensitive).
  94 * @method DataMapper group_by_join_field() group_by_join_field(mixed $related, string $field) Groups the query by a join field.
  95 * @method DataMapper having_join_field() having_join_field(mixed $related, string $field, string $value) Groups the querying using a HAVING clause.
  96 * @method DataMapper or_having_join_field() having_join_field(mixed $related, string $field, string $value) Groups the querying using a HAVING clause, via OR.
  97 * @method DataMapper order_by_join_field() order_by_join_field(mixed $related, string $field, string $direction) Orders the query based on a join field.
  98 *
  99 * SQL Functions:
 100 * @method DataMapper select_func() select_func(string $function_name, mixed $args,..., string $alias) Selects the result of a SQL function. Alias is required.
 101 * @method DataMapper where_func() where_func(string $function_name, mixed $args,..., string $value) Limits results based on a SQL function.
 102 * @method DataMapper or_where_func() or_where_func(string $function_name, mixed $args,..., string $value) Limits results based on a SQL function, via OR.
 103 * @method DataMapper where_in_func() where_in_func(string $function_name, mixed $args,..., array $values) Limits results by comparing a SQL function to a range of values.
 104 * @method DataMapper or_where_in_func() or_where_in_func(string $function_name, mixed $args,..., array $values) Limits results by comparing a SQL function to a range of values.
 105 * @method DataMapper where_not_in_func() where_not_in_func(string $function_name, string $field, array $values) Limits results by comparing a SQL function to a range of values.
 106 * @method DataMapper or_where_not_in_func() or_where_not_in_func(string $function_name, mixed $args,..., array $values) Limits results by comparing a SQL function to a range of values.
 107 * @method DataMapper like_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value.
 108 * @method DataMapper or_like_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value.
 109 * @method DataMapper not_like_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value.
 110 * @method DataMapper or_not_like_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value.
 111 * @method DataMapper ilike_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value (case insensitive).
 112 * @method DataMapper or_ilike_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value (case insensitive).
 113 * @method DataMapper not_ilike_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value (case insensitive).
 114 * @method DataMapper or_not_ilike_func() like_func(string $function_name, mixed $args,..., string $value) Limits results by matching a SQL function to a value (case insensitive).
 115 * @method DataMapper group_by_func() group_by_func(string $function_name, mixed $args,...) Groups the query by a SQL function.
 116 * @method DataMapper having_func() having_func(string $function_name, mixed $args,..., string $value) Groups the querying using a HAVING clause.
 117 * @method DataMapper or_having_func() having_func(string $function_name, mixed $args,..., string $value) Groups the querying using a HAVING clause, via OR.
 118 * @method DataMapper order_by_func() order_by_func(string $function_name, mixed $args,..., string $direction) Orders the query based on a SQL function.
 119 *
 120 * Field -> SQL functions:
 121 * @method DataMapper where_field_field_func() where_field_func($field, string $function_name, mixed $args,...) Limits results based on a SQL function.
 122 * @method DataMapper where_between_field_field_func() where_between_field_func($field, string $function_name, mixed $args,...) Limits results based on a SQL function, via BETWEEN.
 123 * @method DataMapper or_where_field_field_func() or_where_field_func($field, string $function_name, mixed $args,...) Limits results based on a SQL function, via OR.
 124 * @method DataMapper where_in_field_field_func() where_in_field_func($field, string $function_name, mixed $args,...) Limits results by comparing a SQL function to a range of values.
 125 * @method DataMapper or_where_in_field_field_func() or_where_in_field_func($field, string $function_name, mixed $args,...) Limits results by comparing a SQL function to a range of values.
 126 * @method DataMapper where_not_in_field_field_func() where_not_in_field_func($field, string $function_name, string $field) Limits results by comparing a SQL function to a range of values.
 127 * @method DataMapper or_where_not_in_field_field_func() or_where_not_in_field_func($field, string $function_name, mixed $args,...) Limits results by comparing a SQL function to a range of values.
 128 * @method DataMapper like_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value.
 129 * @method DataMapper or_like_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value.
 130 * @method DataMapper not_like_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value.
 131 * @method DataMapper or_not_like_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value.
 132 * @method DataMapper ilike_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value (case insensitive).
 133 * @method DataMapper or_ilike_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value (case insensitive).
 134 * @method DataMapper not_ilike_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value (case insensitive).
 135 * @method DataMapper or_not_ilike_field_field_func() like_field_func($field, string $function_name, mixed $args,...) Limits results by matching a SQL function to a value (case insensitive).
 136 * @method DataMapper group_by_field_field_func() group_by_field_func($field, string $function_name, mixed $args,...) Groups the query by a SQL function.
 137 * @method DataMapper having_field_field_func() having_field_func($field, string $function_name, mixed $args,...) Groups the querying using a HAVING clause.
 138 * @method DataMapper or_having_field_field_func() having_field_func($field, string $function_name, mixed $args,...) Groups the querying using a HAVING clause, via OR.
 139 * @method DataMapper order_by_field_field_func() order_by_field_func($field, string $function_name, mixed $args,...) Orders the query based on a SQL function.
 140 *
 141 * Subqueries:
 142 * @method DataMapper select_subquery() select_subquery(DataMapper $subquery, string $alias) Selects the result of a function. Alias is required.
 143 * @method DataMapper where_subquery() where_subquery(mixed $subquery_or_field, mixed $value_or_subquery) Limits results based on a subquery.
 144 * @method DataMapper or_where_subquery() or_where_subquery(mixed $subquery_or_field, mixed $value_or_subquery) Limits results based on a subquery, via OR.
 145 * @method DataMapper where_in_subquery() where_in_subquery(mixed $subquery_or_field, mixed $values_or_subquery) Limits results by comparing a subquery to a range of values.
 146 * @method DataMapper or_where_in_subquery() or_where_in_subquery(mixed $subquery_or_field, mixed $values_or_subquery) Limits results by comparing a subquery to a range of values.
 147 * @method DataMapper where_not_in_subquery() where_not_in_subquery(mixed $subquery_or_field, string $field, mixed $values_or_subquery) Limits results by comparing a subquery to a range of values.
 148 * @method DataMapper or_where_not_in_subquery() or_where_not_in_subquery(mixed $subquery_or_field, mixed $values_or_subquery) Limits results by comparing a subquery to a range of values.
 149 * @method DataMapper like_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value.
 150 * @method DataMapper or_like_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value.
 151 * @method DataMapper not_like_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value.
 152 * @method DataMapper or_not_like_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value.
 153 * @method DataMapper ilike_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value (case insensitive).
 154 * @method DataMapper or_ilike_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value (case insensitive).
 155 * @method DataMapper not_ilike_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value (case insensitive).
 156 * @method DataMapper or_not_ilike_subquery() like_subquery(DataMapper $subquery, string $value, string $match = 'both') Limits results by matching a subquery to a value (case insensitive).
 157 * @method DataMapper having_subquery() having_subquery(string $field, DataMapper $subquery) Groups the querying using a HAVING clause.
 158 * @method DataMapper or_having_subquery() having_subquery(string $field, DataMapper $subquery) Groups the querying using a HAVING clause, via OR.
 159 * @method DataMapper order_by_subquery() order_by_subquery(DataMapper $subquery, string $direction) Orders the query based on a subquery.
 160 *
 161 * Related Subqueries:
 162 * @method DataMapper where_related_subquery() where_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results based on a subquery.
 163 * @method DataMapper or_where_related_subquery() or_where_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results based on a subquery, via OR.
 164 * @method DataMapper where_in_related_subquery() where_in_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results by comparing a subquery to a range of values.
 165 * @method DataMapper or_where_in_related_subquery() or_where_in_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results by comparing a subquery to a range of values.
 166 * @method DataMapper where_not_in_related_subquery() where_not_in_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results by comparing a subquery to a range of values.
 167 * @method DataMapper or_where_not_in_related_subquery() or_where_not_in_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Limits results by comparing a subquery to a range of values.
 168 * @method DataMapper having_related_subquery() having_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Groups the querying using a HAVING clause.
 169 * @method DataMapper or_having_related_subquery() having_related_subquery(mixed $related_model, string $related_field, DataMapper $subquery) Groups the querying using a HAVING clause, via OR.
 170 *
 171 * Array Extension:
 172 * @method array to_array() to_array($fields = '') NEEDS ARRAY EXTENSION.  Converts this object into an associative array.  @link DMZ_Array::to_array
 173 * @method array all_to_array() all_to_array($fields = '') NEEDS ARRAY EXTENSION.  Converts the all array into an associative array.  @link DMZ_Array::all_to_array
 174 * @method array|bool from_array() from_array($data, $fields = '', $save = FALSE) NEEDS ARRAY EXTENSION.  Converts $this->all into an associative array.  @link DMZ_Array::all_to_array
 175 *
 176 * CSV Extension
 177 * @method bool csv_export() csv_export($filename, $fields = '', $include_header = TRUE) NEEDS CSV EXTENSION.  Exports this object as a CSV file.
 178 * @method array csv_import() csv_import($filename, $fields = '', $header_row = TRUE, $callback = NULL) NEEDS CSV EXTENSION.  Imports a CSV file into this object.
 179 *
 180 * JSON Extension:
 181 * @method string to_json() to_json($fields = '', $pretty_print = FALSE) NEEDS JSON EXTENSION.  Converts this object into a JSON string.
 182 * @method string all_to_json() all_to_json($fields = '', $pretty_print = FALSE) NEEDS JSON EXTENSION.  Converts the all array into a JSON string.
 183 * @method bool from_json() from_json($json, $fields = '') NEEDS JSON EXTENSION.  Imports the values from a JSON string into this object.
 184 * @method void set_json_content_type() set_json_content_type() NEEDS JSON EXTENSION.  Sets the content type header to Content-Type: application/json.
 185 *
 186 * SimpleCache Extension:
 187 * @method DataMapper get_cached() get_cached($limit = '', $offset = '') NEEDS SIMPLECACHE EXTENSION.  Enables cacheable queries.
 188 * @method DataMapper clear_cache() get_cached($segment,...) NEEDS SIMPLECACHE EXTENSION.  Clears a cache for the specfied segment.
 189 *
 190 * Translate Extension:
 191 *
 192 * Nestedsets Extension:
 193 *
 194 */
 195class DataMapper implements IteratorAggregate {
 196
 197	/**
 198	 * Stores the shared configuration
 199	 * @var array
 200	 */
 201	static $config = array();
 202	/**
 203	 * Stores settings that are common across a specific Model
 204	 * @var array
 205	 */
 206	static $common = array(DMZ_CLASSNAMES_KEY => array());
 207	/**
 208	 * Stores global extensions
 209	 * @var array
 210	 */
 211	static $global_extensions = array();
 212	/**
 213	 * Used to override unset default properties.
 214	 * @var array
 215	 */
 216	static $_dmz_config_defaults = array(
 217		'timestamp_format' => 'Y-m-d H:i:s O',
 218		'created_field' => 'created',
 219		'updated_field' => 'updated',
 220		'extensions_path' => 'datamapper',
 221		'field_label_lang_format' => '${model}_${field}',
 222		'model_prefix' => '',
 223		'model_suffix' => '',
 224	);
 225
 226	/**
 227	 * Contains any errors that occur during validation, saving, or other
 228	 * database access.
 229	 * @var DM_Error_Object
 230	 */
 231	public $error;
 232	/**
 233	 * Used to keep track of the original values from the database, to
 234	 * prevent unecessarily changing fields.
 235	 * @var object
 236	 */
 237	public $stored;
 238	/**
 239	 * DB Table Prefix
 240	 * @var string
 241	 */
 242	public $prefix = '';
 243	/**
 244	 * DB Join Table Prefix
 245	 * @var string
 246	 */
 247	public $join_prefix = '';
 248	/**
 249	 * The name of the table for this model (may be automatically generated
 250	 * from the classname).
 251	 * @var string
 252	 */
 253	public $table = '';
 254	/**
 255	 * The singular name for this model (may be automatically generated from
 256	 * the classname).
 257	 * @var string
 258	 */
 259	public $model = '';
 260	/**
 261	 * The primary key used for this models table
 262	 * the classname).
 263	 * @var string
 264	 */
 265	public $primary_key = 'id';
 266	/**
 267	 * Can be used to override the default database behavior.
 268	 * @var mixed
 269	 */
 270	public $db_params = '';
 271	/**
 272	 * Prefix string used when reporting errors.
 273	 * @var string
 274	 */
 275	public $error_prefix = '';
 276	/**
 277	 * Suffic string used when reporting errors.
 278	 * @var string
 279	 */
 280	public $error_suffix = '';
 281	/**
 282	 * Custom name for the automatic timestamp saved with new objects.
 283	 * Defaults to 'created'.
 284	 * @var string
 285	 */
 286	public $created_field = '';
 287	/**
 288	 * Custom name for the automatic timestamp saved when an object changes.
 289	 * Defaults to 'updated'.
 290	 * @var string
 291	 */
 292	public $updated_field = '';
 293	/**
 294	 * If TRUE, automatically wrap every save and delete in a transaction.
 295	 * @var bool
 296	 */
 297	public $auto_transaction = FALSE;
 298	/**
 299	 * If TRUE, has_many relationships are automatically loaded when accessed.
 300	 * Not recommended in most situations.
 301	 * @var bool
 302	 */
 303	public $auto_populate_has_many = FALSE;
 304	/**
 305	 * If TRUE, has_one relationships are automatically loaded when accessed.
 306	 * Not recommended in some situations.
 307	 * @var bool
 308	 */
 309	public $auto_populate_has_one = FALSE;
 310	/**
 311	 * Enables the old method of storing the all array using an object's ID.
 312	 * @var bool
 313	 */
 314	public $all_array_uses_ids = FALSE;
 315	/**
 316	 * The result of validate is stored here.
 317	 * @var bool
 318	 */
 319	public $valid = FALSE;
 320	/**
 321	 * If TRUE, the created/updated fields are stored using local time.
 322	 * If FALSE (the default), they are stored using UTC
 323	 * @var bool
 324	 */
 325	public $local_time = FALSE;
 326	/**
 327	 * If TRUE, the created/updated fields are stored as a unix timestamp,
 328	 * as opposed to a formatted string.
 329	 * Defaults to FALSE.
 330	 * @var bool
 331	 */
 332	public $unix_timestamp = FALSE;
 333	/**
 334	 * Set to a date format to override the default format of
 335	 *	'Y-m-d H:i:s O'
 336	 * @var string
 337	 */
 338	public $timestamp_format = '';
 339	/**
 340	 * delete relations on delete of an object. Defaults to TRUE.
 341	 * set to FALSE if you RDBMS takes care of this using constraints
 342	 * @var bool
 343	 */
 344	public $cascade_delete = TRUE;
 345	/**
 346	 * Contains the database fields for this object.
 347	 * ** Automatically configured **
 348	 * @var array
 349	 */
 350	public $fields = array();
 351	/**
 352	 * Set to a string to use when autoloading lang files.
 353	 * Can contain two magic values: ${model} and ${table}.
 354	 * These are automatically
 355	 * replaced when looking up the language file.
 356	 * Defaults to model_${model}
 357	 * @var string
 358	 */
 359	public $lang_file_format = '';
 360	/**
 361	 * Set to a string to use when looking up field labels.  Can contain three
 362	 * magic values: ${model}, ${table}, and ${field}.  These are automatically
 363	 * replaced when looking up the language file.
 364	 * Defaults to ${model}_${field}
 365	 * @var string
 366	 */
 367	public $field_label_lang_format = '';
 368	/**
 369	 * Contains the result of the last query.
 370	 * @var array
 371	 */
 372	public $all = array();
 373	/**
 374	 * Semi-private field used to track the parent model/id if there is one.
 375	 * @var array
 376	 */
 377	public $parent = array();
 378	/**
 379	 * Contains the validation rules, label, and get_rules for each field.
 380	 * @var array
 381	 */
 382	public $validation = array();
 383	/**
 384	 * Contains any related objects of which this model is related one or more times.
 385	 * @var array
 386	 */
 387	public $has_many = array();
 388	/**
 389	 * Contains any related objects of which this model is singularly related.
 390	 * @var array
 391	 */
 392	public $has_one = array();
 393	/**
 394	 * Used to enable or disable the production cache.
 395	 * This should really only be set in the global configuration.
 396	 * @var bool
 397	 */
 398	public $production_cache = FALSE;
 399	/**
 400	 * Used to determine where to look for extensions.
 401	 * This should really only be set in the global configuration.
 402	 * @var string
 403	 */
 404	public $extensions_path = '';
 405	/**
 406	 * If set to an array of names, this will automatically load the
 407	 * specified extensions for this model.
 408	 * @var mixed
 409	 */
 410	public $extensions = NULL;
 411	/**
 412	 * If a query returns more than the number of rows specified here,
 413	 * then it will be automatically freed after a get.
 414	 * @var int
 415	 */
 416	public $free_result_threshold = 100;
 417	/**
 418	 * This can be specified as an array of fields to sort by if no other
 419	 * sorting or selection has occurred.
 420	 * @var mixed
 421	 */
 422	public $default_order_by = NULL;
 423	/**
 424	 * Model prefix/suffix options.
 425	 * @var string
 426	 */
 427	public $model_prefix = NULL;
 428	public $model_suffix = NULL;
 429
 430	// tracks whether or not the object has already been validated
 431	protected $_validated = FALSE;
 432	// tracks whether validation needs to be forced before save
 433	protected $_force_validation = FALSE;
 434	// Tracks the columns that need to be instantiated after a GET
 435	protected $_instantiations = NULL;
 436	// Tracks get_rules, matches, and intval rules, to spped up _to_object
 437	protected $_field_tracking = NULL;
 438	// used to track related queries in deep relationships.
 439	protected $_query_related = array();
 440	// If true before a related get(), any extra fields on the join table will be added.
 441	protected $_include_join_fields = FALSE;
 442	// If true before a save, this will force the next save to be new.
 443	protected $_force_save_as_new = FALSE;
 444	// If true, the next where statement will not be prefixed with an AND or OR.
 445	protected $_where_group_started = FALSE;
 446	// Tracks total number of groups created
 447	protected $_group_count = 0;
 448
 449	// storage for additional model paths for the autoloader
 450	protected static $model_paths = array();
 451
 452	/**
 453	 * Constructors (both PHP4 and PHP5 style, to stay compatible)
 454	 *
 455	 * Initialize DataMapper.
 456	 * @param	int $id if provided, load in the object specified by that ID.
 457	 */
 458	public function __construct($id = NULL)
 459	{
 460		return $this->DataMapper($id);
 461	}
 462
 463	public function DataMapper($id = NULL)
 464	{
 465		$this->_dmz_assign_libraries();
 466
 467		$this_class = strtolower(get_class($this));
 468		$is_dmz = $this_class == 'datamapper';
 469
 470		if($is_dmz)
 471		{
 472			$this->_load_languages();
 473
 474			$this->_load_helpers();
 475		}
 476		else
 477		{
 478			$this_class = str_replace(array(
 479				DataMapper::$config['model_prefix'],
 480				DataMapper::$config['model_suffix'],
 481			), '', $this_class);
 482		}
 483
 484		// this is to ensure that singular is only called once per model
 485		if(isset(DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class])) {
 486			$common_key = DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class];
 487		} else {
 488			DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class] = $common_key = singular($this_class);
 489		}
 490
 491		// Determine model name
 492		if (empty($this->model))
 493		{
 494			$this->model = $common_key;
 495		}
 496
 497		// Load stored config settings by reference
 498		foreach (DataMapper::$config as $config_key => &$config_value)
 499		{
 500			// Only if they're not already set
 501			if (empty($this->{$config_key}))
 502			{
 503				$this->{$config_key} =& $config_value;
 504			}
 505		}
 506
 507		// Load model settings if not in common storage
 508		if ( ! isset(DataMapper::$common[$common_key]))
 509		{
 510			// If model is 'datamapper' then this is the initial autoload by CodeIgniter
 511			if ($is_dmz)
 512			{
 513				// Load config settings
 514				$this->config->load('datamapper', TRUE, TRUE);
 515
 516				// Get and store config settings
 517				DataMapper::$config = $this->config->item('datamapper');
 518
 519				// now double check that all required config values were set
 520				foreach(DataMapper::$_dmz_config_defaults as $config_key => $config_value)
 521				{
 522					if(empty(DataMapper::$config[$config_key]))
 523					{
 524						DataMapper::$config[$config_key] = $config_value;
 525					}
 526				}
 527
 528				DataMapper::_load_extensions(DataMapper::$global_extensions, DataMapper::$config['extensions']);
 529				unset(DataMapper::$config['extensions']);
 530
 531				return;
 532			}
 533
 534			// load language file, if requested and it exists
 535			if(!empty($this->lang_file_format))
 536			{
 537				$lang_file = str_replace(array('${model}', '${table}'), array($this->model, $this->table), $this->lang_file_format);
 538				$deft_lang = $this->config->item('language');
 539				$idiom = ($deft_lang == '') ? 'english' : $deft_lang;
 540				if(file_exists(APPPATH.'language/'.$idiom.'/'.$lang_file.'_lang'.EXT))
 541				{
 542					$this->lang->load($lang_file, $idiom);
 543				}
 544			}
 545
 546			$loaded_from_cache = FALSE;
 547
 548			// Load in the production cache for this model, if it exists
 549			if( ! empty(DataMapper::$config['production_cache']))
 550			{
 551				// check if it's a fully qualified path first
 552				if (!is_dir($cache_folder = DataMapper::$config['production_cache']))
 553				{
 554					// if not, it's relative to the application path
 555					$cache_folder = APPPATH . DataMapper::$config['production_cache'];
 556				}
 557				if(file_exists($cache_folder) && is_dir($cache_folder) && is_writeable($cache_folder))
 558				{
 559					$cache_file = $cache_folder . '/' . $common_key . EXT;
 560					if(file_exists($cache_file))
 561					{
 562						include($cache_file);
 563						if(isset($cache))
 564						{
 565							DataMapper::$common[$common_key] =& $cache;
 566							unset($cache);
 567
 568							// allow subclasses to add initializations
 569							if(method_exists($this, 'post_model_init'))
 570							{
 571								$this->post_model_init(TRUE);
 572							}
 573
 574							// Load extensions (they are not cacheable)
 575							$this->_initiate_local_extensions($common_key);
 576
 577							$loaded_from_cache = TRUE;
 578						}
 579					}
 580				}
 581			}
 582
 583			if(! $loaded_from_cache)
 584			{
 585
 586				// Determine table name
 587				if (empty($this->table))
 588				{
 589					$this->table = plural($this->_prefix_suffix(get_class($this)));
 590				}
 591
 592				// Add prefix to table
 593				$this->table = $this->prefix . $this->table;
 594
 595				$this->_field_tracking = array(
 596					'get_rules' => array(),
 597					'matches' => array(),
 598					'intval' => array('id')
 599				);
 600
 601				// Convert validation into associative array by field name
 602				$associative_validation = array();
 603
 604				foreach ($this->validation as $name => $validation)
 605				{
 606					if(is_string($name)) {
 607						$validation['field'] = $name;
 608					} else {
 609						$name = $validation['field'];
 610					}
 611
 612					// clean up possibly missing fields
 613					if( ! isset($validation['rules']))
 614					{
 615						$validation['rules'] = array();
 616					}
 617
 618					// Populate associative validation array
 619					$associative_validation[$name] = $validation;
 620
 621					if (!empty($validation['get_rules']))
 622					{
 623						$this->_field_tracking['get_rules'][] = $name;
 624					}
 625
 626					// Check if there is a "matches" validation rule
 627					if (isset($validation['rules']['matches']))
 628					{
 629						$this->_field_tracking['matches'][$name] = $validation['rules']['matches'];
 630					}
 631				}
 632
 633				// set up id column, if not set
 634				if(!isset($associative_validation['id']))
 635				{
 636					// label is set below, to prevent caching language-based labels
 637					$associative_validation['id'] = array(
 638						'field' => 'id',
 639						'rules' => array('integer')
 640					);
 641				}
 642
 643				$this->validation = $associative_validation;
 644
 645				// Force all other has_one ITFKs to integers on get
 646				foreach($this->has_one as $related => $rel_props)
 647				{
 648					$field = $related . '_id';
 649					if(	in_array($field, $this->fields) &&
 650						( ! isset($this->validation[$field]) || // does not have a validation key or...
 651							! isset($this->validation[$field]['get_rules'])) &&  // a get_rules key...
 652						( ! isset($this->validation[$related]) || // nor does the related have a validation key or...
 653							! isset($this->validation[$related]['get_rules'])) ) // a get_rules key
 654					{
 655						// assume an int
 656						$this->_field_tracking['intval'][] = $field;
 657					}
 658				}
 659
 660				// Get and store the table's field names and meta data
 661				$fields = $this->db->field_data($this->table);
 662
 663				// Store only the field names and ensure validation list includes all fields
 664				foreach ($fields as $field)
 665				{
 666					// Populate fields array
 667					$this->fields[] = $field->name;
 668
 669					// Add validation if current field has none
 670					if ( ! isset($this->validation[$field->name]))
 671					{
 672						// label is set below, to prevent caching language-based labels
 673						$this->validation[$field->name] = array('field' => $field->name, 'rules' => array());
 674					}
 675				}
 676
 677				// convert simple has_one and has_many arrays into more advanced ones
 678				foreach(array('has_one', 'has_many') as $arr)
 679				{
 680					foreach ($this->{$arr} as $related_field => $rel_props)
 681					{
 682						// process the relationship
 683						$this->_relationship($arr, $rel_props, $related_field);
 684					}
 685				}
 686
 687				// allow subclasses to add initializations
 688				if(method_exists($this, 'post_model_init'))
 689				{
 690					$this->post_model_init(FALSE);
 691				}
 692
 693				// Store common model settings
 694				foreach (array('table', 'fields', 'validation',
 695							'has_one', 'has_many', '_field_tracking') as $item)
 696				{
 697					DataMapper::$common[$common_key][$item] = $this->{$item};
 698				}
 699
 700				// store the item to the production cache
 701				$this->production_cache();
 702
 703				// Load extensions last, so they aren't cached.
 704				$this->_initiate_local_extensions($common_key);
 705			}
 706
 707			// Finally, localize the labels here (because they shouldn't be cached
 708			// This also sets any missing labels.
 709			$validation =& DataMapper::$common[$common_key]['validation'];
 710			foreach($validation as $field => &$val)
 711			{
 712				// Localize label if necessary
 713				$val['label'] = $this->localize_label($field,
 714						isset($val['label']) ?
 715							$val['label'] :
 716							FALSE);
 717			}
 718			unset($validation);
 719		}
 720
 721		// Load stored common model settings by reference
 722		foreach(DataMapper::$common[$common_key] as $key => &$value)
 723		{
 724			$this->{$key} =& $value;
 725		}
 726
 727		// Clear object properties to set at default values
 728		$this->clear();
 729
 730		if( ! empty($id) && is_numeric($id))
 731		{
 732			$this->get_by_id(intval($id));
 733		}
 734	}
 735
 736	// --------------------------------------------------------------------
 737
 738	/**
 739	 * Reloads in the configuration data for a model.  This is mainly
 740	 * used to handle language changes.  Only this instance and new instances
 741	 * will see the changes.
 742	 */
 743	public function reinitialize_model()
 744	{
 745		// this is to ensure that singular is only called once per model
 746		if(isset(DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class])) {
 747			$common_key = DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class];
 748		} else {
 749			DataMapper::$common[DMZ_CLASSNAMES_KEY][$this_class] = $common_key = singular($this_class);
 750		}
 751		unset(DataMapper::$common[$common_key]);
 752		$model = get_class($this);
 753		new $model(); // re-initialze
 754
 755		// Load stored common model settings by reference
 756		foreach(DataMapper::$common[$common_key] as $key => &$value)
 757		{
 758			$this->{$key} =& $value;
 759		}
 760	}
 761
 762	// --------------------------------------------------------------------
 763
 764	/**
 765	 * Autoload
 766	 *
 767	 * Autoloads object classes that are used with DataMapper.
 768	 * This method will look in any model directories available to CI.
 769	 *
 770	 * Note:
 771	 * It is important that they are autoloaded as loading them manually with
 772	 * CodeIgniter's loader class will cause DataMapper's __get and __set functions
 773	 * to not function.
 774	 *
 775	 * @param	string $class Name of class to load.
 776	 */
 777	public static function autoload($class)
 778	{
 779		$CI =& get_instance();
 780
 781		// Don't attempt to autoload CI_ , EE_, or custom prefixed classes
 782		if (in_array(substr($class, 0, 3), array('CI_', 'EE_')) OR strpos($class, $CI->config->item('subclass_prefix')) === 0)
 783		{
 784			return;
 785		}
 786
 787		// Prepare class
 788		$class = strtolower($class);
 789
 790		// Prepare path
 791		if (method_exists($CI->load, 'get_package_paths'))
 792		{
 793			// use CI 2.0 loader's model paths
 794			$paths = $CI->load->get_package_paths(false);
 795		}
 796		else
 797		{
 798			// search only the applications models folder
 799			$paths[] = APPPATH;
 800		}
 801
 802		foreach (array_merge($paths, self::$model_paths) as $path)
 803		{
 804			// Prepare file
 805			$file = $path . 'models/' . $class . EXT;
 806
 807			// Check if file exists, require_once if it does
 808			if (file_exists($file))
 809			{
 810				require_once($file);
 811				break;
 812			}
 813		}
 814
 815		// if class not loaded, do a recursive search of model paths for the class
 816		if (! class_exists($class))
 817		{
 818			foreach($paths as $path)
 819			{
 820				$found = DataMapper::recursive_require_once($class, $path . 'models');
 821				if($found)
 822				{
 823					break;
 824				}
 825			}
 826		}
 827	}
 828
 829	// --------------------------------------------------------------------
 830
 831	/**
 832	 * Add Model Path
 833	 *
 834	 * Manually add paths for the model autoloader
 835	 *
 836	 * @param	mixed $paths path or array of paths to search
 837	 */
 838	protected static function add_model_path($paths)
 839	{
 840		if ( ! is_array($paths) )
 841		{
 842			$paths = array($paths);
 843		}
 844
 845		foreach($paths as $path)
 846		{
 847			$path = rtrim($path, '/') . '/';
 848			if ( is_dir($path.'models') && ! in_array($path, self::$model_paths))
 849			{
 850				self::$model_paths[] = $path;
 851			}
 852		}
 853	}
 854
 855	// --------------------------------------------------------------------
 856
 857	/**
 858	 * Recursive Require Once
 859	 *
 860	 * Recursively searches the path for the class, require_once if found.
 861	 *
 862	 * @param	string $class Name of class to look for
 863	 * @param	string $path Current path to search
 864	 */
 865	protected static function recursive_require_once($class, $path)
 866	{
 867		$found = FALSE;
 868		if(is_dir($path))
 869		{
 870			$handle = opendir($path);
 871			if ($handle)
 872			{
 873				while (FALSE !== ($dir = readdir($handle)))
 874				{
 875					// If dir does not contain a dot
 876					if (strpos($dir, '.') === FALSE)
 877					{
 878						// Prepare recursive path
 879						$recursive_path = $path . '/' . $dir;
 880
 881						// Prepare file
 882						$file = $recursive_path . '/' . $class . EXT;
 883
 884						// Check if file exists, require_once if it does
 885						if (file_exists($file))
 886						{
 887							require_once($file);
 888							$found = TRUE;
 889
 890							break;
 891						}
 892						else if (is_dir($recursive_path))
 893						{
 894							// Do a recursive search of the path for the class
 895							DataMapper::recursive_require_once($class, $recursive_path);
 896						}
 897					}
 898				}
 899
 900				closedir($handle);
 901			}
 902		}
 903		return $found;
 904	}
 905
 906	// --------------------------------------------------------------------
 907
 908	/**
 909	 * Loads in any extensions used by this class or globally.
 910	 *
 911	 * @param	array $extensions List of extensions to add to.
 912	 * @param	array $name List of new extensions to load.
 913	 */
 914	protected static function _load_extensions(&$extensions, $names)
 915	{
 916		$CI =& get_instance();
 917		$class_prefixes = array(
 918			0 => 'DMZ_',
 919			1 => 'DataMapper_',
 920			2 => $CI->config->item('subclass_prefix'),
 921			3 => 'CI_'
 922		);
 923		foreach($names as $name => $options)
 924		{
 925			if( ! is_string($name))
 926			{
 927				$name = $options;
 928				$options = NULL;
 929			}
 930			// only load an extension if it wasn't already loaded in this context
 931			if(isset($extensions[$name]))
 932			{
 933				return;
 934			}
 935
 936			if( ! isset($extensions['_methods']))
 937			{
 938				$extensions['_methods'] = array();
 939			}
 940
 941			// determine the file name and class name
 942			if(strpos($name, '/') === FALSE)
 943			{
 944				$file = APPPATH . DataMapper::$config['extensions_path'] . '/' . $name . EXT;
 945				$ext = $name;
 946			}
 947			else
 948			{
 949				$file = APPPATH . $name . EXT;
 950				$ext = array_pop(explode('/', $name));
 951			}
 952
 953			if(!file_exists($file))
 954			{
 955				show_error('DataMapper Error: loading extension ' . $name . ': File not found.');
 956			}
 957
 958			// load class
 959			include_once($file);
 960
 961			// Allow for DMZ_Extension, DataMapper_Extension, etc.
 962			foreach($class_prefixes as $index => $prefix)
 963			{
 964				if(class_exists($prefix.$ext))
 965				{
 966					if($index == 2) // "MY_"
 967					{
 968						// Load in the library this class is based on
 969						$CI->load->library($ext);
 970					}
 971					$ext = $prefix.$ext;
 972					break;
 973				}
 974			}
 975			if(!class_exists($ext))
 976			{
 977				show_error("DataMapper Error: Unable to find a class for extension $name.");
 978			}
 979			// create class
 980			if(is_null($options))
 981			{
 982				$o = new $ext(NULL, isset($this) ? $this : NULL);
 983			}
 984			else
 985			{
 986				$o = new $ext($options, isset($this) ? $this : NULL);
 987			}
 988			$extensions[$name] = $o;
 989
 990			// figure out which methods can be called on this class.
 991			$methods = get_class_methods($ext);
 992			foreach($methods as $m)
 993			{
 994				// do not load private methods or methods already loaded.
 995				if($m[0] !== '_' &&
 996						is_callable(array($o, $m)) &&
 997						! isset($extensions['_methods'][$m])
 998						) {
 999					// store this method.
1000					$extensions['_methods'][$m] = $name;
1001				}
1002			}
1003		}
1004	}
1005
1006	// --------------------------------------------------------------------
1007
1008	/**
1009	 * Loads the extensions that are local to this model.
1010	 * @param	string $common_key Shared key to save extenions to.
1011	 */
1012	private function _initiate_local_extensions($common_key)
1013	{
1014		if(!empty($this->extensions))
1015		{
1016			$extensions = $this->extensions;
1017			$this->extensions = array();
1018			DataMapper::_load_extensions($this->extensions, $extensions);
1019		}
1020		else
1021		{
1022			// ensure an empty array
1023			$this->extensions = array('_methods' => array());
1024		}
1025		// bind to the shared key, for dynamic loading
1026		DataMapper::$common[$common_key]['extensions'] =& $this->extensions;
1027	}
1028
1029	// --------------------------------------------------------------------
1030
1031	/**
1032	 * Dynamically load an extension when needed.
1033	 * @param	object $name Name of the extension (or array of extensions).
1034	 * @param	array $options Options for the extension
1035	 * @param	boolean $local If TRUE, only loads the extension into this object
1036	 */
1037	public function load_extension($name, $options = NULL, $local = FALSE)
1038	{
1039		if( ! is_array($name))
1040		{
1041			if( ! is_null($options))
1042			{
1043				$name = array($name => $options);
1044			}
1045			else
1046			{
1047				$name = array($name);
1048			}
1049		}
1050		// called individually to ensure that the array is modified directly
1051		// (and not copied instead)
1052		if($local)
1053		{
1054			DataMapper::_load_extensions($this->extensions, $name);
1055		}
1056		else
1057		{
1058			DataMapper::_load_extensions(DataMapper::$global_extensions, $name);
1059		}
1060
1061	}
1062
1063	// --------------------------------------------------------------------
1064
1065
1066	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1067	 *																   *
1068	 * Magic methods													 *
1069	 *																   *
1070	 * The following are methods to override the default PHP behaviour.  *
1071	 *																   *
1072	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1073
1074	// --------------------------------------------------------------------
1075
1076	/**
1077	 * Magic Get
1078	 *
1079	 * Returns the value of the named property.
1080	 * If named property is a related item, instantiate it first.
1081	 *
1082	 * This method also instantiates the DB object and the form_validation
1083	 * objects as necessary
1084	 *
1085	 * @ignore
1086	 * @param	string $name Name of property to look for
1087	 * @return	mixed
1088	 */
1089	public function __get($name)
1090	{
1091		// We dynamically get DB when needed, and create a copy.
1092		// This allows multiple queries to be generated at the same time.
1093		if($name == 'db')
1094		{
1095			$CI =& get_instance();
1096			if($this->db_params === FALSE)
1097			{
1098				if ( ! isset($CI->db) || ! is_object($CI->db) || ! isset($CI->db->dbdriver) )
1099				{
1100					show_error('DataMapper Error: CodeIgniter database library not loaded.');
1101				}
1102				$this->db =& $CI->db;
1103			}
1104			else
1105			{
1106				if($this->db_params == '' || $this->db_params === TRUE)
1107				{
1108					if ( ! isset($CI->db) || ! is_object($CI->db) || ! isset($CI->db->dbdriver) )
1109					{
1110						show_error('DataMapper Error: CodeIgniter database library not loaded.');
1111					}
1112					// ensure the shared DB is disconnected, even if the app exits uncleanly
1113					if(!isset($CI->db->_has_shutdown_hook))
1114					{
1115						register_shutdown_function(array($CI->db, 'close'));
1116						$CI->db->_has_shutdown_hook = TRUE;
1117					}
1118					// clone, so we don't create additional connections to the DB
1119					$this->db = clone($CI->db);
1120					$this->db->_reset_select();
1121				}
1122				else
1123				{
1124					// connecting to a different database, so we *must* create additional copies.
1125					// It is up to the developer to close the connection!
1126					$this->db = $CI->load->database($this->db_params, TRUE, TRUE);
1127				}
1128				// these items are shared (for debugging)
1129				if(is_object($CI->db) && isset($CI->db->dbdriver))
1130				{
1131					$this->db->queries =& $CI->db->queries;
1132					$this->db->query_times =& $CI->db->query_times;
1133				}
1134			}
1135			// ensure the created DB is disconnected, even if the app exits uncleanly
1136			if(!isset($this->db->_has_shutdown_hook))
1137			{
1138				register_shutdown_function(array($this->db, 'close'));
1139				$this->db->_has_shutdown_hook = TRUE;
1140			}
1141			return $this->db;
1142		}
1143
1144		// Special case to get form_validation when first accessed
1145		if($name == 'form_validation')
1146		{
1147			if ( ! isset($this->form_validation) )
1148			{
1149				$CI =& get_instance();
1150				if( ! isset($CI->form_validation))
1151				{
1152					$CI->load->library('form_validation');
1153					$this->lang->load('form_validation');
1154					$this->load->unset_form_validation_class();
1155				}
1156				$this->form_validation = $CI->form_validation;
1157			}
1158			return $this->form_validation;
1159		}
1160
1161		$has_many = isset($this->has_many[$name]);
1162		$has_one = isset($this->has_one[$name]);
1163
1164		// If named property is a "has many" or "has one" related item
1165		if ($has_many || $has_one)
1166		{
1167			$related_properties = $has_many ? $this->has_many[$name] : $this->has_one[$name];
1168			// Instantiate it before accessing
1169			$class = $this->model_prefix.$related_properties['class'].$this->model_suffix;
1170			$this->{$name} = new $class();
1171
1172			// Store parent data
1173			$this->{$name}->parent = array('model' => $related_properties['other_field'], 'id' => $this->id);
1174
1175			// Check if Auto Populate for "has many" or "has one" is on
1176			// (but only if this object exists in the DB, and we aren't instantiating)
1177			if ($this->exists() &&
1178					($has_many && ($this->auto_populate_has_many || $this->has_many[$name]['auto_populate'])) || ($has_one && ($this->auto_populate_has_one || $this->has_one[$name]['auto_populate'])))
1179			{
1180				$this->{$name}->get();
1181			}
1182
1183			return $this->{$name};
1184		}
1185
1186		$name_single = singular($name);
1187		if($name_single !== $name) {
1188			// possibly return single form of name
1189			$test = $this->{$name_single};
1190			if(is_object($test)) {
1191				return $test;
1192			}
1193		}
1194
1195		return NULL;
1196	}
1197
1198	// --------------------------------------------------------------------
1199
1200	/**
1201	 * Used several places to temporarily override the auto_populate setting
1202	 * @ignore
1203	 * @param string $related Related Name
1204	 * @return DataMapper|NULL
1205	 */
1206	private function &_get_without_auto_populating($related)
1207	{
1208		$b_many = $this->auto_populate_has_many;
1209		$b_one = $this->auto_populate_has_one;
1210		$this->auto_populate_has_many = FALSE;
1211		$this->auto_populate_has_one = FALSE;
1212		$ret =& $this->{$related};
1213		$this->auto_populate_has_many = $b_many;
1214		$this->auto_populate_has_one = $b_one;
1215		return $ret;
1216	}
1217
1218	// --------------------------------------------------------------------
1219
1220	/**
1221	 * Magic Call
1222	 *
1223	 * Calls special methods, or extension methods.
1224	 *
1225	 * @ignore
1226	 * @param	string $method Method name
1227	 * @param	array $arguments Arguments to method
1228	 * @return	mixed
1229	 */
1230	public function __call($method, $arguments)
1231	{
1232
1233		// List of watched method names
1234		// NOTE: order matters: make sure more specific items are listed before
1235		// less specific items
1236		$watched_methods = array(
1237			'save_', 'delete_',
1238			'get_by_related_', 'get_by_related', 'get_by_',
1239			'_related_subquery', '_subquery',
1240			'_related_', '_related',
1241			'_join_field',
1242			'_…

Large files files are truncated, but you can click here to view the full file