PageRenderTime 63ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 1ms

/application/libraries/datamapper.php

https://github.com/aspleenic/Codeigniter-Egypt
PHP | 6788 lines | 3337 code | 807 blank | 2644 comment | 588 complexity | b705400f6c55742ebae0c6f17b224440 MD5 | raw file
Possible License(s): GPL-2.0

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

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