PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/application/libraries/datamapper.php

https://bitbucket.org/matyhaty/senses-designertravelv3
PHP | 6771 lines | 3319 code | 813 blank | 2639 comment | 583 complexity | b26c3d0f94fa832753d1148229f88858 MD5 | raw file
Possible License(s): LGPL-2.1, 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.2
  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.2');
  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 (property_exists($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 = strtolower(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. static $CI = NULL;
  707. // get the CI instance
  708. is_null($CI) AND $CI =& get_instance();
  709. // Don't attempt to autoload CI_ , EE_, or custom prefixed classes
  710. if (in_array(substr($class, 0, 3), array('CI_', 'EE_')) OR strpos($class, $CI->config->item('subclass_prefix')) === 0)
  711. {
  712. return;
  713. }
  714. // Prepare class
  715. $class = strtolower($class);
  716. // Prepare path
  717. $paths = array();
  718. if (method_exists($CI->load, 'get_package_paths'))
  719. {
  720. // use CI 2.0 loader's model paths
  721. $paths = $CI->load->get_package_paths(false);
  722. }
  723. //else
  724. // {
  725. // $this->load->add_package_path(APPPATH.'modules/'.$class.'/');
  726. // $paths = $CI->load->get_package_paths(false);
  727. // }
  728. foreach (array_merge(array(APPPATH),$paths, self::$model_paths) as $path)
  729. {
  730. // Prepare file
  731. $file = $path . 'models/' . $class . EXT;
  732. // Check if file exists, require_once if it does
  733. if (file_exists($file))
  734. {
  735. require_once($file);
  736. break;
  737. }
  738. }
  739. // if class not loaded, do a recursive search of model paths for the class
  740. if (! class_exists($class))
  741. {
  742. foreach($paths as $path)
  743. {
  744. $found = DataMapper::recursive_require_once($class, $path . 'models');
  745. if($found)
  746. {
  747. break;
  748. }
  749. }
  750. }
  751. }
  752. // --------------------------------------------------------------------
  753. /**
  754. * Add Model Path
  755. *
  756. * Manually add paths for the model autoloader
  757. *
  758. * @param mixed $paths path or array of paths to search
  759. */
  760. public static function add_model_path($paths)
  761. {
  762. // make sure paths is an array
  763. is_array($paths) OR $paths = array($paths);
  764. foreach($paths as $path)
  765. {
  766. $path = rtrim($path, '/') . '/';
  767. if ( is_dir($path.'models') && ! in_array($path, self::$model_paths))
  768. {
  769. self::$model_paths[] = $path;
  770. }
  771. }
  772. }
  773. // --------------------------------------------------------------------
  774. /**
  775. * Recursive Require Once
  776. *
  777. * Recursively searches the path for the class, require_once if found.
  778. *
  779. * @param string $class Name of class to look for
  780. * @param string $path Current path to search
  781. */
  782. protected static function recursive_require_once($class, $path)
  783. {
  784. $found = FALSE;
  785. if(is_dir($path))
  786. {
  787. $handle = opendir($path);
  788. if ($handle)
  789. {
  790. while (FALSE !== ($dir = readdir($handle)))
  791. {
  792. // If dir does not contain a dot
  793. if (strpos($dir, '.') === FALSE)
  794. {
  795. // Prepare recursive path
  796. $recursive_path = $path . '/' . $dir;
  797. // Prepare file
  798. $file = $recursive_path . '/' . $class . EXT;
  799. // Check if file exists, require_once if it does
  800. if (file_exists($file))
  801. {
  802. require_once($file);
  803. $found = TRUE;
  804. break;
  805. }
  806. else if (is_dir($recursive_path))
  807. {
  808. // Do a recursive search of the path for the class
  809. DataMapper::recursive_require_once($class, $recursive_path);
  810. }
  811. }
  812. }
  813. closedir($handle);
  814. }
  815. }
  816. return $found;
  817. }
  818. // --------------------------------------------------------------------
  819. /**
  820. * Loads in any extensions used by this class or globally.
  821. *
  822. * @param array $extensions List of extensions to add to.
  823. * @param array $name List of new extensions to load.
  824. */
  825. protected static function _load_extensions(&$extensions, $names)
  826. {
  827. static $CI = NULL;
  828. // get the CI instance
  829. is_null($CI) AND $CI =& get_instance();
  830. $class_prefixes = array(
  831. 0 => 'DMZ_',
  832. 1 => 'DataMapper_',
  833. 2 => $CI->config->item('subclass_prefix'),
  834. 3 => 'CI_'
  835. );
  836. foreach($names as $name => $options)
  837. {
  838. if( ! is_string($name))
  839. {
  840. $name = $options;
  841. $options = NULL;
  842. }
  843. // only load an extension if it wasn't already loaded in this context
  844. if(isset($extensions[$name]))
  845. {
  846. return;
  847. }
  848. if( ! isset($extensions['_methods']))
  849. {
  850. $extensions['_methods'] = array();
  851. }
  852. // determine the file name and class name
  853. $file = DataMapper::$config['extensions_path'] . '/' . $name . EXT;
  854. if ( ! file_exists($file))
  855. {
  856. if(strpos($name, '/') === FALSE)
  857. {
  858. $file = APPPATH . DataMapper::$config['extensions_path'] . '/' . $name . EXT;
  859. $ext = $name;
  860. }
  861. else
  862. {
  863. $file = APPPATH . $name . EXT;
  864. $ext = array_pop(explode('/', $name));
  865. }
  866. if(!file_exists($file))
  867. {
  868. show_error('DataMapper Error: loading extension ' . $name . ': File not found.');
  869. }
  870. }
  871. // load class
  872. include_once($file);
  873. // Allow for DMZ_Extension, DataMapper_Extension, etc.
  874. foreach($class_prefixes as $index => $prefix)
  875. {
  876. if(class_exists($prefix.$ext))
  877. {
  878. if($index == 2) // "MY_"
  879. {
  880. // Load in the library this class is based on
  881. $CI->load->library($ext);
  882. }
  883. $ext = $prefix.$ext;
  884. break;
  885. }
  886. }
  887. if(!class_exists($ext))
  888. {
  889. show_error("DataMapper Error: Unable to find a class for extension $name.");
  890. }
  891. // create class
  892. if(is_null($options))
  893. {
  894. $o = new $ext(NULL, isset($this) ? $this : NULL);
  895. }
  896. else
  897. {
  898. $o = new $ext($options, isset($this) ? $this : NULL);
  899. }
  900. $extensions[$name] = $o;
  901. // figure out which methods can be called on this class.
  902. $methods = get_class_methods($ext);
  903. foreach($methods as $m)
  904. {
  905. // do not load private methods or methods already loaded.
  906. if($m[0] !== '_' &&
  907. is_callable(array($o, $m)) &&
  908. ! isset($extensions['_methods'][$m])
  909. ) {
  910. // store this method.
  911. $extensions['_methods'][$m] = $name;
  912. }
  913. }
  914. }
  915. }
  916. // --------------------------------------------------------------------
  917. /**
  918. * Loads the extensions that are local to this model.
  919. * @param string $common_key Shared key to save extenions to.
  920. */
  921. private function _initiate_local_extensions($common_key)
  922. {
  923. if(!empty($this->extensions))
  924. {
  925. $extensions = $this->extensions;
  926. $this->extensions = array();
  927. DataMapper::_load_extensions($this->extensions, $extensions);
  928. }
  929. else
  930. {
  931. // ensure an empty array
  932. $this->extensions = array('_methods' => array());
  933. }
  934. // bind to the shared key, for dynamic loading
  935. DataMapper::$common[$common_key]['extensions'] =& $this->extensions;
  936. }
  937. // --------------------------------------------------------------------
  938. /**
  939. * Dynamically load an extension when needed.
  940. * @param object $name Name of the extension (or array of extensions).
  941. * @param array $options Options for the extension
  942. * @param boolean $local If TRUE, only loads the extension into this object
  943. */
  944. public function load_extension($name, $options = NULL, $local = FALSE)
  945. {
  946. if( ! is_array($name))
  947. {
  948. if( ! is_null($options))
  949. {
  950. $name = array($name => $options);
  951. }
  952. else
  953. {
  954. $name = array($name);
  955. }
  956. }
  957. // called individually to ensure that the array is modified directly
  958. // (and not copied instead)
  959. if($local)
  960. {
  961. DataMapper::_load_extensions($this->extensions, $name);
  962. }
  963. else
  964. {
  965. DataMapper::_load_extensions(DataMapper::$global_extensions, $name);
  966. }
  967. }
  968. // --------------------------------------------------------------------
  969. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  970. * *
  971. * Magic methods *
  972. * *
  973. * The following are methods to override the default PHP behaviour. *
  974. * *
  975. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  976. // --------------------------------------------------------------------
  977. /**
  978. * Magic Get
  979. *
  980. * Returns the value of the named property.
  981. * If named property is a related item, instantiate it first.
  982. *
  983. * This method also instantiates the DB object and the form_validation
  984. * objects as necessary
  985. *
  986. * @ignore
  987. * @param string $name Name of property to look for
  988. * @return mixed
  989. */
  990. public function __get($name)
  991. {
  992. static $CI = NULL;
  993. // get the CI instance
  994. is_null($CI) AND $CI =& get_instance();
  995. // We dynamically get DB when needed, and create a copy.
  996. // This allows multiple queries to be generated at the same time.
  997. if($name == 'db')
  998. {
  999. if($this->db_params === FALSE)
  1000. {
  1001. if ( ! isset($CI->db) || ! is_object($CI->db) || ! isset($CI->db->dbdriver) )
  1002. {
  1003. show_error('DataMapper Error: CodeIgniter database library not loaded.');
  1004. }
  1005. $this->db =& $CI->db;
  1006. }
  1007. else
  1008. {
  1009. if($this->db_params == '' || $this->db_params === TRUE)
  1010. {
  1011. if ( ! isset($CI->db) || ! is_object($CI->db) || ! isset($CI->db->dbdriver) )
  1012. {
  1013. show_error('DataMapper Error: CodeIgniter database library not loaded.');
  1014. }
  1015. // ensure the shared DB is disconnected, even if the app exits uncleanly
  1016. if(!isset($CI->db->_has_shutdown_hook))
  1017. {
  1018. register_shutdown_function(array($CI->db, 'close'));
  1019. $CI->db->_has_shutdown_hook = TRUE;
  1020. }
  1021. // clone, so we don't create additional connections to the DB
  1022. $this->db = clone($CI->db);
  1023. $this->db->dm_call_method('_reset_select');
  1024. }
  1025. else
  1026. {
  1027. // connecting to a different database, so we *must* create additional copies.
  1028. // It is up to the developer to close the connection!
  1029. $this->db = $CI->load->database($this->db_params, TRUE, TRUE);
  1030. }
  1031. // these items are shared (for debugging)
  1032. if(is_object($CI->db) && isset($CI->db->dbdriver))
  1033. {
  1034. $this->db->queries =& $CI->db->queries;
  1035. $this->db->query_times =& $CI->db->query_times;
  1036. }
  1037. }
  1038. // ensure the created DB is disconnected, even if the app exits uncleanly
  1039. if(!isset($this->db->_has_shutdown_hook))
  1040. {
  1041. register_shutdown_function(array($this->db, 'close'));
  1042. $this->db->_has_shutdown_hook = TRUE;
  1043. }
  1044. return $this->db;
  1045. }
  1046. // Special case to get form_validation when first accessed
  1047. if($name == 'form_validation')
  1048. {
  1049. if ( ! isset($this->form_validation) )
  1050. {
  1051. if( ! isset($CI->form_validation))
  1052. {
  1053. $CI->load->library('form_validation');
  1054. $this->lang->load('form_validation');
  1055. }
  1056. $this->form_validation =& $CI->form_validation;
  1057. }
  1058. return $this->form_validation;
  1059. }
  1060. $has_many = isset($this->has_many[$name]);
  1061. $has_one = isset($this->has_one[$name]);
  1062. // If named property is a "has many" or "has one" related item
  1063. if ($has_many || $has_one)
  1064. {
  1065. $related_properties = $has_many ? $this->has_many[$name] : $this->has_one[$name];
  1066. // Instantiate it before accessing
  1067. $class = $related_properties['class'];
  1068. $this->{$name} = new $class();
  1069. // Store parent data
  1070. $this->{$name}->parent = array('model' => $related_properties['other_field'], 'id' => $this->id);
  1071. // Check if Auto Populate for "has many" or "has one" is on
  1072. // (but only if this object exists in the DB, and we aren't instantiating)
  1073. if ($this->exists() &&
  1074. ($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'])))
  1075. {
  1076. $this->{$name}->get();
  1077. }
  1078. return $this->{$name};
  1079. }
  1080. $name_single = singular($name);
  1081. if($name_single !== $name) {
  1082. // possibly return single form of name
  1083. $test = $this->{$name_single};
  1084. if(is_object($test)) {
  1085. return $test;
  1086. }
  1087. }
  1088. return NULL;
  1089. }
  1090. // --------------------------------------------------------------------
  1091. /**
  1092. * Used several places to temporarily override the auto_populate setting
  1093. * @ignore
  1094. * @param string $related Related Name
  1095. * @return DataMapper|NULL
  1096. */
  1097. private function &_get_without_auto_populating($related)
  1098. {
  1099. $b_many = $this->auto_populate_has_many;
  1100. $b_one = $this->auto_populate_has_one;
  1101. $this->auto_populate_has_many = FALSE;
  1102. $this->auto_populate_has_one = FALSE;
  1103. $ret =& $this->{$related};
  1104. $this->auto_populate_has_many = $b_many;
  1105. $this->auto_populate_has_one = $b_one;
  1106. return $ret;
  1107. }
  1108. // --------------------------------------------------------------------
  1109. /**
  1110. * Magic Call
  1111. *
  1112. * Calls special methods, or extension methods.
  1113. *
  1114. * @ignore
  1115. * @param string $method Method name
  1116. * @param array $arguments Arguments to method
  1117. * @return mixed
  1118. */
  1119. public function __call($method, $arguments)
  1120. {
  1121. // List of watched method names
  1122. // NOTE: order matters: make sure more specific items are listed before
  1123. // less specific items
  1124. static $watched_methods = array(
  1125. 'save_', 'delete_',
  1126. 'get_by_related_', 'get_by_related', 'get_by_',
  1127. '_related_subquery', '_subquery',
  1128. '_related_', '_related',
  1129. '_join_field',
  1130. '_field_func', '_func'
  1131. );
  1132. $ext = NULL;

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