PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/application/libraries/datamapper.php

https://bitbucket.org/anaxamaxan/datamapper
PHP | 6633 lines | 3292 code | 787 blank | 2554 comment | 585 complexity | cc25fd5f1ce17100e8bc877f23d5d286 MD5 | raw file
Possible License(s): MIT

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

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

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