PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/application/libraries/datamapper.php

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

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