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

/fuel/modules/fuel/core/MY_Model.php

https://github.com/jamiegrand/FUEL-CMS
PHP | 5848 lines | 3047 code | 628 blank | 2173 comment | 491 complexity | c2aa7e239a44d91c712ed7c81bd6d4f1 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * FUEL CMS
  4. * http://www.getfuelcms.com
  5. *
  6. * An open source Content Management System based on the
  7. * Codeigniter framework (http://codeigniter.com)
  8. *
  9. * @package FUEL CMS
  10. * @author David McReynolds @ Daylight Studio
  11. * @copyright Copyright (c) 2014, Run for Daylight LLC.
  12. * @license http://docs.getfuelcms.com/general/license
  13. * @link http://www.getfuelcms.com
  14. */
  15. // ------------------------------------------------------------------------
  16. /**
  17. * MY_Model class
  18. *
  19. * An extension of the Model class to map data operations to a table.
  20. * Depends upon the Validator library, date helper and the string helper.
  21. *
  22. * Inspired from this post here Developer13:
  23. * http://codeigniter.com/forums/viewthread/88769/
  24. *
  25. * @package FUEL CMS
  26. * @subpackage Libraries
  27. * @category Libraries
  28. * @author David McReynolds @ Daylight Studio
  29. * @link http://docs.getfuelcms.com/libraries/my_model
  30. * @prefix $this->example_model->
  31. */
  32. require_once(FUEL_PATH.'libraries/Validator.php');
  33. class MY_Model extends CI_Model {
  34. public $auto_validate = TRUE; // use auto-validation before saving
  35. public $return_method = 'auto'; // object, array, query, auto
  36. // fields to auto validate
  37. public $auto_validate_fields = array(
  38. 'email|email_address' => 'valid_email',
  39. 'phone|phone_number' => 'valid_phone'
  40. );
  41. public $required = array(); // an array of required fields. If a key => val is provided, the key is name of the field and the value is the error message to display
  42. public $default_required_message = "Please fill out the required field '%1s'"; // the default required validator message
  43. public $auto_date_add = array('date_added', 'entry_date'); // field names to automatically set the date when the value is NULL
  44. public $auto_date_update = array('last_modified', 'last_updated'); // field names to automatically set the date on updates
  45. public $date_use_gmt = FALSE; // determines whether to use GMT time when storing dates and times
  46. public $default_date = 0; // default date value that get's passed to the model on save. Using 0000-00-00 will not work if it is a required field since it is not seen as an empty value
  47. public $auto_trim = TRUE; // will trim on clean
  48. public $auto_encode_entities = TRUE; // determines whether to automatically encode html entities. An array can be set instead of a boolean value for the names of the fields to perform the safe_htmlentities on
  49. public $xss_clean = FALSE; // determines whether automatically run the xss_clean. An array can be set instead of a boolean value for the names of the fields to perform the xss_clean on
  50. public $readonly = FALSE; // sets the model to readonly mode where you can't save or delete data
  51. public $hidden_fields = array(); // fields to hide when creating a form
  52. public $unique_fields = array(); // fields that are not IDs but are unique. Can also be an array of arrays for compound keys
  53. public $linked_fields = array(); // fields that are linked meaning one value helps to determine another. Key is the field, value is a function name to transform it. (e.g. array('slug' => 'title'), or array('slug' => arry('name' => 'strtolower')));
  54. public $serialized_fields = array(); // fields that contain serialized data. This will automatically serialize before saving and unserialize data upon retrieving
  55. public $default_serialization_method = 'json'; // the default serialization method. Options are 'json' and 'serialize'
  56. public $boolean_fields = array(); // fields that are tinyint and should be treated as boolean
  57. public $suffix = '_model'; // the suffix used for the data record class
  58. public $foreign_keys = array(); // map foreign keys to table models
  59. public $has_many = array(); // keys are model, which can be a key value pair with the key being the module and the value being the model, module (if not specified in model parameter), relationships_model, foreign_key, candidate_key
  60. public $belongs_to = array(); // keys are model, which can be a key value pair with the key being the module and the value being the model, module (if not specified in model parameter), relationships_model, foreign_key, candidate_key
  61. public $representatives = array(); // an array of fields that have arrays or regular expression values to match against different field types (e.g. 'number'=>'bigint|smallint|tinyint|int')
  62. public $custom_fields = array(); // an array of field names/types that map to a specific class
  63. public $formatters = array(); // an array of helper formatter functions related to a specific field type (e.g. string, datetime, number), or name (e.g. title, content) that can augment field results
  64. protected $db; // CI database object
  65. protected $table_name; // the table name to associate the model with
  66. protected $key_field = 'id'; // usually the tables primary key(s)... can be an array if compound key
  67. protected $normalized_save_data = NULL; // the saved data before it is cleaned
  68. protected $cleaned_data = NULL; // data after it is cleaned
  69. protected $dsn = ''; // the DSN string to connect to the database... if blank it will pull in from database config file
  70. protected $has_auto_increment = TRUE; // does the table have auto_increment?
  71. protected $record_class = ''; // the name of the record class (if it can't be determined)
  72. protected $friendly_name = ''; // a friendlier name of the group of objects
  73. protected $singular_name = ''; // a friendly singular name of the object
  74. protected $rules = array(); // validation rules
  75. protected $fields = array(); // fields in the table
  76. protected $use_common_query = TRUE; // include the _common_query method for each query
  77. protected $validator = NULL; // the validator object
  78. protected $clear_related_on_save = 'AUTO'; // clears related records before saving
  79. protected $_tables = array(); // an array of table names with the key being the alias and the value being the actual table
  80. /**
  81. * Constructor - Sets MY_Model preferences
  82. *
  83. * The constructor can be passed an array of config values
  84. */
  85. public function __construct($table = NULL, $params = array())
  86. {
  87. parent::__construct();
  88. $this->load->helper('string');
  89. $this->load->helper('date');
  90. $this->load->helper('security');
  91. $this->load->helper('inflector');
  92. $this->load->helper('language');
  93. $this->load->module_language(FUEL_FOLDER, 'model');
  94. $this->initialize($table, $params);
  95. }
  96. // --------------------------------------------------------------------
  97. /**
  98. * Initialize the user preferences
  99. *
  100. * Accepts an associative array as input, containing display preferences
  101. *
  102. * @access public
  103. * @param string the table name
  104. * @param array config preferences
  105. * @return void
  106. */
  107. public function initialize($table = NULL, $params = array())
  108. {
  109. if (!empty($table))
  110. {
  111. $this->table_name = $table;
  112. }
  113. else
  114. {
  115. $this->table_name = strtolower(get_class($this));
  116. }
  117. if (!empty($params))
  118. {
  119. foreach ($params as $key => $val)
  120. {
  121. if (isset($this->$key))
  122. {
  123. $this->$key = $val;
  124. }
  125. }
  126. }
  127. // if a DSN property is set,then we will load that database in
  128. if (!empty($this->dsn))
  129. {
  130. $this->db = $this->load->database($this->dsn, TRUE, TRUE);
  131. }
  132. else
  133. {
  134. // else we use the database set on the CI object
  135. if (empty($this->db))
  136. {
  137. $this->load->database($this->dsn);
  138. }
  139. $CI =& get_instance();
  140. if (isset($CI->db))
  141. {
  142. // create a copy of the DB object to prevent cross model interference
  143. unset($this->db);
  144. $this->db = clone $CI->db;
  145. }
  146. else
  147. {
  148. $CI->load->language('db');
  149. show_error(lang('db_unable_to_connect'));
  150. }
  151. }
  152. $this->validator = new Validator();
  153. $this->validator->register_to_global_errors = FALSE;
  154. // load any additional classes needed for custom fields
  155. $this->load_custom_field_classes();
  156. }
  157. // --------------------------------------------------------------------
  158. /**
  159. * Returns the database object
  160. *
  161. <code>
  162. $db = $this->examples_model->db();
  163. </code>
  164. *
  165. * @access public
  166. * @return array
  167. */
  168. public function &db()
  169. {
  170. //$this->_check_readonly();
  171. return $this->db;
  172. }
  173. // --------------------------------------------------------------------
  174. /**
  175. * Gets the short name minus the suffix
  176. *
  177. <code>
  178. echo $this->examples_model->short_name(TRUE);
  179. // examples
  180. </code>
  181. *
  182. * @access public
  183. * @param boolean lower case the name (optional)
  184. * @param boolean return the record clas name (optional)
  185. * @return array
  186. */
  187. public function short_name($lower = FALSE, $record_class = FALSE)
  188. {
  189. $class_name = ($record_class) ? $this->record_class_name() : get_class($this);
  190. $end_index = strlen($class_name) - strlen($this->suffix);
  191. $short_name = substr($class_name, 0, $end_index);
  192. if ($lower)
  193. {
  194. return strtolower($short_name);
  195. }
  196. else
  197. {
  198. return $short_name;
  199. }
  200. }
  201. // --------------------------------------------------------------------
  202. /**
  203. * Gets the name of the model object. By default it will be the same as the short_name(FALSE, FALSE) if no "friendly_name" value is specfied on the model
  204. *
  205. <code>
  206. echo $this->examples_model->friendly_name(TRUE);
  207. // examples
  208. </code>
  209. *
  210. * @access public
  211. * @param boolean lower case the name (optional)
  212. * @return array
  213. */
  214. public function friendly_name($lower = FALSE)
  215. {
  216. if (!empty($this->friendly_name))
  217. {
  218. if ($lower)
  219. {
  220. return strtolower($this->friendly_name);
  221. }
  222. return $this->friendly_name;
  223. }
  224. $friendly_name = $this->short_name($lower, FALSE);
  225. $friendly_name = ucfirst(str_replace('_', ' ', $friendly_name));
  226. return $friendly_name;
  227. }
  228. // --------------------------------------------------------------------
  229. /**
  230. * Gets the singular name of the model object. By default it will be the same as the short_name(FALSE, TRUE) if no "singular_name" value is specfied on the model
  231. *
  232. <code>
  233. echo $this->examples_model->singular_name(TRUE);
  234. // example
  235. </code>
  236. *
  237. * @access public
  238. * @param boolean lower case the name (optional)
  239. * @return array
  240. */
  241. public function singular_name($lower = FALSE)
  242. {
  243. if (!empty($this->singular_name))
  244. {
  245. if ($lower)
  246. {
  247. return strtolower($this->singular_name);
  248. }
  249. return $this->singular_name;
  250. }
  251. $singular_name = $this->short_name($lower, TRUE);
  252. $singular_name = ucfirst(str_replace('_', ' ', $singular_name));
  253. return $singular_name;
  254. }
  255. // --------------------------------------------------------------------
  256. /**
  257. * Get the table name
  258. *
  259. <code>
  260. echo $this->examples_model->table_name();
  261. // examples
  262. </code>
  263. *
  264. * @access public
  265. * @return array
  266. */
  267. public function table_name()
  268. {
  269. return $this->table_name;
  270. }
  271. // --------------------------------------------------------------------
  272. /**
  273. * Sets the aliases to table(s) that you can use in your queries
  274. *
  275. <code>
  276. $my_tables = array('mytable' => 'my_table');
  277. $this->examples_model->set_tables($my_tables);
  278. </code>
  279. *
  280. * @access public
  281. * @param array an array of tables
  282. * @return void
  283. */
  284. public function set_tables($tables)
  285. {
  286. $this->_tables = array_merge($this->_tables, $tables);
  287. }
  288. // --------------------------------------------------------------------
  289. /**
  290. * Gets the table(s) name based on the configuration
  291. *
  292. <code>
  293. $table_name = $this->examples_model->tables('my_table');
  294. </code>
  295. *
  296. * @access public
  297. * @param string the table name (optional)
  298. * @return string
  299. */
  300. public function tables($table = NULL)
  301. {
  302. if (!empty($table))
  303. {
  304. if (isset($this->_tables[$table]))
  305. {
  306. return $this->_tables[$table];
  307. }
  308. else
  309. {
  310. return NULL;
  311. }
  312. }
  313. else
  314. {
  315. return $this->_tables;
  316. }
  317. return NULL;
  318. }
  319. // --------------------------------------------------------------------
  320. /**
  321. * Get the key field(s)
  322. *
  323. <code>
  324. $fields = $this->examples_model->key_field();
  325. </code>
  326. *
  327. * @access public
  328. * @return array
  329. */
  330. public function key_field()
  331. {
  332. return $this->key_field;
  333. }
  334. // --------------------------------------------------------------------
  335. /**
  336. * Get the fields of the table
  337. *
  338. <code>
  339. $fields = $this->examples_model->fields();
  340. foreach($fields as $field)
  341. {
  342. echo $field; // field name
  343. }
  344. </code>
  345. *
  346. * @access public
  347. * @return array
  348. */
  349. public function fields()
  350. {
  351. if (empty($this->fields)) $this->fields = $this->db->list_fields($this->table_name);
  352. return $this->fields;
  353. }
  354. // --------------------------------------------------------------------
  355. /**
  356. * Get the results of the query
  357. *
  358. <code>
  359. $rows = $this->examples_model->get(TRUE, 'object', FALSE);
  360. foreach($rows->result() as $row)
  361. {
  362. echo $row->name;
  363. }
  364. // The third parameter is the column name to be used as the array key value (if <dfn>$force_array</dfn> is set to <dfn>TRUE</dfn>)
  365. $rows = $this->examples_model->get(TRUE, 'object', 'id');
  366. foreach($rows->result() as $id => $row)
  367. {
  368. echo $id;
  369. }
  370. </code>
  371. *
  372. * @access public
  373. * @param boolean return multiple records (optional)
  374. * @param string method return type (object, array, query, auto) (optional)
  375. * @param string the column to use for an associative key array (optional)
  376. * @param boolean determine whether to use the _common_query method in the query (optional)
  377. * @return array
  378. */
  379. public function get($force_array = TRUE, $return_method = NULL, $assoc_key = NULL, $use_common_query = NULL){
  380. if (!empty($this->return_method) AND empty($return_method)) $return_method = $this->return_method;
  381. //$this->fields();
  382. if (!isset($use_common_query)) $use_common_query = $this->use_common_query;
  383. // common query if exists
  384. if (method_exists($this, '_common_query') AND $use_common_query)
  385. {
  386. $this->_common_query();
  387. }
  388. if (empty($this->db->ar_select))
  389. {
  390. $this->db->select($this->table_name.'.*'); // make select table specific
  391. }
  392. //Get the data out of the database
  393. $query = $this->db->get($this->table_name);
  394. if (empty($query)) $query = new MY_DB_mysql_result();
  395. if ($this->return_method == 'query')
  396. {
  397. return $query;
  398. }
  399. if ($return_method == 'array' OR !class_exists($this->record_class_name()))
  400. {
  401. if ($return_method == 'object')
  402. {
  403. $result_objects = (!empty($assoc_key)) ? $query->result_assoc($assoc_key) : $query->result() ;
  404. }
  405. else
  406. {
  407. $result_objects = (!empty($assoc_key)) ? $query->result_assoc_array($assoc_key) : $query->result_array();
  408. }
  409. $this->last_data_set = new Data_set($result_objects, $force_array);
  410. }
  411. else
  412. {
  413. $result_objects = $this->map_query_records($query, $assoc_key);
  414. $this->last_data_set = new Data_set($result_objects, $force_array);
  415. }
  416. $query->free_result();
  417. //This array holds all result data
  418. return $this->last_data_set;
  419. }
  420. // --------------------------------------------------------------------
  421. /**
  422. * Maps a query result object to an array of record objects
  423. *
  424. <code>
  425. ...
  426. $query = $this->db->query('SELECT * FROM USERS');
  427. $users = $this->examples_model->map_query_records($query, 'id');
  428. foreach($users as $id => $user)
  429. {
  430. echo $user->name;
  431. }
  432. </code>
  433. *
  434. * @access public
  435. * @param object the query object
  436. * @param string the field name to be used the key value (optional)
  437. * @return array
  438. */
  439. public function map_query_records($query, $assoc_key = NULL)
  440. {
  441. $result = $query->result_array();
  442. $result_objects = array();
  443. if (!empty($result))
  444. {
  445. foreach ($result as $row)
  446. {
  447. $record = $this->map_to_record_class($row);
  448. if (!empty($assoc_key))
  449. {
  450. $result_objects[$row[$assoc_key]] = $record;
  451. }
  452. else
  453. {
  454. $result_objects[] = $record;
  455. }
  456. }
  457. }
  458. return $result_objects;
  459. }
  460. // --------------------------------------------------------------------
  461. /**
  462. * Maps an associative record array to a record object
  463. *
  464. <code>
  465. $my_user['id'] = 1;
  466. $my_user['name'] = 'Darth Vader';
  467. $my_user['email'] = 'darth@deathstar.com';
  468. $my_custom_record = $this->examples_model->map_to_record_class($my_user);
  469. echo $my_custom_record->name;
  470. </code>
  471. *
  472. * @access public
  473. * @param array field values
  474. * @param array all the fields available for the object (optional)
  475. * @return array
  476. */
  477. public function map_to_record_class($row, $fields = NULL)
  478. {
  479. if (empty($fields))
  480. {
  481. $fields = array_keys($row);
  482. }
  483. $record_class = $this->record_class_name();
  484. $record = new $record_class();
  485. $record->initialize($this, $fields);
  486. $record->fill($row);
  487. return $record;
  488. }
  489. // --------------------------------------------------------------------
  490. /**
  491. * Get the results of the query
  492. *
  493. <code>
  494. $examples = $this->examples_model->find_all(array('published' => 'yes'), 'date_added desc');
  495. </code>
  496. *
  497. * @access public
  498. * @param string the type of find to perform. Options are "key", "one", "options", "all" and find_"{your_method}". By default it will perform a find_all (optional)
  499. * @param mixed an array or string containg the where paramters of a query (optional)
  500. * @param string the order by of the query (optional)
  501. * @param int the number of records to limit in the results (optional)
  502. * @param int the offset value for the results (optional)
  503. * @param string return type (object, array, query, auto) (optional)
  504. * @param string the column to use for an associative key array (optional)
  505. * @return array
  506. */
  507. public function find($find = 'all', $where = NULL, $order = NULL, $limit = NULL, $offset = NULL, $return_method = NULL, $assoc_key = NULL)
  508. {
  509. // allows for just a single parameter of arrays to be passed
  510. if (is_array($find))
  511. {
  512. extract($find);
  513. }
  514. $data = array();
  515. if ($find === 'key')
  516. {
  517. $data = $this->find_by_key($where, $return_method);
  518. }
  519. else if ($find === 'one')
  520. {
  521. $data = $this->find_one($where, $order, $return_method);
  522. }
  523. else if ($find === 'options')
  524. {
  525. $data = $this->options_list(NULL, NULL, $where, $order);
  526. }
  527. else
  528. {
  529. if (empty($find) OR $find == 'all')
  530. {
  531. $data = $this->find_all($where, $order, $limit, $offset, $return_method, $assoc_key);
  532. }
  533. else
  534. {
  535. $method = 'find_'.$find;
  536. if (is_callable(array($this, $method)))
  537. {
  538. $args = func_get_args();
  539. array_shift($args);
  540. $data = call_user_func_array(array($this, $method), $args);
  541. }
  542. else
  543. {
  544. return FALSE;
  545. }
  546. }
  547. }
  548. return $data;
  549. }
  550. // --------------------------------------------------------------------
  551. /**
  552. * Get one record result based on the key value
  553. *
  554. <code>
  555. $id = 1;
  556. $example = $this->examples_model->find_by_key($id, 'object');
  557. </code>
  558. *
  559. * @access public
  560. * @param string the key value to find a single record
  561. * @param mixed return type (object, array, query, auto) (optional)
  562. * @return array
  563. */
  564. public function find_by_key($key_val, $return_method = NULL)
  565. {
  566. $where = array();
  567. if (is_array($key_val))
  568. {
  569. $key_field = (array) $this->key_field;
  570. foreach($key_field as $val)
  571. {
  572. if (is_array($key_val))
  573. {
  574. foreach($key_val as $key2 => $val2)
  575. {
  576. if ($key2 == $val)
  577. {
  578. $where[$val] = $val2;
  579. }
  580. }
  581. }
  582. }
  583. }
  584. else
  585. {
  586. $where[$this->table_name.'.'.$this->key_field] = $key_val;
  587. }
  588. return $this->find_one($where, NULL, $return_method);
  589. }
  590. // --------------------------------------------------------------------
  591. /**
  592. * Get one record result
  593. *
  594. <code>
  595. $example = $this->examples_model->find_one(array('published' => 'yes'), ''asc');
  596. </code>
  597. *
  598. * @access public
  599. * @param mixed an array or string containg the where paramters of a query (optional)
  600. * @param string the order by of the query (optional)
  601. * @param string return type (object, array, query, auto) (optional)
  602. * @return array
  603. */
  604. public function find_one($where = array(), $order_by = NULL, $return_method = NULL)
  605. {
  606. $where = $this->_safe_where($where);
  607. $this->_handle_where($where);
  608. if (!empty($order_by)) $this->db->order_by($order_by);
  609. $this->db->limit(1);
  610. $query = $this->get(FALSE, $return_method);
  611. if ($return_method == 'query') return $query;
  612. $data = $query->result();
  613. // unserialize any data
  614. if ($return_method == 'array')
  615. {
  616. $data = $this->unserialize_field_values($data);
  617. }
  618. return $data;
  619. }
  620. // --------------------------------------------------------------------
  621. /**
  622. * Get one record result as an array
  623. *
  624. <code>
  625. $examples = $this->examples_model->find_one_array(array('published' => 'yes'), 'date_added desc');
  626. </code>
  627. *
  628. * @access public
  629. * @param mixed an array or string containg the where paramters of a query
  630. * @param string the order by of the query (optional)
  631. * @return array
  632. */
  633. public function find_one_array($where, $order_by = NULL)
  634. {
  635. return $this->find_one($where, $order_by, 'array');
  636. }
  637. // --------------------------------------------------------------------
  638. /**
  639. * Get the results of the query
  640. *
  641. <code>
  642. $examples = $this->examples_model->find_all(array('published' => 'yes'), 'date_added desc');
  643. </code>
  644. *
  645. * @access public
  646. * @param mixed an array or string containg the where paramters of a query (optional)
  647. * @param string the order by of the query (optional)
  648. * @param int the number of records to limit in the results (optional)
  649. * @param int the offset value for the results (optional)
  650. * @param string return type (object, array, query, auto) (optional)
  651. * @param string the column to use for an associative key array (optional)
  652. * @return array
  653. */
  654. public function find_all($where = array(), $order_by = NULL, $limit = NULL, $offset = NULL, $return_method = NULL, $assoc_key = NULL)
  655. {
  656. $where = $this->_safe_where($where);
  657. $this->_handle_where($where);
  658. $params = array('order_by', 'limit', 'offset');
  659. foreach($params as $method)
  660. {
  661. if (!empty($$method)) $this->db->$method($$method);
  662. }
  663. $query = $this->get(TRUE, $return_method, $assoc_key);
  664. if ($return_method == 'query') return $query;
  665. $data = $query->result();
  666. // unserialize any data if the return method is an array. If it is a custom object, then we let the object take care of it
  667. if ($return_method == 'array')
  668. {
  669. $data = $this->unserialize_field_values($data);
  670. }
  671. return $data;
  672. }
  673. // --------------------------------------------------------------------
  674. /**
  675. * Get the results of the query as an array
  676. *
  677. <code>
  678. $examples = $this->examples_model->find_all_array(array('published' => 'yes'), 'date_added desc');
  679. </code>
  680. *
  681. * @access public
  682. * @param mixed an array or string containg the where paramters of a query (optional)
  683. * @param string the order by of the query (optional)
  684. * @param int the number of records to limit in the results (optional)
  685. * @param int the offset value for the results (optional)
  686. * @return array
  687. */
  688. public function find_all_array($where = array(), $order_by = NULL, $limit = NULL, $offset = NULL)
  689. {
  690. return $this->find_all($where, $order_by, $limit, $offset, 'array');
  691. }
  692. // --------------------------------------------------------------------
  693. /**
  694. * Get the results of the query returned as a keyed array of objects
  695. *
  696. <code>
  697. $examples = $this->examples_model->find_all_assoc(array('published' => 'yes'), 'date_added desc');
  698. </code>
  699. *
  700. * @access public
  701. * @param string the column to use for an associative key array (optional)
  702. * @param mixed an array or string containg the where paramters of a query (optional)
  703. * @param string the order by of the query (optional)
  704. * @param int the number of records to limit in the results (optional)
  705. * @param int the offset value for the results (optional)
  706. * @return array
  707. */
  708. public function find_all_assoc($assoc_key = 'id', $where = array(), $order_by = NULL, $limit = NULL, $offset = NULL)
  709. {
  710. return $this->find_all($where, $order_by, $limit, $offset, 'object', $assoc_key);
  711. }
  712. // --------------------------------------------------------------------
  713. /**
  714. * Get the results of the query returned as a keyed array of arrays
  715. *
  716. <code>
  717. $examples = $this->examples_model->find_all_assoc(array('published' => 'yes'), 'date_added desc');
  718. </code>
  719. *
  720. * @access public
  721. * @param string the column to use for an associative key array (optional)
  722. * @param mixed an array or string containg the where paramters of a query (optional)
  723. * @param string the order by of the query (optional)
  724. * @param int the number of records to limit in the results (optional)
  725. * @param int the offset value for the results (optional)
  726. * @return array
  727. */
  728. public function find_all_array_assoc($assoc_key = 'id', $where = array(), $order_by = NULL, $limit = NULL, $offset = NULL)
  729. {
  730. return $this->find_all($where, $order_by, $limit, $offset, 'array', $assoc_key);
  731. }
  732. // --------------------------------------------------------------------
  733. /**
  734. * Get the results of a query from within a select group of key field values. Results are sorted by the order from within the group.
  735. *
  736. <code>
  737. $examples = $this->examples_model->find_within(array(1, 2, 3, 4), array('published' => 'yes'), 'date_added desc');
  738. </code>
  739. *
  740. * @access public
  741. * @param group an array of keys to limit the search results to
  742. * @param mixed an array or string containg the where paramters of a query (optional)
  743. * @param int the number of records to limit in the results (optional)
  744. * @param int the offset value for the results (optional)
  745. * @param string return type (object, array, query, auto) (optional)
  746. * @param string the column to use for an associative key array (optional)
  747. * @return array
  748. */
  749. public function find_within($group, $where = array(), $limit = NULL, $offset = NULL, $return_method = NULL, $assoc_key = NULL)
  750. {
  751. if (empty($group) OR !is_array($group))
  752. {
  753. return array();
  754. }
  755. // setup wherein for the group
  756. $this->db->where_in($this->key_field(), $group);
  757. // must set protect identifiers to FALSE in order for order by to work
  758. $_protect_identifiers = $this->db->_protect_identifiers;
  759. $this->db->_protect_identifiers = FALSE;
  760. // escape group
  761. foreach($group as $key => $val)
  762. {
  763. $group[$key] = $this->db->escape($val);
  764. }
  765. // remove any cached order by
  766. $this->db->ar_cache_orderby = array();
  767. $this->db->order_by('FIELD('.$this->key_field().', '.implode(', ', $group).')');
  768. // set it _protect_identifiers back to original value
  769. $this->db->_protect_identifiers = $_protect_identifiers;
  770. // do a normal find all
  771. $data = $this->find_all($where, NULL, $limit, $offset, $return_method, $assoc_key);
  772. return $data;
  773. }
  774. // --------------------------------------------------------------------
  775. /**
  776. * This method takes an associative array with the key values that map to CodeIgniter active record methods and returns a query result object.
  777. *
  778. * For more advanced, use CI Active Record. Below are the key values you can pass:
  779. <ul>
  780. <li><strong>select</strong></li>
  781. <li><strong>from</strong></li>
  782. <li><strong>join</strong></li>
  783. <li><strong>where</strong></li>
  784. <li><strong>or_where</strong></li>
  785. <li><strong>where_in</strong></li>
  786. <li><strong>or_where_in</strong></li>
  787. <li><strong>where_not_in</strong></li>
  788. <li><strong>or_where_not_in</strong></li>
  789. <li><strong>like</strong></li>
  790. <li><strong>or_like</strong></li>
  791. <li><strong>not_like</strong></li>
  792. <li><strong>or_not_like</strong></li>
  793. <li><strong>group_by</strong></li>
  794. <li><strong>order_by</strong></li>
  795. <li><strong>limit</strong></li>
  796. <li><strong>offset</strong></li>
  797. </ul>
  798. *
  799. <code>
  800. $where['select'] = 'id, name, published';
  801. $where['where'] = array('published' => 'yes');
  802. $where['order_by'] = 'name asc';
  803. $where['limit'] = 10;
  804. $query = $this->examples_model->query($where);
  805. $results = $query->result();
  806. </code>
  807. *
  808. * @access public
  809. * @param array an array of parameters to create a query (optional)
  810. * @param boolean determines whether to execute the query and return the results or not
  811. * @return array
  812. */
  813. public function query($params = array(), $exec = TRUE)
  814. {
  815. if (is_array($params))
  816. {
  817. $defaults = array(
  818. 'select' => $this->table_name.'.*',
  819. 'from' => $this->table_name,
  820. 'join' => array(),
  821. 'where' => array(),
  822. 'or_where' => array(),
  823. 'where_in' => array(),
  824. 'or_where_in' => array(),
  825. 'where_not_in' => array(),
  826. 'or_where_not_in' => array(),
  827. 'like' => array(),
  828. 'or_like' => array(),
  829. 'not_like' => array(),
  830. 'or_not_like' => array(),
  831. 'group_by' => NULL,
  832. 'order_by' => NULL,
  833. 'limit' => NULL,
  834. 'offset' => NULL
  835. );
  836. $defaults2 = array(
  837. 'join',
  838. 'from',
  839. 'where',
  840. 'or_where',
  841. 'where_in',
  842. 'or_where_in',
  843. 'where_not_in',
  844. 'or_where_not_in',
  845. 'like',
  846. 'or_like',
  847. 'not_like',
  848. 'or_not_like'
  849. );
  850. // merge params with defaults
  851. $params = array_merge($defaults, $params);
  852. // add joins
  853. if (!empty($params['join'][0]))
  854. {
  855. $join_select = '';
  856. if (is_array($params['join'][0]))
  857. {
  858. foreach($params['join'] as $join)
  859. {
  860. if (empty($join[2]))
  861. {
  862. $join[2] = 'left';
  863. }
  864. $this->db->join($join[0], $join[1], $join[2]);
  865. $join_select .= ', '.$this->db->safe_select($join[0]);
  866. }
  867. }
  868. else
  869. {
  870. if (empty($params['join'][2]))
  871. {
  872. $params['join'][2] = 'left';
  873. }
  874. $this->db->join($params['join'][0], $params['join'][1], $params['join'][2]);
  875. $join_select .= ', '.$this->db->safe_select($params['join'][0]);
  876. }
  877. //if (empty($params['select'])) $params['select'] = $join_select;
  878. $params['select'] = $params['select'].$join_select;
  879. }
  880. // select
  881. if (!empty($params['select'])) $this->db->select($params['select'], FALSE);
  882. if (!empty($params['join_select'])) $this->db->select($join_select, FALSE);
  883. // from
  884. if ($params['from'] != $this->table_name)
  885. {
  886. $this->db->from($params['from']);
  887. }
  888. // loop through list above to set params
  889. foreach($defaults2 as $val)
  890. {
  891. if ($val == 'where_in' OR $val == 'or_where_in' OR $val == 'where_not_in' OR $val == 'or_where_not_in')
  892. {
  893. foreach($params[$val] as $key => $val2)
  894. {
  895. $this->db->$val($key, $val2);
  896. }
  897. }
  898. else if ($val != 'join' AND $val != 'from' AND $val != 'order_by')
  899. {
  900. if (!empty($params[$val]))
  901. {
  902. if (is_array($params[$val]))
  903. {
  904. $this->db->$val($params[$val]);
  905. }
  906. else
  907. {
  908. $this->db->$val($params[$val]);
  909. }
  910. }
  911. }
  912. }
  913. // group by
  914. if (!empty($params['group_by'])) $this->db->group_by($params['group_by']);
  915. //order by
  916. if (!empty($params['order_by']))
  917. {
  918. if (is_array($params['order_by']))
  919. {
  920. foreach($params['order_by'] as $val)
  921. {
  922. $order_by = explode(' ', trim($val));
  923. $this->db->order_by($order_by[0], $order_by[1]);
  924. }
  925. }
  926. else
  927. {
  928. $this->db->order_by($params['order_by']);
  929. }
  930. }
  931. if ( ! empty($params['limit']))
  932. {
  933. $this->db->limit($params['limit']);
  934. }
  935. if ( ! empty($params['offset']))
  936. {
  937. $this->db->offset($params['offset']);
  938. }
  939. if ($exec === FALSE)
  940. {
  941. return;
  942. }
  943. $results = $this->get();
  944. }
  945. else
  946. {
  947. $results = $this->get();
  948. }
  949. return $results;
  950. }
  951. // --------------------------------------------------------------------
  952. /**
  953. * Get the results of a query as an associative array... good for form option lists
  954. *
  955. <code>
  956. $where['published'] = 'yes';
  957. $order = 'name, desc';
  958. $examples_list = $this->examples_model->options_list('id', 'name', $where, $order);
  959. </code>
  960. *
  961. * @access public
  962. * @param string the column to use for the value (optional)
  963. * @param string the column to use for the label (optional)
  964. * @param mixed an array or string containg the where paramters of a query (optional)
  965. * @param mixed the order by of the query. Defaults to TRUE which means it will sort by $val asc (optional)
  966. * @return array
  967. */
  968. public function options_list($key = NULL, $val = NULL, $where = array(), $order = TRUE)
  969. {
  970. if (empty($key))
  971. {
  972. if (!is_array($this->key_field))
  973. {
  974. $key = $this->key_field;
  975. }
  976. }
  977. if (empty($val))
  978. {
  979. $fields = $this->fields();
  980. $val = $fields[1];
  981. }
  982. // don't need extra model sql stuff so just use normal active record'
  983. if (!empty($order) AND is_bool($order))
  984. {
  985. $this->db->order_by($val, 'asc');
  986. }
  987. else if (!empty($order) AND is_string($order))
  988. {
  989. if (strpos($order, ' ') === FALSE) $order .= ' asc';
  990. $this->db->order_by($order);
  991. }
  992. $this->db->select($key.', '.$val, FALSE);
  993. if (!empty($where))
  994. {
  995. $this->db->where($where);
  996. }
  997. $query = $this->db->get($this->table_name);
  998. $key_arr = explode('.', $key);
  999. $clean_key = $key_arr[(count($key_arr) - 1)];
  1000. if (!empty($query))
  1001. {
  1002. $results = $query->result_assoc_array($clean_key);
  1003. return $results;
  1004. }
  1005. return FALSE;
  1006. }
  1007. // --------------------------------------------------------------------
  1008. /**
  1009. * Determine if a record exists in the database
  1010. *
  1011. <code>
  1012. $where['type'] = 'A';
  1013. if ($this->examples_model->record_exists($where))
  1014. {
  1015. echo 'record exists';
  1016. }
  1017. </code>
  1018. *
  1019. * @access public
  1020. * @param mixed an array or string containg the where paramters of a query
  1021. * @return boolean
  1022. */
  1023. public function record_exists($where)
  1024. {
  1025. $query = $this->db->get_where($this->table_name, $where);
  1026. return ($query->num_rows() != 0);
  1027. }
  1028. // --------------------------------------------------------------------
  1029. /**
  1030. * Create a new record if a custom record object exists
  1031. *
  1032. <code>
  1033. $example = $this->examples_model->create($_POST); // Be sure to always clean your $_POST variables before using them
  1034. </code>
  1035. *
  1036. * @access public
  1037. * @param mixed the record oject associated with this class (optional)
  1038. * @return boolean
  1039. */
  1040. public function create($values = array())
  1041. {
  1042. $record_class = $this->record_class_name();
  1043. if (class_exists($record_class))
  1044. {
  1045. $record = new $record_class();
  1046. $record->initialize($this, $this->table_info());
  1047. // call on_create hook
  1048. $values = $this->on_create($values);
  1049. if (!empty($values)) $record->fill($values);
  1050. return $record;
  1051. }
  1052. else
  1053. {
  1054. throw new Exception(lang('error_could_not_find_record_class', get_class($this)));
  1055. }
  1056. }
  1057. // --------------------------------------------------------------------
  1058. /**
  1059. * Clean the data before saving
  1060. *
  1061. <code>
  1062. $cleaned_data = $this->examples_model->clean($_POST); // Be sure to always clean your $_POST variables before using them
  1063. </code>
  1064. *
  1065. * @access public
  1066. * @param mixed an array of values to be saved (optional)
  1067. * @param boolean run on_before_clean hook or not (optional)
  1068. * @return array
  1069. */
  1070. public function clean($values = array(), $run_hook = FALSE)
  1071. {
  1072. $CI =& get_instance();
  1073. if (empty($values)) $values = $CI->input->post();
  1074. $original_values = $values;
  1075. // run clean hook
  1076. if ($run_hook)
  1077. {
  1078. $values = $this->on_before_clean($values);
  1079. }
  1080. // get table information to clean against
  1081. $fields = $this->table_info();
  1082. $clean = array();
  1083. $values = array();
  1084. foreach($fields as $key => $val)
  1085. {
  1086. if (is_array($original_values) AND array_key_exists($key, $original_values))
  1087. {
  1088. $values[$key] = ($this->auto_trim AND is_string($original_values[$key])) ? trim($original_values[$key]) : $original_values[$key];
  1089. }
  1090. // add the date fields if they don't exist to the values array
  1091. elseif(is_array($original_values) AND !array_key_exists($key, $original_values) AND (in_array($key, $this->auto_date_add) OR in_array($key, $this->auto_date_update)))
  1092. {
  1093. $values[$key] = NULL;
  1094. }
  1095. }
  1096. // process linked fields
  1097. $values = $this->process_linked($values);
  1098. foreach ($values as $key => $field)
  1099. {
  1100. $field = $fields[$key];
  1101. if ($field['type'] == 'datetime')
  1102. {
  1103. if (empty($values[$key]) OR (int)$values[$key] == 0)
  1104. {
  1105. $values[$key] = $this->default_date;
  1106. }
  1107. }
  1108. else if ($field['type'] == 'date')
  1109. {
  1110. if (empty($values[$key]) OR (int)$values[$key] == 0) $values[$key] = $this->default_date;
  1111. if (!empty($values[$key]) AND !is_date_db_format($values[$key])) $values[$key] = english_date_to_db_format($values[$key]);
  1112. }
  1113. $date_func = ($this->date_use_gmt) ? 'gmdate' : 'date';
  1114. // create dates for date added and last updated fields automatically
  1115. $is_date_field_type = ($field['type'] == 'datetime' OR $field['type'] == 'timestamp' OR $field['type'] == 'date');
  1116. if ($is_date_field_type AND in_array($key, $this->auto_date_add))
  1117. {
  1118. $test_date = (isset($values[$key])) ? (int) $values[$key] : 0;
  1119. // if no key field then we assume it is a new save and so we add the date if it's empty'
  1120. if (!$this->_has_key_field_value($values) AND empty($test_date))
  1121. {
  1122. $values[$key] = ($field['type'] == 'date') ? $date_func('Y-m-d') : $date_func('Y-m-d H:i:s');
  1123. }
  1124. }
  1125. else if ($is_date_field_type AND in_array($key, $this->auto_date_update))
  1126. {
  1127. $values[$key] = ($field['type'] == 'date') ? $date_func('Y-m-d') : $date_func('Y-m-d H:i:s');
  1128. }
  1129. if (is_array($values) AND array_key_exists($key, $values))
  1130. {
  1131. // format dates
  1132. if (!in_array($key, $this->auto_date_add))
  1133. {
  1134. if ($field['type'] == 'datetime' OR $field['type'] == 'timestamp' OR $field['type'] == 'date')
  1135. {
  1136. if (isset($values[$key]) AND strncmp($values[$key], '0000', 4) !== 0)
  1137. {
  1138. if ($field['type'] == 'date')
  1139. {
  1140. $values[$key] = ($values[$key] != 'invalid') ? $date_func('Y-m-d', strtotime($values[$key])) : $this->default_date;
  1141. }
  1142. else
  1143. {
  1144. $values[$key] = ($values[$key] != 'invalid') ? $date_func('Y-m-d H:i:s', strtotime($values[$key])) : $this->default_date;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. // safe_htmlspecialchars is buggy for unserialize so we use the encode_and_clean
  1150. if (is_string($values[$key]))
  1151. {
  1152. $values[$key] = $this->encode_and_clean($values[$key], NULL, $key);
  1153. }
  1154. else if (is_array($values[$key]))
  1155. {
  1156. array_walk_recursive($values[$key], array($this, 'encode_and_clean'), $key);
  1157. }
  1158. $clean[$key] = $values[$key];
  1159. }
  1160. }
  1161. $this->cleaned_data = $clean;
  1162. return $clean;
  1163. }
  1164. public function encode_and_clean(&$val, $k, $key = NULL)
  1165. {
  1166. if (empty($key))
  1167. {
  1168. $key = $k;
  1169. }
  1170. if (is_string($val))
  1171. {
  1172. if ($this->auto_encode_entities)
  1173. {
  1174. if ((is_array($this->auto_encode_entities) AND in_array($key, $this->auto_encode_entities))
  1175. OR (is_string($this->auto_encode_entities) AND $key == $this->auto_encode_entities)
  1176. OR ($this->auto_encode_entities === TRUE)
  1177. )
  1178. {
  1179. $val = safe_htmlentities($val);
  1180. }
  1181. }
  1182. if ($this->xss_clean)
  1183. {
  1184. if ((is_array($this->xss_clean) AND in_array($key, $this->xss_clean))
  1185. OR (is_string($this->xss_clean) AND $key == $this->xss_clean)
  1186. OR ($this->xss_clean === TRUE)
  1187. )
  1188. {
  1189. $val = xss_clean(($val));
  1190. }
  1191. }
  1192. }
  1193. return $val;
  1194. }
  1195. // --------------------------------------------------------------------
  1196. /**
  1197. * Get the cleaned data
  1198. *
  1199. <code>
  1200. $cleaned_data = $this->examples_model->cleaned_data();
  1201. </code>
  1202. *
  1203. * @access public
  1204. * @return array
  1205. */
  1206. public function cleaned_data()
  1207. {
  1208. return $this->cleaned_data;
  1209. }
  1210. // --------------------------------------------------------------------
  1211. /**
  1212. * Returns number of query results
  1213. *
  1214. <code>
  1215. $where['published'] = 'yes';
  1216. echo $this->examples_model->record_count($where); // dislays the number of records
  1217. </code>
  1218. *
  1219. * @access public
  1220. * @param mixed where condition (optional)
  1221. * @return int
  1222. */
  1223. public function record_count($where = array())
  1224. {
  1225. $this->_handle_where($where);
  1226. $query = $this->db->get($this->table_name);
  1227. return $query->num_rows();
  1228. }
  1229. // --------------------------------------------------------------------
  1230. /**
  1231. * Returns number of records in the table
  1232. *
  1233. <code>
  1234. $total_count = $this->examples_model->total_record_count();
  1235. </code>
  1236. *
  1237. * @access public
  1238. * @return int
  1239. */
  1240. public function total_record_count()
  1241. {
  1242. return $this->db->count_all($this->table_name);
  1243. }
  1244. // --------------------------------------------------------------------
  1245. /**
  1246. * Saves record object, or array of data to the database
  1247. *
  1248. <code>
  1249. $this->examples_model->save($_POST, TRUE, TRUE); // Be sure to always clean your $_POST variables before using them
  1250. </code>
  1251. *
  1252. * @access public
  1253. * @param mixed an array or object to save to the database
  1254. * @param boolean validate the data before saving
  1255. * @param boolean ignore duplicate records on insert
  1256. * @return mixed
  1257. */
  1258. public function save($record = NULL, $validate = TRUE, $ignore_on_insert = TRUE, $clear_related = NULL)
  1259. {
  1260. $this->_check_readonly();
  1261. $CI =& get_instance();
  1262. if (!isset($record)) $record = $CI->input->post();
  1263. if ($this->_is_nested_array($record))
  1264. {
  1265. $saved = TRUE;
  1266. foreach($record as $rec)
  1267. {
  1268. if(!$this->save($rec, $validate, $ignore_on_insert, $clear_related))
  1269. {
  1270. $saved = FALSE;
  1271. }
  1272. }
  1273. return $saved;
  1274. }
  1275. else
  1276. {
  1277. $fields = array();
  1278. $old_clear_related_on_save = $this->clear_related_on_save;
  1279. if (isset($clear_related))
  1280. {
  1281. $this->clear_related_on_save = $clear_related;
  1282. }
  1283. $values = $this->normalize_save_values($record);
  1284. // reset validator here so that all validation set with hooks will not be lost
  1285. $this->validator->reset();
  1286. // clean the data before saving. on_before_clean hook now runs in the clean() method
  1287. $values = $this->on_before_clean($values);
  1288. $values = $this->clean($values);
  1289. $values = $this->on_before_validate($values);
  1290. // now validate. on_before_validate hook now runs inside validate() method
  1291. $validated = ($validate) ? $this->validate($values) : TRUE;
  1292. if ($validated AND !empty($values))
  1293. {
  1294. // now clean the data to be ready for database saving
  1295. $this->db->set($values);
  1296. if ($ignore_on_insert)
  1297. {
  1298. // execute on_before_insert/update hook methods
  1299. $values = $this->on_before_save($values);
  1300. // process serialized values
  1301. $values = $this->serialize_field_values($values);
  1302. if (!$this->_has_key_field_value($values))
  1303. {
  1304. $values = $this->on_before_insert($values);
  1305. }
  1306. else
  1307. {
  1308. $values = $this->on_before_update($values);
  1309. }
  1310. $insert_key = ($this->has_auto_increment) ? $this->key_field : NULL;
  1311. $this->db->insert_ignore($this->table_name, $values, $insert_key);
  1312. // execute on_insert/update hook methods
  1313. $no_key = FALSE;
  1314. $insert_id = $this->db->insert_id();
  1315. if (!$this->_has_key_field_value($values) AND $insert_id)
  1316. {
  1317. $no_key = TRUE;
  1318. if (is_string($this->key_field))
  1319. {
  1320. $values[$this->key_field] = $insert_id;
  1321. }
  1322. $this->on_after_insert($values);
  1323. }
  1324. else
  1325. {
  1326. $this->on_after_update($values);
  1327. }
  1328. // execute on_insert/update hook methods on the Date_record model if exists
  1329. if (is_object($record) AND ($record instanceof Data_record))
  1330. {
  1331. if ($no_key)
  1332. {
  1333. $record->on_after_insert($values);
  1334. }
  1335. else
  1336. {
  1337. $record->on_after_update($values);
  1338. }
  1339. }
  1340. }
  1341. else if (!$this->_has_key_field_value($values))
  1342. {
  1343. // execute on_before_insert/update hook methods
  1344. $values = $this->on_before_save($values);
  1345. $values = $this->on_before_insert($values);
  1346. // process serialized values
  1347. $values = $this->serialize_field_values($values);
  1348. $this->db->insert($this->table_name, $values);
  1349. $insert_id = $this->db->insert_id();
  1350. if (is_string($this->key_field))
  1351. {
  1352. $values[$this->key_field] = $insert_id;
  1353. }
  1354. $this->on_after_insert($values);
  1355. if ($record instanceof Data_record)
  1356. {
  1357. $record->on_after_insert($values);
  1358. }
  1359. }
  1360. else
  1361. {
  1362. $key_field = (array) $this->key_field;
  1363. foreach($key_field as $key)
  1364. {
  1365. $this->db->where($key, $values[$key]);
  1366. }
  1367. $values = $this->on_before_save($values);
  1368. $values = $this->on_before_update($values);
  1369. // process serialized values
  1370. $values = $this->serialize_field_values($values);
  1371. $this->db->update($this->table_name, $values);
  1372. $this->on_after_update($values);
  1373. if ($record instanceof Data_record)
  1374. {
  1375. $record->on_after_update();
  1376. }
  1377. }
  1378. }
  1379. else
  1380. {
  1381. return FALSE;
  1382. }
  1383. // returns the key value of the record upon save
  1384. if (isset($insert_id) AND ! empty($insert_id))
  1385. {
  1386. $return = $insert_id;
  1387. }
  1388. else
  1389. {
  1390. if ($record instanceof Data_record)
  1391. {
  1392. $key_field = $this->key_field;
  1393. if (is_string($this->key_field))
  1394. {
  1395. $return = $record->$key_field;
  1396. }
  1397. else
  1398. {
  1399. $return = array();
  1400. foreach($key_field as $key)
  1401. {
  1402. $return[$key] = $record->$key;
  1403. }
  1404. }
  1405. }
  1406. else if (is_string($this->key_field) AND !empty($values[$this->key_field]))
  1407. {
  1408. $return = $values[$this->key_field];
  1409. }
  1410. else if (is_array($this->key_field))
  1411. {
  1412. $return = array();
  1413. foreach($key_field as $key)
  1414. {
  1415. $return[$key] = $values[$key_field];
  1416. }
  1417. }
  1418. else
  1419. {
  1420. $return = TRUE;
  1421. // not valid test because a save could happen and no data is changed
  1422. //return (bool)($this->db->affected_rows()) ? TRUE : FALSE;
  1423. }
  1424. }
  1425. $this->on_after_save($values);
  1426. // set this back to the old value
  1427. $this->clear_related_on_save = $old_clear_related_on_save;
  1428. // check for errors here in case some are thrown in the hooks
  1429. if ($this->has_error())
  1430. {
  1431. return FALSE;
  1432. }
  1433. return $return;
  1434. }
  1435. }
  1436. // --------------------------------------------------------------------
  1437. /**
  1438. * Save related data to a many to many table. To be used in on_after_save hook
  1439. *
  1440. <code>
  1441. $this->examples_model->save_related('examples_to_categories', array('example_id' => $obj->id), array('categories_id' => $_POST['categories']));
  1442. </code>
  1443. *
  1444. * @access public
  1445. * @param mixed an array or object to save to the database
  1446. * @param array key is the column name, and value is the value to save
  1447. * @param array key is the column name, and the array of data to iterate over and save
  1448. * @return boolean
  1449. */
  1450. public function save_related($model, $key_field, $data)
  1451. {
  1452. $this->_check_readonly();
  1453. $CI =& get_instance();
  1454. $model = $this->load_model($model);
  1455. $id = current($key_field);
  1456. $key_field = key($key_field);
  1457. $other_field = key($data);
  1458. $data = current($data);
  1459. // first remove all the articles
  1460. $CI->$model->delete(array($key_field => $id));
  1461. // then read them
  1462. $return = TRUE;
  1463. foreach($data as $val)
  1464. {
  1465. $d = $CI->$model->create();
  1466. $d->$key_field = $id;
  1467. $d->$other_field = $val;
  1468. if ($d->save())
  1469. {
  1470. $return = FALSE;
  1471. }
  1472. }
  1473. return $return;
  1474. }
  1475. // --------------------------------------------------------------------
  1476. /**
  1477. * Handles grabbing of the related data's keys
  1478. *
  1479. <code>
  1480. </code>
  1481. *
  1482. * @access public
  1483. * @param array $values
  1484. * @param string $related_model
  1485. * @param string $mode, has_many or belongs_to (optional)
  1486. * @return array
  1487. */
  1488. public function get_related_keys($values, $related_model, $mode = 'has_many', $rel_config = '')
  1489. {
  1490. $CI =& get_instance();
  1491. $use_rel_tbl = $this->is_using_relationship_table($rel_config);
  1492. $fields = $this->relationship_field_names($mode);
  1493. if (is_array($related_model))
  1494. {
  1495. $related_model = $this->load_related_model($related_model);
  1496. }
  1497. $key_field = $this->key_field();
  1498. if ($use_rel_tbl == FALSE)
  1499. {
  1500. $assoc_where = array($rel_config['foreign_key'] => $values[$key_field]);
  1501. $related_keys = array_keys($CI->$related_model->find_all_array_assoc($CI->$related_model->key_field(), $assoc_where));
  1502. }
  1503. else
  1504. {
  1505. $relationships_model = $this->load_model($fields['relationships_model']);
  1506. $assoc_where = array();
  1507. if ($mode == 'belongs_to')
  1508. {
  1509. if (!empty($fields['candidate_table']) AND !empty($fields['foreign_table']))
  1510. {
  1511. $assoc_where = array($fields['candidate_table'] => $CI->$related_model->table_name, $fields['foreign_table'] => $this->table_name());
  1512. }
  1513. if ( ! empty($values) AND array_key_exists($key_field, $values))
  1514. {
  1515. $assoc_where[$fields['foreign_key']] = $values[$key_field];
  1516. }
  1517. $related_keys = array_keys($CI->$relationships_model->find_all_array_assoc($fields['candidate_key'], $assoc_where, $CI->$relationships_model->key_field()));
  1518. }
  1519. else
  1520. {
  1521. if (!empty($fields['foreign_table']) AND !empty($fields['candidate_table']))
  1522. {
  1523. $assoc_where = array($fields['candidate_table'] => $this->table_name(), $fields['foreign_table'] => $CI->$related_model->table_name);
  1524. }
  1525. if ( ! empty($values) AND array_key_exists($key_field, $values))
  1526. {
  1527. $assoc_where[$fields['candidate_key']] = $values[$key_field];
  1528. }
  1529. $related_keys = array_keys($CI->$relationships_model->find_all_array_assoc($fields['foreign_key'], $assoc_where, $CI->$relationships_model->key_field()));
  1530. }
  1531. }
  1532. return $related_keys;
  1533. }
  1534. // --------------------------------------------------------------------
  1535. /**
  1536. * Performs a simple insert to the database record.
  1537. *
  1538. <code>
  1539. $values['name'] = 'Darth Vader';
  1540. $values['email'] = 'dvader@deathstar.com';
  1541. $this->examples_model->insert($values);
  1542. </code>
  1543. *
  1544. * @access public
  1545. * @param mixed an array or object to save to the database
  1546. * @return boolean
  1547. */
  1548. public function insert($values)
  1549. {
  1550. $this->_check_readonly();
  1551. $values = $this->on_before_insert($values);
  1552. $values = $this->serialize_field_values($values);
  1553. $return = $this->db->insert($this->table_name, $values);
  1554. if (is_string($this->key_field))
  1555. {
  1556. $values[$this->key_field] = $this->db->insert_id();
  1557. }
  1558. $this->on_after_insert($values);
  1559. return $return;
  1560. }
  1561. // --------------------------------------------------------------------
  1562. /**
  1563. * Performs a simple update to the database record based on the <dfn>$where</dfn> condition passed.
  1564. *
  1565. <code>
  1566. $where['id'] = 1;
  1567. $this->examples_model->update($_POST, $where); // Be sure to always clean your $_POST variables before using them
  1568. </code>
  1569. *
  1570. * @access public
  1571. * @param mixed an array or object to save to the database
  1572. * @param mixed where condition
  1573. * @return boolean
  1574. */
  1575. public function update($values, $where)
  1576. {
  1577. $this->_check_readonly();
  1578. $values = $this->on_before_update($values);
  1579. $values = $this->serialize_field_values($values);
  1580. $this->db->where($where);
  1581. $return = $this->db->update($this->table_name, $values);
  1582. $this->on_after_update($values);
  1583. return $return;
  1584. }
  1585. // --------------------------------------------------------------------
  1586. /**
  1587. * Deletes database record(s) based on the <dfn>$where</dfn> condition passed. Does execute <dfn>before</dfn> and <dfn>after</dfn> delete hooks.
  1588. *
  1589. <code>
  1590. $where['id'] = 1;
  1591. $this->examples_model->delete($where);
  1592. </code>
  1593. *
  1594. * @access public
  1595. * @param mixed where condition
  1596. * @return void
  1597. */
  1598. public function delete($where)
  1599. {
  1600. $this->_check_readonly();
  1601. $this->on_before_delete($where);
  1602. if (is_object($where) AND $where instanceof Data_record)
  1603. {
  1604. $obj = $where;
  1605. $key_field = (array) $this->key_field;
  1606. $where = array();
  1607. foreach($key_field as $key)
  1608. {
  1609. $where[$key] = $obj->$key;
  1610. }
  1611. unset($obj);
  1612. }
  1613. $return = FALSE;
  1614. $this->db->where($where);
  1615. if ($this->is_valid())
  1616. {
  1617. $return = $this->db->delete($this->table_name);
  1618. }
  1619. $this->on_after_delete($where);
  1620. return $return;
  1621. }
  1622. // --------------------------------------------------------------------
  1623. /**
  1624. * Delete related data. To be used in on_before/on_after delete hooks.
  1625. *
  1626. <code>
  1627. $obj = $this->examples_model->create();
  1628. $obj->name = 'Darth Vader';
  1629. $obj->save();
  1630. $this->examples_model->delete_related('examples_to_categories', 'example_id', $obj);
  1631. </code>
  1632. *
  1633. * @access public
  1634. * @param string
  1635. * @param string
  1636. * @param mixed
  1637. * @return boolean
  1638. */
  1639. public function delete_related($model, $key_field, $where)
  1640. {
  1641. $this->_check_readonly();
  1642. // delete all has and belongs to many
  1643. $CI =& get_instance();
  1644. $id = $this->_determine_key_field_value($where);
  1645. if (!empty($id))
  1646. {
  1647. $model = $this->load_model($model);
  1648. return $CI->$model->delete(array($key_field => $id));
  1649. }
  1650. return FALSE;
  1651. }
  1652. // --------------------------------------------------------------------
  1653. /**
  1654. * Truncates the data in the table
  1655. *
  1656. <code>
  1657. $this->examples_model->truncate();
  1658. </code>
  1659. *
  1660. * @access public
  1661. * @return void
  1662. */
  1663. public function truncate()
  1664. {
  1665. $this->_check_readonly();
  1666. $this->db->truncate($this->table_name);
  1667. }
  1668. // --------------------------------------------------------------------
  1669. /**
  1670. * Creates a where condition and queries the model's table to see if the column (<dfn>$key</dfn>) already contains the <dfn>$val</dfn> value.
  1671. * Usually used for validation to check if a unique key already exists.
  1672. *
  1673. <code>
  1674. $this->examples_model->is_new('location', 'id');
  1675. </code>
  1676. *
  1677. * @access public
  1678. * @param string value to be checked
  1679. * @param string column name to check
  1680. * @return array
  1681. */
  1682. public function is_new($val, $key)
  1683. {
  1684. if (!isset($val)) return FALSE;
  1685. if (is_array($key))
  1686. {
  1687. $data = $this->find_one_array($key);
  1688. }
  1689. else
  1690. {
  1691. $data = $this->find_one_array(array($key => $val));
  1692. }
  1693. if (!empty($data) AND $data[$key] == $val) return FALSE;
  1694. return TRUE;
  1695. }
  1696. // --------------------------------------------------------------------
  1697. /**
  1698. * Creates a where condition and queries the model's table to see if the column (<dfn>$key</dfn>) already contains the <dfn>$val</dfn> value but compares it to the tables key column with the <dfn>$id</dfn> value.
  1699. * Usually used for validation. The example below uses this method to validate on the model before saving. The <dfn>is_new</dfn> and <dfn>is_editable</dfn> methods are usually used during the models validation process as shown in the example.
  1700. *
  1701. <code>
  1702. public function on_before_validate($values)
  1703. {
  1704. if (!empty($values['id']))
  1705. {
  1706. $this->add_validation('location', array(&$this, 'is_editable'), 'The location value already exists.' , array('location', $values['id']));
  1707. }
  1708. else
  1709. {
  1710. $this->add_validation('location', array(&$this, 'is_new'), 'The location value already exists.', 'location');
  1711. }
  1712. return $values;
  1713. }
  1714. </code>
  1715. *
  1716. * @access public
  1717. * @param string value to be checked
  1718. * @param string column name to check
  1719. * @param mixed the key field value to check against. May also be the complete array of values if the key value is also an array (for compound unique keys)
  1720. * @return array
  1721. */
  1722. public function is_editable($val, $key, $id)
  1723. {
  1724. if (!isset($val)) return FALSE;
  1725. $key_field = $this->key_field();
  1726. if (is_array($key) AND is_array($id))
  1727. {
  1728. foreach($key as $k)
  1729. {
  1730. if (!isset($id[$k]))
  1731. {
  1732. return FALSE;
  1733. }
  1734. $where[$k] = $id[$k];
  1735. }
  1736. $data = $this->find_one_array($where);
  1737. $unique_value = $id[$key_field];
  1738. }
  1739. else
  1740. {
  1741. $data = $this->find_one_array(array($key => $val));
  1742. $unique_value = $id;
  1743. }
  1744. // if no data then we are new and good
  1745. if (empty($data)) return TRUE;
  1746. // we are going to ignore multiple keys
  1747. if (is_string($key_field))
  1748. {
  1749. if (!empty($data) AND $data[$key_field] == $unique_value)
  1750. {
  1751. return TRUE;
  1752. }
  1753. }
  1754. else
  1755. {
  1756. return FALSE;
  1757. }
  1758. return FALSE;
  1759. }
  1760. // --------------------------------------------------------------------
  1761. /**
  1762. * Validate the data before saving. Can pass either a custom record object or an associative array of table column values.
  1763. * Will run all validation rules, including required and autovalidation (if auto_validation is set on the model)
  1764. * and return <dfn>TRUE</dfn> if it passes validation and <dfn>FALSE</dfn> if it fails. The <dfn>run_hook</dfn> parameter
  1765. * will run the model's on_before_validate hook if set to TRUE (default is FALSE);
  1766. *
  1767. <code>
  1768. // Using an array of values
  1769. $values['name'] = 'Mr. Jones';
  1770. $values['email'] = 'jones@example.com';
  1771. $values['active'] = 'yes';
  1772. $this->examples_model->validate($values);
  1773. // Using a custom record
  1774. $example = $this->examples_model->create();
  1775. $example->name = 'Mr. Jones';
  1776. $example->email = 'jones@example.com';
  1777. $example->active = 'yes';
  1778. $this->examples_model->validate($example);
  1779. // you can also just call the validate method on the custom record object
  1780. $example->validate();
  1781. </code>
  1782. *
  1783. * @access public
  1784. * @param mixed object or array of values
  1785. * @param boolean run on_before_validate hook or not (optional)
  1786. * @return array
  1787. */
  1788. public function validate($record, $run_hook = FALSE)
  1789. {
  1790. $values = array();
  1791. if (is_array($record))
  1792. {
  1793. $values = $record;
  1794. }
  1795. else if (is_object($record))
  1796. {
  1797. if ($record instanceof Data_record)
  1798. {
  1799. $values = $record->values();
  1800. }
  1801. else
  1802. {
  1803. $values = get_object_vars($record);
  1804. }
  1805. }
  1806. if ($run_hook)
  1807. {
  1808. $values = $this->on_before_validate($values);
  1809. }
  1810. // add required values in if they don't exist
  1811. foreach($this->required as $key => $val)
  1812. {
  1813. $field = (is_int($key)) ? $val : $key;
  1814. if (!isset($values[$field]))
  1815. {
  1816. $values[$field] = NULL;
  1817. }
  1818. }
  1819. // if any errors are generated in the previous hooks then we return FALSE
  1820. if ($this->get_errors())
  1821. {
  1822. return FALSE;
  1823. }
  1824. $required = $this->required;
  1825. foreach($this->unique_fields as $unique_field)
  1826. {
  1827. if (is_array($unique_field))
  1828. {
  1829. foreach($unique_field as $uf)
  1830. {
  1831. if (!in_array($uf, $required))
  1832. {
  1833. $required[] = $uf;
  1834. }
  1835. }
  1836. }
  1837. else if (!in_array($unique_field, $required))
  1838. {
  1839. $required[] = $unique_field;
  1840. }
  1841. }
  1842. // changed to above so we can have compound keys
  1843. //$required = array_merge($this->unique_fields, $this->required);
  1844. // convert required fields to rules
  1845. foreach($required as $key => $val)
  1846. {
  1847. if (is_int($key))
  1848. {
  1849. $field = $val;
  1850. if (empty($this->default_required_message))
  1851. {
  1852. $this->default_required_message = lang('error_required_fields');
  1853. }
  1854. $msg = sprintf($this->default_required_message, str_replace('_', ' ', $val));
  1855. }
  1856. else
  1857. {
  1858. $field = $key;
  1859. $msg = $val;
  1860. }
  1861. $this->rules[] = array($field, 'required', $msg);
  1862. }
  1863. // check required first
  1864. foreach($this->rules as $rule)
  1865. {
  1866. if ($rule[1] == 'required')
  1867. {
  1868. foreach ($values as $key => $val)
  1869. {
  1870. if ($key == $rule[0])
  1871. {
  1872. // check required fields and make sure there is a value passed
  1873. array_push($rule, $val); // add value to last parameter of rule
  1874. break;
  1875. }
  1876. }
  1877. call_user_func_array(array(&$this->validator, 'add_rule'), $rule);
  1878. }
  1879. }
  1880. // check unique fields and add validation for them
  1881. foreach($this->unique_fields as $field)
  1882. {
  1883. $has_key_field = $this->_has_key_field_value($values);
  1884. $key_field = $this->key_field();
  1885. if (is_array($field))
  1886. {
  1887. $where = array();
  1888. foreach($field as $f)
  1889. {
  1890. if (isset($values[$f]))
  1891. {
  1892. $where[$f] = $values[$f];
  1893. }
  1894. }
  1895. foreach($field as $f)
  1896. {
  1897. $friendly_field = ucwords(str_replace('_', ' ', implode(', ', $field)));
  1898. if ($has_key_field)
  1899. {
  1900. if (!is_array($key_field))
  1901. {
  1902. $this->add_validation($f, array(&$this, 'is_editable'), lang('error_val_empty_or_already_exists', $friendly_field), array($field, $values));
  1903. }
  1904. }
  1905. else
  1906. {
  1907. $this->add_validation($f, array(&$this, 'is_new'), lang('error_val_empty_or_already_exists', $friendly_field), array($where));
  1908. }
  1909. }
  1910. }
  1911. else
  1912. {
  1913. $friendly_field = ucwords(str_replace('_', ' ', $field));
  1914. if ($has_key_field)
  1915. {
  1916. if (!is_array($key_field))
  1917. {
  1918. $this->add_validation($field, array(&$this, 'is_editable'), lang('error_val_empty_or_already_exists', $friendly_field), array($field, $values[$key_field]));
  1919. }
  1920. }
  1921. else
  1922. {
  1923. $this->add_validation($field, array(&$this, 'is_new'), lang('error_val_empty_or_already_exists', $friendly_field), array($field, $field));
  1924. }
  1925. }
  1926. }
  1927. // run other validation in model if exists
  1928. foreach ($values as $key => $val)
  1929. {
  1930. foreach($this->rules as $rule)
  1931. {
  1932. if ($key == $rule[0] AND $rule[1] != 'required')
  1933. {
  1934. $rule_val = (array) $val;
  1935. if (empty($rule[3]))
  1936. {
  1937. $rule[3] = (!empty($values[$key])) ? array($values[$key]) : array();
  1938. }
  1939. else if (!is_array($rule[3]))
  1940. {
  1941. $rule[3] = array($rule[3]);
  1942. }
  1943. // now replace any placeholders for values
  1944. foreach($rule[3] as $r_key => $r_val)
  1945. {
  1946. if (is_array($r_val))
  1947. {
  1948. foreach($r_val as $rv)
  1949. {
  1950. if (strpos($rv, '{') === 0)
  1951. {
  1952. $val_key = str_replace(array('{', '}'), '', $rv);
  1953. if (isset($values[$val_key])) $rule[3][$r_key] = $values[$val_key];
  1954. }
  1955. }
  1956. }
  1957. else
  1958. {
  1959. if (strpos($r_val, '{') === 0)
  1960. {
  1961. $val_key = str_replace(array('{', '}'), '', $r_val);
  1962. if (isset($values[$val_key])) $rule[3][$r_key] = $values[$val_key];
  1963. }
  1964. }
  1965. }
  1966. $rule[3] = array_merge($rule_val, $rule[3]);
  1967. call_user_func_array(array(&$this->validator, 'add_rule'), $rule);
  1968. }
  1969. }
  1970. }
  1971. // run the auto validation
  1972. if ($this->auto_validate)
  1973. {
  1974. foreach ($this->fields() as $field)
  1975. {
  1976. if (isset($values[$field]))
  1977. {
  1978. $this->auto_validate_field($field, $values[$field]);
  1979. }
  1980. }
  1981. }
  1982. $validated = ($this->validator->validate(array_keys($values)));
  1983. return $validated;
  1984. }
  1985. // --------------------------------------------------------------------
  1986. /**
  1987. * Returns a field type of <dfn>string</dfn>, <dfn>number</dfn>, <dfn>date</dfn>, <dfn>datetime</dfn>, <dfn>time</dfn>, <dfn>blob</dfn>, <dfn>enum</dfn>
  1988. *
  1989. <code>
  1990. $type = $this->examples_model->field_type('email');
  1991. echo $type; // string
  1992. </code>
  1993. *
  1994. * @access public
  1995. * @param string field
  1996. * @return array
  1997. */
  1998. public function field_type($field)
  1999. {
  2000. $field_info = $this->field_info($field);
  2001. switch($field_info['type'])
  2002. {
  2003. case 'var' : case 'varchar': case 'string': case 'tinytext': case 'text': case 'longtext':
  2004. return 'string';
  2005. case 'int': case 'tinyint': case 'smallint': case 'mediumint': case 'float': case 'double': case 'decimal':
  2006. return 'number';
  2007. case 'datetime': case 'timestamp':
  2008. return 'datetime';
  2009. case 'date':
  2010. return 'date';
  2011. case 'year':
  2012. return 'year';
  2013. case 'time':
  2014. return 'time';
  2015. case 'blob': case 'mediumblob': case 'longblob': case 'binary':
  2016. return 'blob';
  2017. case 'enum':
  2018. return 'enum';
  2019. default:
  2020. return $field_info['type'];
  2021. }
  2022. }
  2023. // --------------------------------------------------------------------
  2024. /**
  2025. * Automatically validate the data before saving based on the table meta info
  2026. *
  2027. * @access protected
  2028. * @param string field name
  2029. * @param string value of field
  2030. * @return array
  2031. */
  2032. protected function auto_validate_field($field, $value)
  2033. {
  2034. $CI =& get_instance();
  2035. // set auto validation field rules
  2036. foreach($this->auto_validate_fields as $key => $val)
  2037. {
  2038. if (!empty($value) AND preg_match("/".$key."/", $field))
  2039. {
  2040. $this->validator->add_rule($field, $val, lang('error_'.$val, $val), $value);
  2041. }
  2042. }
  2043. // set auto validation based on field type
  2044. $field_data = $this->db->field_info($this->table_name, $field);
  2045. if (!empty($field_data) AND !empty($value))
  2046. {
  2047. $field_name = "'".(str_replace('_', ' ', $field))."'";
  2048. $type = $this->field_type($field);
  2049. if ( !is_array($value) )
  2050. {
  2051. switch($type)
  2052. {
  2053. case 'string':
  2054. if (!empty($field_data['max_length'])) $this->validator->add_rule($field, 'length_max', lang('error_value_exceeds_length', $field_name), array($value, $field_data['max_length']));
  2055. break;
  2056. case 'number':
  2057. $this->validator->add_rule($field, 'is_numeric', lang('error_not_number', $field_name), $value);
  2058. if ($field_data['type'] != 'float') $this->validator->add_rule($field, 'length_max', lang('error_value_exceeds_length', $field_name), array($value, $field_data['max_length']));
  2059. break;
  2060. case 'date':
  2061. if (strncmp($value, '0000', 4) !== 0)
  2062. {
  2063. $this->validator->add_rule($field, 'valid_date', lang('error_invalid_date', $field_name), $value);
  2064. if ($field_data['type'] == 'datetime') $this->validator->add_rule($field, 'valid_time', lang('error_invalid_time', $field_name), $value);
  2065. }
  2066. break;
  2067. case 'year':
  2068. $reg_ex = (strlen(strval($value)) == 2) ? '\d{2}' : '\d{4}';
  2069. $this->validator->add_rule($field, 'regex', lang('error_invalid_year', $field_name), array($value, $reg_ex));
  2070. break;
  2071. case 'enum':
  2072. $options = (!empty($field_data['options'])) ? $field_data['options'] : $field_data['max_length'];
  2073. $this->validator->add_rule($field, 'is_one_of_these', lang('error_invalid_generic', $field_name), array($value, $options)); // options get put into max_length field
  2074. }
  2075. }
  2076. }
  2077. }
  2078. // --------------------------------------------------------------------
  2079. /**
  2080. * Returns a boolean value if all validation rules that have been run have passed.
  2081. *
  2082. <code>
  2083. $values['name'] = 'Mr. Jones';
  2084. $values['email'] = 'jones@example.com';
  2085. $values['active'] = 'yes';
  2086. $this->examples_model->validate($values);
  2087. if ($this->examples_model->is_valid($values))
  2088. {
  2089. echo 'We are valid!';
  2090. }
  2091. </code>
  2092. *
  2093. * @access public
  2094. * @return boolean
  2095. */
  2096. public function is_valid()
  2097. {
  2098. return $this->validator->is_valid();
  2099. }
  2100. // --------------------------------------------------------------------
  2101. /**
  2102. * Add a validation rule
  2103. *
  2104. <code>
  2105. $this->examples_model->add_validation('email', 'valid_email', 'The email is invalid.', 'jones@example.com');
  2106. </code>
  2107. *
  2108. * @access public
  2109. * @param string field name
  2110. * @param mixed function name OR array($object_instance, $method)
  2111. * @param string error message to display
  2112. * @return void
  2113. */
  2114. public function add_validation($field, $rule, $msg)
  2115. {
  2116. $key = (is_array($rule)) ? $rule[1]: $rule;
  2117. $this->rules[$field.'-'.$key] = func_get_args();
  2118. }
  2119. // --------------------------------------------------------------------
  2120. /**
  2121. * Add an error to the validation to prevent saving
  2122. *
  2123. <code>
  2124. $this->examples_model->add_error('There was an error in processing the data.', 'my_key');
  2125. </code>
  2126. *
  2127. * @access public
  2128. * @param string error message
  2129. * @param string key value of error message (optional)
  2130. * @return void
  2131. */
  2132. public function add_error($msg, $key = NULL)
  2133. {
  2134. $this->validator->catch_errors($msg, $key);
  2135. }
  2136. // --------------------------------------------------------------------
  2137. /**
  2138. * Returns a <dfn>TRUE</dfn> if the model has any errors associated with it and <dfn>FALSE</dfn> otherwise.
  2139. *
  2140. <code>
  2141. if ($this->examples_model->has_error())
  2142. {
  2143. return FALSE;
  2144. }
  2145. </code>
  2146. *
  2147. * @access public
  2148. * @return boolean
  2149. */
  2150. public function has_error()
  2151. {
  2152. return (count($this->validator->get_errors()) > 0);
  2153. }
  2154. // --------------------------------------------------------------------
  2155. /**
  2156. * Remove a validation rule from the validator object
  2157. *
  2158. <code>
  2159. $this->examples_model->remove_validation('my_field', 'my_func');
  2160. </code>
  2161. *
  2162. * @access public
  2163. * @param string field name
  2164. * @param string function name (optional)
  2165. * @return array
  2166. */
  2167. public function remove_validation($field, $rule = NULL)
  2168. {
  2169. $key = (is_array($rule)) ? $rule[1]: $rule;
  2170. unset($this->rules[$field.'-'.$key]);
  2171. $this->validator->remove_rule($field, $rule);
  2172. }
  2173. // --------------------------------------------------------------------
  2174. /**
  2175. * Add a required field
  2176. *
  2177. <code>
  2178. $this->examples_model->add_required('my_field');
  2179. </code>
  2180. *
  2181. * @access public
  2182. * @param string field name
  2183. * @return void
  2184. */
  2185. public function add_required($field)
  2186. {
  2187. if (is_array($field))
  2188. {
  2189. foreach($field as $required)
  2190. {
  2191. if (!in_array($required, $this->required)) $this->required[] = $required;
  2192. }
  2193. }
  2194. else
  2195. {
  2196. if (!in_array($field, $this->required)) $this->required[] = $field;
  2197. }
  2198. }
  2199. // --------------------------------------------------------------------
  2200. /**
  2201. * Gets the validation object
  2202. *
  2203. <code>
  2204. $validation = $this->examples_model->get_validation();
  2205. if ($validation->is_valid())
  2206. {
  2207. echo 'YEAH!';
  2208. }
  2209. </code>
  2210. *
  2211. * @access public
  2212. * @return object
  2213. */
  2214. public function &get_validation()
  2215. {
  2216. return $this->validator;
  2217. }
  2218. // --------------------------------------------------------------------
  2219. /**
  2220. * Sets the validation to register to the global scope
  2221. *
  2222. <code>
  2223. $this->examples_model->register_to_global_errors(TRUE);
  2224. ...// code goes here
  2225. $errors = get_errors(); // validator_helper function to get global errors
  2226. foreach($errors as $error)
  2227. {
  2228. echo $error;
  2229. // There was an error in processing your data
  2230. }
  2231. </code>
  2232. *
  2233. * @access public
  2234. * @return object
  2235. */
  2236. public function register_to_global_errors($register)
  2237. {
  2238. $this->validator->register_to_global_errors = $register;
  2239. }
  2240. // --------------------------------------------------------------------
  2241. /**
  2242. * Returns the errors found in the validator object
  2243. *
  2244. <code>
  2245. $errors = $this->examples_model->get_errors();
  2246. foreach($errors as $error)
  2247. {
  2248. echo $error;
  2249. // There was an error in processing your data
  2250. }
  2251. </code>
  2252. *
  2253. * @access public
  2254. * @return array
  2255. */
  2256. public function get_errors()
  2257. {
  2258. return $this->validator->get_errors();
  2259. }
  2260. // --------------------------------------------------------------------
  2261. /**
  2262. * Removes all the validation
  2263. *
  2264. <code>
  2265. $this->examples_model->remove_all_validation(TRUE);
  2266. </code>
  2267. *
  2268. * @access public
  2269. * @param boolean determines whether to remove required validation as well as other validation rules
  2270. * @return void
  2271. */
  2272. public function remove_all_validation($remove_required = FALSE)
  2273. {
  2274. $this->validator->reset(TRUE);
  2275. if ($remove_required)
  2276. {
  2277. $this->required = array();
  2278. }
  2279. $this->rules = array();
  2280. }
  2281. // --------------------------------------------------------------------
  2282. /**
  2283. * Returns an associative array of a specific field's meta information which includes the following:
  2284. *
  2285. <ul>
  2286. <li><strong>name</strong> - the name of the field</li>
  2287. <li><strong>type</strong> - the type of field (e.g. int, varchar, datetime... etc)</li>
  2288. <li><strong>default</strong> - the default value of the field</li>
  2289. <li><strong>options/max_length</strong> - if it is an enum field, then the enum options will be displayed. Otherwise, it will show the max length of the field which may not be relevant for some field types.</li>
  2290. <li><strong>primary_key</strong> - the primary key column</li>
  2291. <li><strong>comment</strong> - the comment</li>
  2292. <li><strong>collation</strong> - the collation method</li>
  2293. <li><strong>extra</strong> - extra field meta information like auto_increment</li>
  2294. <li><strong>null</strong> - a boolean value if the field is <dfn>NULL</dfn> or not </li>
  2295. </ul>
  2296. <code>
  2297. $field_meta = $this->examples_model->field_info('email');
  2298. echo $field_meta['name']; // email
  2299. echo $field_meta['type']; // varchar
  2300. </code>
  2301. *
  2302. * @access public
  2303. * @return array
  2304. */
  2305. public function field_info($field)
  2306. {
  2307. return $this->db->field_info($this->table_name, $field);
  2308. }
  2309. // --------------------------------------------------------------------
  2310. /**
  2311. * Returns an associative array of table field meta information with each key representing a table field.
  2312. *
  2313. <code>
  2314. $table_meta = $this->examples_model->table_info();
  2315. echo $table_meta['id']['type']; // int
  2316. echo $table_meta['id']['primary_key']; // 1 (TRUE)
  2317. echo $table_meta['email']['type']; // varchar
  2318. echo $table_meta['first_name']['type']; // varchar
  2319. echo $table_meta['description']['type']; // text
  2320. echo $table_meta['active']['type']; // enum
  2321. print_r($table_meta['active']['options']); // array('yes', 'no')
  2322. </code>
  2323. *
  2324. * @access public
  2325. * @return array
  2326. */
  2327. public function table_info()
  2328. {
  2329. return $this->db->table_info($this->table_name);
  2330. }
  2331. // --------------------------------------------------------------------
  2332. /**
  2333. * Returns an array of information that can be used for building a form (e.g. Form_builder).
  2334. *
  2335. * Somewhat similar to the table_info method with difference being that the returned array has information for creating a form.
  2336. * The related parameter is used to conveniently map other model information with this form to create a many to many multi-select form element.
  2337. * This method is usally used with the <a href="<?=user_guide_url('libraries/form_builder')?>">Form_builder</a> class.
  2338. *
  2339. <code>
  2340. $form_info = $this->examples_model->form_fields();
  2341. echo $form_info['id']['type']; // hidden
  2342. echo $form_info['email']['type']; // text
  2343. echo $form_info['email']['required']; // 1 (TRUE)
  2344. echo $form_info['first_name']['type']; // text
  2345. echo $form_info['description']['type']; // textfield
  2346. echo $form_info['active']['type']; // select or enum
  2347. echo $form_info['date_added']['type']; // datetime (a special field type in the form_builder class)
  2348. </code>
  2349. *
  2350. * @access public
  2351. * @param array array of values to pass to the form fields
  2352. * @param array an array of info about related models that create multiselects
  2353. * @return array
  2354. */
  2355. public function form_fields($values = array(), $related = array())
  2356. {
  2357. $CI =& get_instance();
  2358. $fields = $this->table_info();
  2359. foreach($fields as $key => $val)
  2360. {
  2361. if (is_array($this->required))
  2362. {
  2363. $required = array();
  2364. foreach($this->required as $req => $req_val)
  2365. {
  2366. if (is_string($req))
  2367. {
  2368. $required[] = $req;
  2369. }
  2370. else
  2371. {
  2372. $required[] = $req_val;
  2373. }
  2374. }
  2375. }
  2376. $fields[$key]['required'] = (in_array($key, $required)) ? TRUE : FALSE;
  2377. // create options for enum values
  2378. if ($val['type'] == 'enum')
  2379. {
  2380. if (is_array($val['options']))
  2381. {
  2382. $fields[$key]['options'] = array_combine($val['options'], $val['options']);
  2383. }
  2384. }
  2385. // create boolean checkboxes
  2386. if(in_array($val['name'], $this->boolean_fields) AND ($val['type'] === 'tinyint'))
  2387. {
  2388. $fields[$key]['type'] = 'checkbox';
  2389. $fields[$key]['value'] = 1;
  2390. }
  2391. // set password fields
  2392. if ($key == 'password' OR $key == 'pwd' OR $key == 'pass')
  2393. {
  2394. $fields[$key]['type'] = 'password';
  2395. }
  2396. }
  2397. // lookup unique fields and give them a required parameter
  2398. if (!empty($this->unique_fields))
  2399. {
  2400. foreach($this->unique_fields as $val)
  2401. {
  2402. if (is_array($val))
  2403. {
  2404. foreach($val as $v)
  2405. {
  2406. $fields[$v]['required'] = TRUE;
  2407. }
  2408. }
  2409. else
  2410. {
  2411. $fields[$val]['required'] = TRUE;
  2412. }
  2413. }
  2414. }
  2415. // lookup foreign keys and make the selects by default
  2416. if (!empty($this->foreign_keys))
  2417. {
  2418. foreach($this->foreign_keys as $key => $val)
  2419. {
  2420. $where = NULL;
  2421. $order = TRUE;
  2422. $model = $this->load_model($val);
  2423. if (is_array($val))
  2424. {
  2425. if (!empty($val['where']))
  2426. {
  2427. $where = $val['where'];
  2428. $where = $this->_replace_placeholders($where, $values);
  2429. unset($val['where']);
  2430. }
  2431. if (!empty($val['order']))
  2432. {
  2433. $order = $val['order'];
  2434. unset($val['order']);
  2435. }
  2436. }
  2437. $fields[$key]['type'] = 'select';
  2438. $fields[$key]['options'] = $CI->$model->options_list(NULL, NULL, $where, $order);
  2439. $fields[$key]['first_option'] = lang('label_select_one');
  2440. $fields[$key]['label'] = ucfirst(str_replace('_', ' ', $CI->$model->singular_name(FALSE)));
  2441. $fields[$key]['module'] = $CI->$model->short_name(TRUE, FALSE);
  2442. }
  2443. }
  2444. // create related
  2445. if (!empty($related))
  2446. {
  2447. $key_field = $this->key_field();
  2448. if (is_string($key_field))
  2449. {
  2450. foreach($related as $key => $val)
  2451. {
  2452. // related need to be loaded using slash syntax if model belongs in another module (e.g. my_module/my_model)
  2453. $related_name = end(explode('/', $key));
  2454. $related_model = $this->load_model($key.$this->suffix);
  2455. $related_model_name = $related_name.$this->suffix;
  2456. $lookup_name = end(explode('/', $val));
  2457. $lookup_model = $this->load_model($val);
  2458. $options = $CI->$related_model_name->options_list();
  2459. // important to sort by id ascending order in case a field type uses the saving order as how it should be returned (e.g. a sortable multi-select)
  2460. $singular_name = $this->singular_name(TRUE);
  2461. $field_values = (!empty($values[$key_field])) ? array_keys($CI->$lookup_name->find_all_array_assoc($singular_name.'_id', array($singular_name.'_id' => $values[$key_field]), 'id asc')) : array();
  2462. $fields[$key] = array('label' => ucfirst($related_name), 'type' => 'multi', 'module' => $key, 'options' => $options, 'value' => $field_values, 'mode' => 'multi');
  2463. }
  2464. }
  2465. }
  2466. // attach relationship fields if they exist
  2467. if ( ! empty($this->has_many))
  2468. {
  2469. foreach ($this->has_many as $related_field => $rel_config)
  2470. {
  2471. $related_model = $this->load_related_model($rel_config);
  2472. $where = NULL;
  2473. $order = TRUE;
  2474. if (is_array($rel_config))
  2475. {
  2476. if (!empty($rel_config['where']))
  2477. {
  2478. $where = $rel_config['where'];
  2479. $where = $this->_replace_placeholders($where, $values);
  2480. }
  2481. if (!empty($rel_config['order']))
  2482. {
  2483. $order = $rel_config['order'];
  2484. }
  2485. }
  2486. $related_options = $CI->$related_model->options_list(NULL, NULL, $where, $order);
  2487. $related_vals = ( ! empty($values['id'])) ? $this->get_related_keys($values, $related_model, 'has_many', $rel_config) : array();
  2488. $fields[$related_field] = array('label' => humanize($related_field), 'type' => 'multi', 'options' => $related_options, 'value' => $related_vals, 'mode' => 'multi', 'module' => $CI->$related_model->short_name(TRUE));
  2489. }
  2490. }
  2491. if ( ! empty($this->belongs_to))
  2492. {
  2493. foreach ($this->belongs_to as $related_field => $rel_config)
  2494. {
  2495. $where = NULL;
  2496. $order = TRUE;
  2497. if (is_array($rel_config))
  2498. {
  2499. if (!empty($rel_config['where']))
  2500. {
  2501. $where = $rel_config['where'];
  2502. $where = $this->_replace_placeholders($where, $values);
  2503. }
  2504. if (!empty($rel_config['order']))
  2505. {
  2506. $order = $rel_config['order'];
  2507. }
  2508. }
  2509. $related_model = $this->load_related_model($rel_config);
  2510. $related_options = $CI->$related_model->options_list(NULL, NULL, $where, $order);
  2511. $related_vals = ( ! empty($values['id'])) ? $this->get_related_keys($values, $related_model, 'belongs_to', $rel_config) : array();
  2512. $fields[$related_field] = array('label' => lang('label_belongs_to').'<br />' . humanize($related_field), 'type' => 'multi', 'options' => $related_options, 'value' => $related_vals, 'mode' => 'multi', 'module' => $CI->$related_model->short_name(TRUE));
  2513. }
  2514. }
  2515. // !@todo Finish creating has_one relationship
  2516. /*
  2517. if ( ! empty($this->has_one))
  2518. {
  2519. foreach ($this->has_one as $related_field => $rel_config)
  2520. {
  2521. $related_model = $this->load_related_model($rel_config);
  2522. $related_options = $this->$related_model->options_list();
  2523. $related_vals = ( ! empty($values['id'])) ? $this->get_related_keys($values, $related_model, 'has_many', $rel_config) : array();
  2524. $fields[$related_field] = array('label' => humanize($related_field), 'type' => 'select', 'options' => $related_options, 'value' => $related_vals, 'first_option' => lang('label_select_one'));
  2525. }
  2526. }
  2527. */
  2528. // set auto dates to display only be default
  2529. $hidden_fields = array_merge($this->auto_date_add, $this->auto_date_update, $this->hidden_fields);
  2530. foreach($hidden_fields as $f)
  2531. {
  2532. if (isset($fields[$f])) $fields[$f]['type'] = 'hidden';
  2533. }
  2534. return $fields;
  2535. }
  2536. /**
  2537. * Outputs the data passed to in into a comma separated value (CSV)
  2538. *
  2539. <code>
  2540. $items = $this->find_all();
  2541. $data = $this->csv($items);
  2542. </code>
  2543. *
  2544. * @access public
  2545. * @param boolean Display headers?
  2546. * @param string The delimiter - comma by default
  2547. * @param string The newline character - \n by default
  2548. * @param string The enclosure - double quote by default
  2549. * @return string
  2550. */
  2551. public function csv($data = NULL, $display_headers = TRUE, $delim = ",", $newline = "\n", $enclosure = '"')
  2552. {
  2553. // borrowed from CI DBUtil class
  2554. $out = '';
  2555. if (is_null($data))
  2556. {
  2557. $data = $this->find_all_array();
  2558. }
  2559. // First generate the headings from the table column names
  2560. if ($display_headers !== FALSE)
  2561. {
  2562. $headers = array();
  2563. // check if it is a query object first
  2564. if (is_object($data) AND method_exists($data, 'list_fields'))
  2565. {
  2566. $headers = $query->list_fields();
  2567. $data = $query->result_array();
  2568. }
  2569. // then check to see if it is just a data array
  2570. else if ($this->_is_nested_array($data))
  2571. {
  2572. $record = current($data);
  2573. $headers = array_keys($this->normalize_data($record));
  2574. }
  2575. foreach ($headers as $name)
  2576. {
  2577. $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim;
  2578. }
  2579. }
  2580. $out = rtrim($out);
  2581. $out .= $newline;
  2582. // Next blast through the result array and build out the rows
  2583. foreach ($data as $row)
  2584. {
  2585. // normalize the row data
  2586. $row = $this->normalize_data($row);
  2587. foreach ($row as $item)
  2588. {
  2589. $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim;
  2590. }
  2591. $out = rtrim($out);
  2592. $out .= $newline;
  2593. }
  2594. return $out;
  2595. }
  2596. // --------------------------------------------------------------------
  2597. /**
  2598. * Returns the custom record class name if it exists.
  2599. *
  2600. * If a name does not exist, it will try to intelligently find a class with a singular version
  2601. * of the parent table model's name (e.g. <dfn>examples_model</dfn> = <dfn>example_model</dfn>)
  2602. *
  2603. * @access public
  2604. * @param string the table class name (not the record class)
  2605. * @return string
  2606. */
  2607. public function record_class_name()
  2608. {
  2609. $record_class = '';
  2610. if (empty($this->record_class))
  2611. {
  2612. $class_name = $this->short_name();
  2613. $record_class = substr(ucfirst($class_name), 0, -1);
  2614. // common change
  2615. $record_class = preg_replace('#ie$#', 'y', $record_class);
  2616. $record_class .= $this->suffix;
  2617. }
  2618. else
  2619. {
  2620. $record_class = ucfirst($this->record_class).$this->suffix;
  2621. }
  2622. if (strtolower($record_class) != strtolower(get_class($this)))
  2623. {
  2624. return $record_class;
  2625. }
  2626. else
  2627. {
  2628. return NULL;
  2629. }
  2630. }
  2631. // --------------------------------------------------------------------
  2632. /**
  2633. * Set's the default return method for methods like <dfn>get()</dfn>, <dfn>find_one</dfn> and <dfn>find_all</dfn>.
  2634. *
  2635. * Values can be <dfn>object</dfn>, <dfn>array</dfn>, <dfn>query</dfn>, <dfn>auto</dfn>
  2636. *
  2637. <code>
  2638. $this->examples_model->set_return_method('object');
  2639. $examples = $this->examples_model->find_all(); // an array of custom objects (if a custom object is defined. If not, a standard object will be used)
  2640. $this->examples_model->set_return_method('array');
  2641. $examples = $this->examples_model->find_all(); // an array of associative arrays is returned and will ignore any custom object
  2642. </code>
  2643. *
  2644. * @access public
  2645. * @param string return type (object, array, query, auto)
  2646. * @return void
  2647. */
  2648. public function set_return_method($return_method)
  2649. {
  2650. $this->return_method = $return_method;
  2651. }
  2652. // --------------------------------------------------------------------
  2653. /**
  2654. * Returns the return method used for querying (<dfn>object</dfn>, <dfn>array</dfn>, <dfn>query</dfn>, <dfn>auto</dfn>)
  2655. *
  2656. <code>
  2657. $return_method = $this->examples_model->get_return_method(); // object
  2658. </code>
  2659. *
  2660. * @access public
  2661. * @return mixed
  2662. */
  2663. public function get_return_method()
  2664. {
  2665. return $this->return_method;
  2666. }
  2667. // --------------------------------------------------------------------
  2668. /**
  2669. * Prints out to the screen the last query results ran by the model.
  2670. *
  2671. <code>
  2672. $this->examples_model->debug_data(); // prints out an array of information to the screen
  2673. </code>
  2674. *
  2675. * @access public
  2676. * @return mixed
  2677. */
  2678. public function debug_data()
  2679. {
  2680. if (!empty($this->last_data_set))
  2681. {
  2682. $this->last_data_set->debug();
  2683. }
  2684. else
  2685. {
  2686. echo 'Data empty';
  2687. }
  2688. }
  2689. // --------------------------------------------------------------------
  2690. /**
  2691. * Prints out to the screen the last query SQL ran by the model.
  2692. *
  2693. <code>
  2694. $this->examples_model->debug_query(); // prints out the last query run by the model
  2695. </code>
  2696. *
  2697. * @access public
  2698. * @return void
  2699. */
  2700. public function debug_query()
  2701. {
  2702. $this->db->debug_query();
  2703. }
  2704. // --------------------------------------------------------------------
  2705. /**
  2706. * Normalize the data to be saved so that it becomes an array and can be referenced by the 'normalized_save_data' property
  2707. *
  2708. <code>
  2709. $record = $this->examples_model->create();
  2710. $record->name = 'John Smith';
  2711. $values = $this->examples_model->normalize_save_values($record);
  2712. echo $values['name'] = 'John Smith';
  2713. </code>
  2714. *
  2715. * @access public
  2716. * @param mixed array of values to be saved
  2717. * @return array
  2718. */
  2719. public function normalize_save_values($record)
  2720. {
  2721. $CI =& get_instance();
  2722. if (!isset($record)) $record = $CI->input->post();
  2723. $values = $this->normalize_data($record);
  2724. $this->normalized_save_data = $values;
  2725. return $values;
  2726. }
  2727. // --------------------------------------------------------------------
  2728. /**
  2729. * Normailzes the data passed to it so that it becomes an array (used by the normalize_save_values)
  2730. *
  2731. <code>
  2732. $record = $this->examples_model->create();
  2733. $record->name = 'John Smith';
  2734. $values = $this->examples_model->normalize_data($record);
  2735. echo $values['name'] = 'John Smith';
  2736. </code>
  2737. *
  2738. * @access public
  2739. * @param mixed array of values
  2740. * @return array
  2741. */
  2742. public function normalize_data($data)
  2743. {
  2744. if (is_object($data))
  2745. {
  2746. if ($data instanceof Data_record)
  2747. {
  2748. $values = $data->values();
  2749. }
  2750. else
  2751. {
  2752. $values = get_object_vars($data);
  2753. }
  2754. }
  2755. else
  2756. {
  2757. $values = (array) $data;
  2758. }
  2759. return $values;
  2760. }
  2761. // --------------------------------------------------------------------
  2762. /**
  2763. * Processes $linked_fields and will convert any empty values with their corresponding linked field function
  2764. *
  2765. * @access public
  2766. * @param array values to be saved
  2767. * @return array
  2768. */
  2769. public function process_linked($values)
  2770. {
  2771. // process linked fields
  2772. foreach($this->linked_fields as $field => $func_val)
  2773. {
  2774. if (empty($values[$field]))
  2775. {
  2776. if (is_string($func_val) AND !empty($values[$func_val]))
  2777. {
  2778. // convenience for most common
  2779. $values[$field] = url_title($values[$func_val], 'dash', TRUE);
  2780. }
  2781. else if (is_array($func_val))
  2782. {
  2783. $func = current($func_val);
  2784. $val = key($func_val);
  2785. if (!empty($values[$val]))
  2786. {
  2787. $params = array($values[$val]);
  2788. if (is_array($func))
  2789. {
  2790. $f = array_shift($func);
  2791. $params = array_merge($params, $func);
  2792. $func = $f;
  2793. }
  2794. if (function_exists($func))
  2795. {
  2796. $values[$field] = call_user_func_array($func, $params);
  2797. }
  2798. else
  2799. {
  2800. $values[$field] = $values[$val];
  2801. }
  2802. }
  2803. }
  2804. }
  2805. }
  2806. return $values;
  2807. }
  2808. // --------------------------------------------------------------------
  2809. /**
  2810. * Process relationships
  2811. *
  2812. * @access public
  2813. * @param array values to be saved
  2814. * @return array
  2815. */
  2816. public function process_relationships($id)
  2817. {
  2818. $CI =& get_instance();
  2819. // handle has_many relationships
  2820. if ( ! empty($this->has_many))
  2821. {
  2822. $fields = $this->relationship_field_names('has_many');
  2823. $relationships_model = $this->load_model($fields['relationships_model']);
  2824. // first delete in case there are multiple saves to the same relationship table
  2825. foreach ($this->has_many as $related_field => $related_model)
  2826. {
  2827. $clear_on_save = ((strtoupper($this->clear_related_on_save) == 'AUTO' AND isset($this->normalized_save_data['exists_'.$related_field])) OR $this->clear_related_on_save === TRUE);
  2828. if ($clear_on_save)
  2829. {
  2830. // remove pre-existing relationships
  2831. if (!empty($fields['candidate_table']))
  2832. {
  2833. $del_where = array($fields['candidate_table'] => $this->table_name, $fields['candidate_key'] => $id);
  2834. }
  2835. else
  2836. {
  2837. $del_where = array($fields['candidate_key'] => $id);
  2838. }
  2839. $CI->$relationships_model->delete($del_where);
  2840. }
  2841. }
  2842. // then save
  2843. foreach ($this->has_many as $related_field => $related_model)
  2844. {
  2845. if ( ! empty($this->normalized_save_data[$related_field]))
  2846. {
  2847. $related_model = $this->load_related_model($related_model);
  2848. // create relationships
  2849. foreach ($this->normalized_save_data[$related_field] as $foreign_id)
  2850. {
  2851. $CI->$relationships_model->save(array($fields['candidate_table'] => $this->table_name, $fields['candidate_key'] => $id, $fields['foreign_table'] => $CI->$related_model->table_name, $fields['foreign_key'] => $foreign_id));
  2852. }
  2853. }
  2854. }
  2855. }
  2856. // handle belongs_to relationships
  2857. if ( ! empty($this->belongs_to))
  2858. {
  2859. $fields = $this->relationship_field_names('belongs_to');
  2860. $relationships_model = $this->load_model($fields['relationships_model']);
  2861. $related_models = array();
  2862. // first delete in case there are multiple saves to the same relationship table
  2863. foreach ($this->belongs_to as $related_field => $related_model)
  2864. {
  2865. // cache the loaded models here for reference below
  2866. $related_models[$related_field] =& $this->load_related_model($related_model);
  2867. $clear_on_save = ((strtoupper($this->clear_related_on_save) == 'AUTO' AND isset($this->normalized_save_data['exists_'.$related_field])) OR $this->clear_related_on_save === TRUE);
  2868. if ($clear_on_save)
  2869. {
  2870. // remove pre-existing relationships
  2871. if (!empty($fields['foreign_table']))
  2872. {
  2873. $del_where = array($fields['candidate_table'] => $CI->$related_models[$related_field]->table_name, $fields['foreign_table'] => $this->table_name, $fields['foreign_key'] => $id);
  2874. }
  2875. else
  2876. {
  2877. $del_where = array($fields['foreign_key'] => $id);
  2878. }
  2879. $CI->$relationships_model->delete($del_where);
  2880. }
  2881. }
  2882. // then save
  2883. foreach ($this->belongs_to as $related_field => $related_model)
  2884. {
  2885. if ( ! empty($this->normalized_save_data[$related_field]))
  2886. {
  2887. $related_model = $related_models[$related_field];
  2888. // create relationships
  2889. foreach ($this->normalized_save_data[$related_field] as $candidate_id)
  2890. {
  2891. $CI->$relationships_model->save(array($fields['foreign_table'] => $this->table_name, $fields['foreign_key'] => $id, $fields['candidate_table'] => $CI->$related_model->table_name, $fields['candidate_key'] => $candidate_id));
  2892. }
  2893. }
  2894. }
  2895. }
  2896. }
  2897. // --------------------------------------------------------------------
  2898. /**
  2899. * Process relationships before delete
  2900. *
  2901. * @access public
  2902. * @param array where condition for deleting
  2903. * @return void
  2904. */
  2905. public function process_relationships_delete($id)
  2906. {
  2907. $CI =& get_instance();
  2908. // clear out any relationships
  2909. if ( ! empty($this->has_many))
  2910. {
  2911. $fields = $this->relationship_field_names('has_many');
  2912. foreach ($this->has_many as $related_field => $related_model)
  2913. {
  2914. $relationships_model = $this->load_model($fields['relationships_model']);
  2915. if (!empty($fields['candidate_table']))
  2916. {
  2917. $del_where = array($fields['candidate_table'] => $this->table_name, $fields['candidate_key'] => $id);
  2918. }
  2919. else
  2920. {
  2921. $del_where = array($fields['candidate_key'] => $id);
  2922. }
  2923. $CI->$relationships_model->delete($del_where);
  2924. }
  2925. }
  2926. if ( ! empty($this->belongs_to))
  2927. {
  2928. $fields = $this->relationship_field_names('belongs_to');
  2929. foreach ($this->belongs_to as $related_field => $related_model)
  2930. {
  2931. $related_model = $this->load_related_model($related_model);
  2932. $relationships_model = $this->load_model($fields['relationships_model']);
  2933. if (!empty($fields['foreign_table']) AND !empty($fields['foreign_table']))
  2934. {
  2935. $del_where = array($fields['candidate_table'] => $CI->$related_model->table_name, $fields['foreign_table'] => $this->table_name, $fields['foreign_key'] => $id);
  2936. }
  2937. else
  2938. {
  2939. $del_where = array($fields['foreign_key'] => $id);
  2940. }
  2941. $CI->$relationships_model->delete($del_where);
  2942. }
  2943. }
  2944. }
  2945. // --------------------------------------------------------------------
  2946. /**
  2947. * Placeholder hook - right before cleaning of data
  2948. *
  2949. * @access public
  2950. * @param array values to be saved
  2951. * @return array
  2952. */
  2953. public function on_before_clean($values)
  2954. {
  2955. return $values;
  2956. }
  2957. // --------------------------------------------------------------------
  2958. /**
  2959. * Placeholder hook - right before validation of data
  2960. *
  2961. * @access public
  2962. * @param array values to be saved
  2963. * @return array
  2964. */
  2965. public function on_before_validate($values)
  2966. {
  2967. return $values;
  2968. }
  2969. // --------------------------------------------------------------------
  2970. /**
  2971. * Placeholder hook - right before insertion of data
  2972. *
  2973. * @access public
  2974. * @param array values to be saved
  2975. * @return array
  2976. */
  2977. public function on_before_insert($values)
  2978. {
  2979. return $values;
  2980. }
  2981. // --------------------------------------------------------------------
  2982. /**
  2983. * Placeholder hook - right after insertion of data
  2984. *
  2985. * @access public
  2986. * @param array values to be saved
  2987. * @return void
  2988. */
  2989. public function on_after_insert($values)
  2990. {
  2991. }
  2992. // --------------------------------------------------------------------
  2993. /**
  2994. * Placeholder hook - right before update of data
  2995. *
  2996. * @access public
  2997. * @param array values to be saved
  2998. * @return array
  2999. */
  3000. public function on_before_update($values)
  3001. {
  3002. return $values;
  3003. }
  3004. // --------------------------------------------------------------------
  3005. /**
  3006. * Placeholder hook - right after update of data
  3007. *
  3008. * @access public
  3009. * @param array values to be saved
  3010. * @return void
  3011. */
  3012. public function on_after_update($values)
  3013. {
  3014. }
  3015. // --------------------------------------------------------------------
  3016. /**
  3017. * Hook - right before saving of data
  3018. *
  3019. * @access public
  3020. * @param array values to be saved
  3021. * @return array
  3022. */
  3023. public function on_before_save($values)
  3024. {
  3025. return $values;
  3026. }
  3027. // --------------------------------------------------------------------
  3028. /**
  3029. * Hook - right after saving of data
  3030. *
  3031. * @access public
  3032. * @param array values to be saved
  3033. * @return array
  3034. */
  3035. public function on_after_save($values)
  3036. {
  3037. // process relationship values
  3038. $id = $this->_determine_key_field_value($values);
  3039. $this->process_relationships($id);
  3040. return $values;
  3041. }
  3042. // --------------------------------------------------------------------
  3043. /**
  3044. * Placeholder hook - right before delete
  3045. *
  3046. * @access public
  3047. * @param array where condition for deleting
  3048. * @return void
  3049. */
  3050. public function on_before_delete($where)
  3051. {
  3052. $id = $this->_determine_key_field_value($where);
  3053. $this->process_relationships_delete($id);
  3054. }
  3055. // --------------------------------------------------------------------
  3056. /**
  3057. * Placeholder hook - right after delete
  3058. *
  3059. * @access public
  3060. * @param array where condition for deleting
  3061. * @return void
  3062. */
  3063. public function on_after_delete($where)
  3064. {
  3065. }
  3066. // --------------------------------------------------------------------
  3067. /**
  3068. * Placeholder hook - for right after before processing a post. To be used outside of
  3069. * the saving process and must be called manually from your own code
  3070. *
  3071. * @access public
  3072. * @param array values to be saved
  3073. * @return array
  3074. */
  3075. public function on_before_post($values = array())
  3076. {
  3077. return $values;
  3078. }
  3079. // --------------------------------------------------------------------
  3080. /**
  3081. * Placeholder hook - for right after posting. To be used outside of
  3082. * the saving process and must be called manually from your own code
  3083. *
  3084. * @access public
  3085. * @param array values to be saved
  3086. * @return array
  3087. */
  3088. public function on_after_post($values)
  3089. {
  3090. return $values;
  3091. }
  3092. // --------------------------------------------------------------------
  3093. /**
  3094. * Placeholder hook - to inject extra information to a record object after
  3095. * duplicating a new record via $rec->duplicate();
  3096. *
  3097. * @access public
  3098. * @param array values to initialize object
  3099. * @return array
  3100. */
  3101. public function on_duplicate($values)
  3102. {
  3103. return $values;
  3104. }
  3105. // --------------------------------------------------------------------
  3106. /**
  3107. * Placeholder hook - to inject extra information to a record object after
  3108. * creating a new record via $this->my_model->create();
  3109. *
  3110. * @access public
  3111. * @param array values to initialize object
  3112. * @return array
  3113. */
  3114. public function on_create($values)
  3115. {
  3116. return $values;
  3117. }
  3118. // --------------------------------------------------------------------
  3119. /**
  3120. * Load another model
  3121. *
  3122. <code>
  3123. // load model from application directory
  3124. $this->load_model('my_model');
  3125. // load model from another module
  3126. $this->load_model(array('my_module' => 'my_model'));
  3127. </code>
  3128. *
  3129. * @access public
  3130. * @param mixed the name of the model. If an array, the key is the module and the name is the model
  3131. * @return string
  3132. */
  3133. public function load_model($model)
  3134. {
  3135. $CI =& get_instance();
  3136. if (is_array($model))
  3137. {
  3138. $module = key($model);
  3139. $m = current($model);
  3140. // TODO .... DECIDE IF WE SHOULD PASS THROUGH to format_model_name... the suffix may be different if configured
  3141. $CI->load->module_model($module, $m);
  3142. $return = $m;
  3143. }
  3144. else
  3145. {
  3146. $CI->load->model($model);
  3147. $return = $model;
  3148. }
  3149. $return = end(explode('/', $return));
  3150. return $return;
  3151. }
  3152. // --------------------------------------------------------------------
  3153. /**
  3154. * Load a related model
  3155. *
  3156. * @access public
  3157. * @param string the name of the model
  3158. * @return boolean
  3159. */
  3160. public function load_related_model($related_model)
  3161. {
  3162. // rearrange some params if model is an array
  3163. if (is_array($related_model))
  3164. {
  3165. if (isset($related_model['model']))
  3166. {
  3167. if (is_array($related_model['model']))
  3168. {
  3169. $module_model = $related_model['model'];
  3170. $related_model = array(
  3171. 'module' => key($module_model),
  3172. 'model' => current($module_model),
  3173. );
  3174. }
  3175. else
  3176. {
  3177. $related_model = $related_model['model'];
  3178. }
  3179. }
  3180. else
  3181. {
  3182. $module = key($related_model);
  3183. $model = current($related_model);
  3184. $related_model = array(
  3185. 'model' => $model,
  3186. 'module' => $module,
  3187. );
  3188. }
  3189. }
  3190. if (is_array($related_model))
  3191. {
  3192. if (is_array($related_model['model']))
  3193. {
  3194. $related_model = $this->load_model($related_model['model']);
  3195. }
  3196. else if (isset($related_model['module'], $related_model['model']))
  3197. {
  3198. $related_model = $this->load_model(array($related_model['module'] => $this->format_model_name($related_model['model'])));
  3199. }
  3200. }
  3201. else
  3202. {
  3203. $related_model = $this->load_model($this->format_model_name($related_model));
  3204. }
  3205. return $related_model;
  3206. }
  3207. // --------------------------------------------------------------------
  3208. /**
  3209. * Returns an array of the relationship field names to be used
  3210. *
  3211. * @access public
  3212. * @param string relationship type
  3213. * @return array
  3214. */
  3215. public function relationship_field_names($relationship_type)
  3216. {
  3217. $valid_rel_types = array('has_many', 'belongs_to');
  3218. if ( ! in_array($relationship_type, $valid_rel_types))
  3219. {
  3220. return FALSE;
  3221. }
  3222. if (empty($this->$relationship_type))
  3223. {
  3224. return FALSE;
  3225. }
  3226. $fields = array(
  3227. 'candidate_table' => 'candidate_table',
  3228. 'foreign_table' => 'foreign_table',
  3229. 'foreign_key' => 'foreign_key',
  3230. 'candidate_key' => 'candidate_key',
  3231. 'relationships_model'=> array(FUEL_FOLDER => 'fuel_relationships_model'),
  3232. );
  3233. foreach($this->$relationship_type as $key => $rel_config)
  3234. {
  3235. if (is_array($rel_config))
  3236. {
  3237. // loop
  3238. foreach($fields as $k => $v)
  3239. {
  3240. if (isset($rel_config[$k]))
  3241. {
  3242. $fields[$k] = $rel_config[$k];
  3243. }
  3244. }
  3245. }
  3246. }
  3247. return $fields;
  3248. }
  3249. // --------------------------------------------------------------------
  3250. /**
  3251. * Format a model's name
  3252. *
  3253. * @access public
  3254. * @param string the name of the model
  3255. * @return boolean
  3256. */
  3257. public function format_model_name($model)
  3258. {
  3259. $model_name = $model;
  3260. if (substr($model, -strlen($this->suffix)) != $this->suffix)
  3261. {
  3262. $model_name .= $this->suffix;
  3263. }
  3264. return $model_name;
  3265. }
  3266. /**
  3267. * Returns whether the relationship is using a pivot table
  3268. */
  3269. public function is_using_relationship_table($rel_config)
  3270. {
  3271. if (is_array($rel_config) AND array_key_exists('relationships_model', $rel_config) AND ($rel_config['relationships_model'] == FALSE)
  3272. AND array_key_exists('foreign_key', $rel_config) AND ! empty($rel_config['foreign_key'])
  3273. )
  3274. {
  3275. return FALSE;
  3276. }
  3277. else
  3278. {
  3279. return TRUE;
  3280. }
  3281. }
  3282. // --------------------------------------------------------------------
  3283. /**
  3284. * Serializes field values specified in the $serialized_fields property
  3285. *
  3286. * @access public
  3287. * @param string the field name to unserialize
  3288. * @return array
  3289. */
  3290. public function serialize_field_values($data)
  3291. {
  3292. // serialize any data
  3293. if (!empty($this->serialized_fields) AND is_array($data))
  3294. {
  3295. if ($this->_is_nested_array($data))
  3296. {
  3297. foreach($data as $key => $val)
  3298. {
  3299. $data[$key] = $this->serialize_field_values($val);
  3300. }
  3301. }
  3302. $method = NULL;
  3303. foreach($this->serialized_fields as $method => $field)
  3304. {
  3305. if (!is_numeric($method))
  3306. {
  3307. $method = ($method != 'json') ? 'serialize' : 'json';
  3308. }
  3309. if (isset($data[$field]))
  3310. {
  3311. $data[$field] = $this->serialize_value($data[$field], $method);
  3312. }
  3313. }
  3314. }
  3315. return $data;
  3316. }
  3317. // --------------------------------------------------------------------
  3318. /**
  3319. * Serialize a value to be saved
  3320. *
  3321. * @access public
  3322. * @param string the array value to unserialize
  3323. * @param string the unserialization method. If none is specified it will default to the default setting of the model which is 'json'
  3324. * @return array
  3325. */
  3326. public function serialize_value($val, $method = NULL)
  3327. {
  3328. if (empty($method))
  3329. {
  3330. $method = $this->default_serialization_method;
  3331. }
  3332. if (is_array($val))
  3333. {
  3334. if ($method == 'serialize')
  3335. {
  3336. $val = serialize($val);
  3337. }
  3338. else
  3339. {
  3340. $val = json_encode($val);
  3341. }
  3342. }
  3343. return $val;
  3344. }
  3345. // --------------------------------------------------------------------
  3346. /**
  3347. * Unserialize a field's value
  3348. *
  3349. * @access public
  3350. * @param mixed the data to unserialize. Can be an array of arrays too
  3351. * @return array
  3352. */
  3353. public function unserialize_field_values($data)
  3354. {
  3355. // unserialize any data
  3356. if (!empty($this->serialized_fields))
  3357. {
  3358. if ($this->_is_nested_array($data))
  3359. {
  3360. foreach($data as $key => $val)
  3361. {
  3362. $data[$key] = $this->unserialize_field_values($val);
  3363. }
  3364. }
  3365. else
  3366. {
  3367. $method = NULL;
  3368. foreach($this->serialized_fields as $method => $field)
  3369. {
  3370. if (!is_numeric($method))
  3371. {
  3372. $method = ($method != 'json') ? 'serialize' : 'json';
  3373. }
  3374. if (isset($data[$field]))
  3375. {
  3376. $data[$field] = $this->unserialize_value($data[$field], $method);
  3377. }
  3378. }
  3379. }
  3380. }
  3381. return $data;
  3382. }
  3383. // --------------------------------------------------------------------
  3384. /**
  3385. * Unserialize a saved value
  3386. *
  3387. * @access public
  3388. * @param string the array value to unserialize
  3389. * @param string the unserialization method. If none is specified it will default to the default setting of the model which is 'json'
  3390. * @return array
  3391. */
  3392. public function unserialize_value($val, $method = NULL)
  3393. {
  3394. // if not a valid field name, then we look for a key value with that name in the serialized_fields array
  3395. $invalid_field_names = array('json', 'serialize');
  3396. if (!in_array($method, $invalid_field_names))
  3397. {
  3398. foreach($this->serialized_fields as $m => $field)
  3399. {
  3400. if ($field == $method)
  3401. {
  3402. $method = $m;
  3403. break;
  3404. }
  3405. }
  3406. }
  3407. if (empty($method))
  3408. {
  3409. $method = $this->default_serialization_method;
  3410. }
  3411. if ($method == 'serialize')
  3412. {
  3413. if (is_serialized_str($val))
  3414. {
  3415. $val = unserialize($val);
  3416. }
  3417. }
  3418. else
  3419. {
  3420. if (is_json_str($val))
  3421. {
  3422. $val = json_decode($val, TRUE);
  3423. }
  3424. }
  3425. return $val;
  3426. }
  3427. // --------------------------------------------------------------------
  3428. /**
  3429. * Loads custom field classes so they can be instantiated when retrieved from the database
  3430. *
  3431. * @access public
  3432. * @param string the folder to look into from within either the module or application directory
  3433. * @return void
  3434. */
  3435. public function load_custom_field_classes($folder = 'libraries/custom_fields')
  3436. {
  3437. if (!is_array($this->custom_fields))
  3438. {
  3439. return;
  3440. }
  3441. // normalize slashes at the ends
  3442. $folder = trim($folder, '/');
  3443. foreach($this->custom_fields as $field => $custom_class)
  3444. {
  3445. if (is_array($custom_class))
  3446. {
  3447. $module = FUEL_FOLDER;
  3448. // look in a module folder
  3449. if (is_array($custom_class) AND isset($custom_class['model']))
  3450. {
  3451. $m = $custom_class['model'];
  3452. if (isset($custom_class['module']))
  3453. {
  3454. $module = $custom_class['module'];
  3455. }
  3456. }
  3457. else
  3458. {
  3459. $m = current($custom_class);
  3460. $module = key($custom_class);
  3461. }
  3462. $class_path = MODULES_PATH.$module.'/'.$folder.'/'.$m.EXT;
  3463. }
  3464. else
  3465. {
  3466. // look in the application folder
  3467. $m = $custom_class;
  3468. $class_path = APPPATH.$folder.'/'.$custom_class.EXT;
  3469. // check the FUEL folder if it doesn't exist
  3470. if (!file_exists($class_path))
  3471. {
  3472. $class_path = MODULES_PATH.FUEL_FOLDER.'/'.$folder.'/'.$m.EXT;
  3473. }
  3474. }
  3475. // load the class so it can be instantiated later
  3476. if (file_exists($class_path))
  3477. {
  3478. require_once($class_path);
  3479. }
  3480. else
  3481. {
  3482. // THROW ERROR
  3483. throw new Exception(lang('error_invalid_custom_class', $field));
  3484. }
  3485. }
  3486. }
  3487. // --------------------------------------------------------------------
  3488. /**
  3489. * Adds a formatter helper function
  3490. *
  3491. * @access public
  3492. * @param string the field type to apply the function to
  3493. * @param mixed the function
  3494. * @param string the alias to that function name (e.g. formatted = auto_typography)
  3495. * @return void
  3496. */
  3497. public function add_formatter($type, $func, $alias = NULL)
  3498. {
  3499. if (!empty($alias))
  3500. {
  3501. $this->formatters[$type][$alias] = $func;
  3502. }
  3503. else if (!isset($this->formatters[$type]) OR !in_array($func, $this->formatters[$type]))
  3504. {
  3505. $this->formatters[$type][] = $func;
  3506. }
  3507. }
  3508. // --------------------------------------------------------------------
  3509. /**
  3510. * Removes a formatter helper function
  3511. *
  3512. * @access public
  3513. * @param string the field type to apply the function to
  3514. * @param mixed the formatter key
  3515. * @return void
  3516. */
  3517. public function remove_formatter($type, $key)
  3518. {
  3519. if (!isset($this->formatters[$type]))
  3520. {
  3521. return FALSE;
  3522. }
  3523. if (isset($this->formatters[$type][$key]))
  3524. {
  3525. unset($this->formatters[$type][$key]);
  3526. }
  3527. else if (in_array($key, $this->formatters[$type]))
  3528. {
  3529. unset($this->formatters[$type][array_search($key, $this->formatters[$type])]);
  3530. }
  3531. }
  3532. // --------------------------------------------------------------------
  3533. /**
  3534. * Create safe where query parameters to avoid column name conflicts in queries
  3535. *
  3536. * @access protected
  3537. * @param mixed where condition
  3538. * @return mixed
  3539. */
  3540. protected function _safe_where($where)
  3541. {
  3542. if (is_array($where))
  3543. {
  3544. $new_where = array();
  3545. foreach($where as $key => $val)
  3546. {
  3547. $table_col = explode('.', $key);
  3548. // one less query if we use table_info instead of
  3549. // fields method since it's already been called and cached
  3550. $fields = array_keys($this->table_info());
  3551. if (empty($table_col[1]) AND in_array($table_col[0], $fields))
  3552. {
  3553. $key = $this->table_name.'.'.$key;
  3554. }
  3555. $new_where[$key] = $val;
  3556. }
  3557. return $new_where;
  3558. }
  3559. return $where;
  3560. }
  3561. // --------------------------------------------------------------------
  3562. /**
  3563. * Handle the where params and use where_in when values are arrays
  3564. *
  3565. * @access protected
  3566. * @param mixed where condition
  3567. */
  3568. protected function _handle_where($where = array())
  3569. {
  3570. if ( ! empty($where))
  3571. {
  3572. if (is_array($where))
  3573. {
  3574. foreach($where as $key => $val)
  3575. {
  3576. // check for nested array values to use for wherein
  3577. $method = (!empty($val) AND is_array($val)) ? 'where_in' : 'where';
  3578. $this->db->$method($key, $val);
  3579. }
  3580. }
  3581. else
  3582. {
  3583. $this->db->where($where);
  3584. }
  3585. }
  3586. }
  3587. // --------------------------------------------------------------------
  3588. /**
  3589. * Create safe where query parameters to avoid column name conflicts in queries
  3590. *
  3591. * @access protected
  3592. * @param mixed where condition
  3593. * @return mixed
  3594. */
  3595. protected function _check_readonly()
  3596. {
  3597. if ($this->readonly)
  3598. {
  3599. throw new Exception(lang('error_in_readonly_mode', get_class($this)));
  3600. }
  3601. }
  3602. // --------------------------------------------------------------------
  3603. /**
  3604. * Determines if there is a key field value in the array of values
  3605. *
  3606. * @access protected
  3607. * @param array values to be saved
  3608. * @return boolean
  3609. */
  3610. protected function _has_key_field_value($values)
  3611. {
  3612. $key_field = (array) $this->key_field;
  3613. $return = TRUE;
  3614. foreach($key_field as $key)
  3615. {
  3616. if (empty($values[$key]))
  3617. {
  3618. $return = FALSE;
  3619. break;
  3620. }
  3621. }
  3622. return $return;
  3623. }
  3624. // --------------------------------------------------------------------
  3625. /**
  3626. * Determines the id value based on an array or record
  3627. *
  3628. * @access protected
  3629. * @param array values to be saved
  3630. * @return boolean
  3631. */
  3632. protected function _determine_key_field_value($where)
  3633. {
  3634. $id = NULL;
  3635. $id_field = $this->key_field();
  3636. if (is_string($id_field))
  3637. {
  3638. if (is_object($where) AND $where instanceof Data_record)
  3639. {
  3640. $id = $where->$id_field;
  3641. }
  3642. else if (is_array($where) AND isset($where[$id_field]))
  3643. {
  3644. $id = $where[$id_field];
  3645. }
  3646. else if (!empty($id) AND is_int($id))
  3647. {
  3648. $id = $where;
  3649. }
  3650. }
  3651. return $id;
  3652. }
  3653. // --------------------------------------------------------------------
  3654. /**
  3655. * Determines if the value is an array of arrays
  3656. *
  3657. * @access protected
  3658. * @param mixed
  3659. * @return boolean
  3660. */
  3661. protected function _is_nested_array($record)
  3662. {
  3663. return (is_array($record) AND (is_int(key($record)) AND is_array(current($record))));
  3664. }
  3665. // --------------------------------------------------------------------
  3666. /**
  3667. * Replaces placeholder strings with values in an array
  3668. *
  3669. * @access protected
  3670. * @param mixed
  3671. * @return boolean
  3672. */
  3673. protected function _replace_placeholders($str, $values)
  3674. {
  3675. if (is_string($str))
  3676. {
  3677. if (strpos($str, '{') !== FALSE)
  3678. {
  3679. if (!empty($values))
  3680. {
  3681. foreach($values as $key => $val)
  3682. {
  3683. $str = str_replace('{'.$key.'}', $val, $str);
  3684. }
  3685. }
  3686. else
  3687. {
  3688. // returns nothing to prevent SQL errors
  3689. $str = '';
  3690. }
  3691. }
  3692. }
  3693. return $str;
  3694. }
  3695. // --------------------------------------------------------------------
  3696. /**
  3697. * What to print when echoing out this object
  3698. *
  3699. <code>
  3700. </code>
  3701. *
  3702. * @access public
  3703. * @return string
  3704. */
  3705. public function __toString()
  3706. {
  3707. return $this->table_name;
  3708. }
  3709. // --------------------------------------------------------------------
  3710. /**
  3711. * Magically create methods based on the following syntax
  3712. *
  3713. * find_all_by_{column1}_and_{column2}, find_one_by_{column1}_or_{column2}
  3714. *
  3715. * @access protected
  3716. * @param string name of the method called
  3717. * @param array arguments sent to the method call
  3718. * @return array
  3719. */
  3720. public function __call($name, $args)
  3721. {
  3722. $find_how_many = substr($name, 0, 8);
  3723. $find_where = substr($name, 8);
  3724. $find_and_or = preg_split("/_by_|(_and_)|(_or_)/", $find_where, -1, PREG_SPLIT_DELIM_CAPTURE);
  3725. if (!empty($find_and_or) AND strncmp($name, 'find', 4) == 0)
  3726. {
  3727. $arg_index = 0;
  3728. foreach($find_and_or as $key => $find)
  3729. {
  3730. if (empty($find) OR $find == '_and_')
  3731. {
  3732. $this->db->where(array($find_and_or[$key + 1] => $args[$arg_index]));
  3733. $arg_index++;
  3734. }
  3735. else if ($find == '_or_')
  3736. {
  3737. $this->db->or_where(array($find_and_or[$key + 1] => $args[$arg_index]));
  3738. $arg_index++;
  3739. }
  3740. }
  3741. $force_array = ($find_how_many == 'find_all') ? TRUE : FALSE;
  3742. $limit = NULL;
  3743. if (!$force_array)
  3744. {
  3745. $limit = 1;
  3746. }
  3747. else if (!empty($other_args[1]))
  3748. {
  3749. $limit = $other_args[1];
  3750. }
  3751. $other_args = array_slice($args, count($find_and_or) -1);
  3752. if (!empty($other_args[0])) $this->db->order_by($other_args[0]);
  3753. if (!empty($limit)) $this->db->limit($limit);
  3754. if (!empty($other_args[1])) $this->db->offset($other_args[2]);
  3755. return $this->get($force_array)->result();
  3756. }
  3757. throw new Exception(lang('error_method_does_not_exist', $name));
  3758. }
  3759. }
  3760. // --------------------------------------------------------------------
  3761. /**
  3762. * FUEL CMS
  3763. * http://www.getfuelcms.com
  3764. *
  3765. * An open source Content Management System based on the
  3766. * Codeigniter framework (http://codeigniter.com)
  3767. *
  3768. * @package FUEL CMS
  3769. * @author David McReynolds @ Daylight Studio
  3770. * @copyright Copyright (c) 2014, Run for Daylight LLC.
  3771. * @license http://docs.getfuelcms.com/general/license
  3772. * @link http://www.getfuelcms.com
  3773. */
  3774. // ------------------------------------------------------------------------
  3775. /**
  3776. * This class is a wrapper around the query results returned by the MY_Model class
  3777. *
  3778. * This class is instantiated by the Table Class (MY_Model) when a result set is needed.
  3779. * The Data_set class has a few methods to retrieve information about the data set.
  3780. *
  3781. * @package FUEL CMS
  3782. * @subpackage Libraries
  3783. * @category Libraries
  3784. * @author David McReynolds @ Daylight Studio
  3785. * @link http://docs.getfuelcms.com/libraries/my_model
  3786. * @prefix $data_set->
  3787. */
  3788. class Data_set {
  3789. protected $results; // the results array
  3790. protected $force_array; // return one or many
  3791. /**
  3792. * Constructor - requires a result set from MY_Model.
  3793. * @param array result set
  3794. * @param boolean returns 1 or many
  3795. */
  3796. public function __construct($result_array, $force_array = TRUE)
  3797. {
  3798. $this->results = $result_array;
  3799. $this->force_array = $force_array;
  3800. }
  3801. // --------------------------------------------------------------------
  3802. /**
  3803. * Returns the results of the query
  3804. *
  3805. <code>
  3806. $single_result_set = $this->examples_model->get(FALSE);
  3807. $example = $single_result_set->result();
  3808. echo $example->name;
  3809. // If multiple result sets are needed, then the result method will return multiple result objects/arrays like so:
  3810. $multiple_result_set = $this->examples_model->get(TRUE);
  3811. foreach($multiple_result_set as $example)
  3812. {
  3813. echo $example->name;
  3814. }
  3815. </code>
  3816. *
  3817. * @access public
  3818. * @param array values to be saved
  3819. * @return array
  3820. */
  3821. public function result(){
  3822. if(empty($this->results))
  3823. {
  3824. return array();
  3825. }
  3826. return (!$this->force_array) ? $this->results[0] : $this->results;
  3827. }
  3828. // --------------------------------------------------------------------
  3829. /**
  3830. * Determines if there is a key field value in the array of values
  3831. *
  3832. <code>
  3833. $multiple_result_set = $this->examples_model->get(TRUE);
  3834. echo $multiple_result_set->num_records();
  3835. </code>
  3836. *
  3837. * @access public
  3838. * @param array values to be saved
  3839. * @return boolean
  3840. */
  3841. public function num_records(){
  3842. if(empty($this->results))
  3843. {
  3844. return 0;
  3845. }
  3846. return count($this->results);
  3847. }
  3848. // --------------------------------------------------------------------
  3849. /**
  3850. * Debug data sets
  3851. *
  3852. <code>
  3853. $multiple_result_set = $this->examples_model->get(TRUE);
  3854. $multiple_result_set->debug();
  3855. </code>
  3856. *
  3857. * @access public
  3858. * @return void
  3859. */
  3860. public function debug()
  3861. {
  3862. echo '<pre>';
  3863. if (isset($this->results[0]))
  3864. {
  3865. foreach($this->results as $key =>$data)
  3866. {
  3867. echo '['.$key.']';
  3868. echo "\t ".$data;
  3869. }
  3870. }
  3871. else
  3872. {
  3873. echo $this->results;
  3874. }
  3875. echo '</pre>';
  3876. }
  3877. // --------------------------------------------------------------------
  3878. /**
  3879. * Debug data sets
  3880. *
  3881. <code>
  3882. </code>
  3883. *
  3884. * @access public
  3885. * @return void
  3886. */
  3887. public function __toString(){
  3888. $str = '<pre>';
  3889. $str .= $this->force_array;
  3890. $str .= print_r($this->results, true);
  3891. $str .= '</pre>';
  3892. return $str;
  3893. }
  3894. }
  3895. // --------------------------------------------------------------------
  3896. /**
  3897. * FUEL CMS
  3898. * http://www.getfuelcms.com
  3899. *
  3900. * An open source Content Management System based on the
  3901. * Codeigniter framework (http://codeigniter.com)
  3902. *
  3903. * @package FUEL CMS
  3904. * @author David McReynolds @ Daylight Studio
  3905. * @copyright Copyright (c) 2014, Run for Daylight LLC.
  3906. * @license http://docs.getfuelcms.com/general/license
  3907. * @link http://www.getfuelcms.com
  3908. */
  3909. // ------------------------------------------------------------------------
  3910. /**
  3911. * This class can be extended to return custom record objects for MY_Model
  3912. *
  3913. * The Data_record class is used to create custom record objects for a Table class (MY_Model).
  3914. * Data_record objects provides a greater level of flexibility with your models by allowing you to create not only
  3915. * methods on your model to retreive records from your datasource, but also the ability to create
  3916. * derived attributes and lazy load other objects with each record returned.
  3917. * This class is <strong>optional</strong>. If it it doesn't exist, then the Table Class parent model
  3918. * will use either a standard generic class or an array depending on the return method specified.
  3919. *
  3920. * @package FUEL CMS
  3921. * @subpackage Libraries
  3922. * @category Libraries
  3923. * @author David McReynolds @ Daylight Studio
  3924. * @link http://docs.getfuelcms.com/libraries/my_model
  3925. * @prefix $record->
  3926. */
  3927. class Data_record {
  3928. protected $_CI = NULL; // global CI object
  3929. protected $_db = NULL; // database object
  3930. protected $_fields = array(); // fields of the record
  3931. protected $_objs = array(); // nested objects
  3932. protected $_parent_model = NULL; // the name of the parent model
  3933. protected $_inited = FALSE; // returns whether the object has been initiated or not
  3934. /**
  3935. * Constructor - requires a result set from MY_Model.
  3936. * @param object parent object
  3937. */
  3938. public function __construct(&$parent = NULL)
  3939. {
  3940. $this->_CI =& get_instance();
  3941. if (!empty($parent)) $this->initialize($parent);
  3942. }
  3943. // --------------------------------------------------------------------
  3944. /**
  3945. * Initializes the class with the parent model and field names
  3946. *
  3947. * @access public
  3948. * @param object parent model object
  3949. * @param array field names
  3950. * @return array
  3951. */
  3952. public function initialize(&$parent, $fields = array())
  3953. {
  3954. $this->_parent_model = $parent;
  3955. $this->_db = $this->_parent_model->db();
  3956. if (empty($this->_fields))
  3957. {
  3958. // auto create fields based on table
  3959. foreach($fields as $key => $val)
  3960. {
  3961. if (is_array($val))
  3962. {
  3963. $this->_fields[$key] = $val['default'];
  3964. }
  3965. else
  3966. {
  3967. $this->_fields[$val] = NULL;
  3968. }
  3969. }
  3970. }
  3971. $class_vars = get_class_vars(get_class($this));
  3972. $this->set_fields(array_merge($this->_fields, $class_vars));
  3973. $this->on_init();
  3974. $this->_inited = TRUE;
  3975. }
  3976. // --------------------------------------------------------------------
  3977. /**
  3978. * This method returns either <dfn>TRUE</dfn> or <dfn>FALSE</dfn> depending on if the record class has been properly intialized.
  3979. *
  3980. <code>
  3981. $record = $this->examples_model->create();
  3982. $record->is_initialized(); // Returns TRUE
  3983. </code>
  3984. *
  3985. * @access public
  3986. * @return boolean
  3987. */
  3988. public function is_initialized()
  3989. {
  3990. return $this->_inited;
  3991. }
  3992. // --------------------------------------------------------------------
  3993. /**
  3994. * Sets the fields of the oject ignoring those fields prefixed with an underscore
  3995. *
  3996. <code>
  3997. $fields = array('id', 'name', 'email');
  3998. $record = $this->examples_model->set_fields($fields);
  3999. </code>
  4000. *
  4001. * @access public
  4002. * @param array field names
  4003. * @return void
  4004. */
  4005. public function set_fields($fields)
  4006. {
  4007. $filtered_fields = array();
  4008. foreach($fields as $key => $val)
  4009. {
  4010. if (strncmp($key, '_', 1) !== 0)
  4011. {
  4012. $filtered_fields[$key] = $val;
  4013. }
  4014. }
  4015. $this->_fields = $filtered_fields;
  4016. }
  4017. // --------------------------------------------------------------------
  4018. /**
  4019. * Returns the id field name
  4020. *
  4021. <code>
  4022. $record->id(); // Returns id
  4023. </code>
  4024. *
  4025. * @access public
  4026. * @param array field values
  4027. * @return void
  4028. */
  4029. public function id()
  4030. {
  4031. return $this->_parent_model->key_field();
  4032. }
  4033. // --------------------------------------------------------------------
  4034. /**
  4035. * Sets the values of the fields
  4036. *
  4037. <code>
  4038. $record = $this->examples_model->create();
  4039. $record->fill($_POST); // Be sure to always clean your $_POST variables before using them
  4040. </code>
  4041. *
  4042. * @access public
  4043. * @param array field values
  4044. * @return void
  4045. */
  4046. public function fill($values = array())
  4047. {
  4048. if (!is_array($values)) return FALSE;
  4049. foreach($values as $key => $val)
  4050. {
  4051. if ($this->prop_exists($key))
  4052. {
  4053. if ($this->_parent_model->field_type($key) == 'number' AND is_numeric($val))
  4054. {
  4055. $field_info = $this->_parent_model->field_info($key);
  4056. if ($field_info['type'] == 'float' OR $field_info['type'] == 'decimal')
  4057. {
  4058. $this->$key = (float) $val;
  4059. }
  4060. else
  4061. {
  4062. $this->$key = (int) $val;
  4063. }
  4064. }
  4065. else
  4066. {
  4067. $this->$key = $val;
  4068. }
  4069. }
  4070. }
  4071. }
  4072. // --------------------------------------------------------------------
  4073. /**
  4074. * Returns an array of the record's values.
  4075. *
  4076. <code>
  4077. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4078. $values = $record->values()
  4079. echo $values['email']; // vader@deathstar.com
  4080. </code>
  4081. *
  4082. * @access public
  4083. * @param boolean Determins whether to include derived attributes (those starting with get_)
  4084. * @return array
  4085. */
  4086. public function values($include_derived = FALSE)
  4087. {
  4088. $values = array();
  4089. $class_vars = get_class_vars(get_class($this));
  4090. foreach(get_object_vars($this) as $key => $val)
  4091. {
  4092. if ((array_key_exists($key, $class_vars) AND strncmp($key, '_', 1) !== 0))
  4093. {
  4094. $values[$key] = $val;
  4095. }
  4096. }
  4097. if ($include_derived)
  4098. {
  4099. $methods = get_class_methods($this);
  4100. $reflection = new ReflectionClass(get_class($this));
  4101. foreach($methods as $method)
  4102. {
  4103. if (strncmp($method, 'get_', 4) === 0 AND $reflection->getMethod($method)->getNumberOfParameters() == 0)
  4104. {
  4105. $key = substr($method, 4); // remove get_
  4106. $values[$key] = $this->$method();
  4107. }
  4108. }
  4109. }
  4110. $values = array_merge($this->_fields, $values);
  4111. return $values;
  4112. }
  4113. // --------------------------------------------------------------------
  4114. /**
  4115. * Duplicates the record with all it's current values
  4116. *
  4117. <code>
  4118. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4119. $duplicate_record = $record->duplicate()
  4120. echo $duplicate_record->email; // vader@deathstar.com
  4121. </code>
  4122. *
  4123. * @access public
  4124. * @return object
  4125. */
  4126. public function duplicate()
  4127. {
  4128. $dup = $this->_parent_model->create();
  4129. $values = $this->values();
  4130. // call on duplicate method
  4131. $values = $this->_parent_model->on_duplicate($values);
  4132. $dup->fill($$values);
  4133. // NULL out key values so as not to overwrite existing objects
  4134. $key_field = (array) $this->_parent_model->key_field();
  4135. foreach($key_field as $key)
  4136. {
  4137. $dup->$key = NULL;
  4138. }
  4139. return $dup;
  4140. }
  4141. // --------------------------------------------------------------------
  4142. /**
  4143. * Saves all the properties of the object.
  4144. *
  4145. <code>
  4146. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4147. $record->email = 'hsolo@milleniumfalcon.com';
  4148. $record->save();
  4149. </code>
  4150. *
  4151. * @access public
  4152. * @param boolean validate before saving?
  4153. * @param boolean ignore on insert
  4154. * @return boolean
  4155. */
  4156. public function save($validate = TRUE, $ignore_on_insert = TRUE, $clear_related = NULL)
  4157. {
  4158. return $this->_parent_model->save($this, $validate, $ignore_on_insert, $clear_related);
  4159. }
  4160. // --------------------------------------------------------------------
  4161. /**
  4162. * Validates the values of the object to makes sure they are valid to be saved.
  4163. *
  4164. <code>
  4165. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4166. $record->email = 'hsolomilleniumfalcon.com'; // note the invalid email address
  4167. if ($record->validate())
  4168. {
  4169. echo 'VALID';
  4170. }
  4171. else
  4172. {
  4173. echo 'Please fill out a valid email address';
  4174. }
  4175. </code>
  4176. *
  4177. * @access public
  4178. * @return boolean
  4179. */
  4180. public function validate()
  4181. {
  4182. return $this->_parent_model->validate($this);
  4183. }
  4184. // --------------------------------------------------------------------
  4185. /**
  4186. * Returns <dfn>TRUE</dfn> or <dfn>FALSE</dfn> depending on if validation has been run and is valid.
  4187. *
  4188. <p class="important">The validate <strong>method</strong> must be called before calling <strong>is_valid</strong>.</p>
  4189. <code>
  4190. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4191. $record->email = 'hsolomilleniumfalcon.com'; // note the invalid email address
  4192. $record->validate();
  4193. ... other code ...
  4194. if ($record->is_valid())
  4195. {
  4196. echo 'VALID';
  4197. }
  4198. else
  4199. {
  4200. echo 'Please fill out a valid email address';
  4201. }
  4202. </code>
  4203. *
  4204. * @access public
  4205. * @return boolean
  4206. */
  4207. public function is_valid()
  4208. {
  4209. return $this->_parent_model->is_valid();
  4210. }
  4211. // --------------------------------------------------------------------
  4212. /**
  4213. * Returns an array of error messages if there were any found after validating.
  4214. * This is commonly called after saving because validation will occur automatically on save.
  4215. *
  4216. <code>
  4217. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4218. $record->email = 'hsolomilleniumfalcon.com'; // note the invalid email address
  4219. if (!$record->save())
  4220. {
  4221. foreach($record->errors as $error)
  4222. {
  4223. echo $error;
  4224. }
  4225. }
  4226. </code>
  4227. *
  4228. * @access public
  4229. * @return array
  4230. */
  4231. public function errors()
  4232. {
  4233. return $this->_parent_model->get_errors();
  4234. }
  4235. // --------------------------------------------------------------------
  4236. /**
  4237. * Deletes the record. Similar to the save method, it will call the parent model's delete method passing itself as the where condition to delete
  4238. *
  4239. <code>
  4240. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4241. $record->delete(); // note the invalid email address
  4242. </code>
  4243. *
  4244. * @access public
  4245. * @return boolean
  4246. */
  4247. public function delete()
  4248. {
  4249. return $this->_parent_model->delete($this);
  4250. }
  4251. // --------------------------------------------------------------------
  4252. /**
  4253. * Refreshes the object from the data source.
  4254. *
  4255. <code>
  4256. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4257. $record->email = 'hsolo@milleniumfalcon.com';
  4258. $record->refresh();
  4259. echo $record->email; // dvader@deathstar.com
  4260. </code>
  4261. *
  4262. * @access public
  4263. * @return void
  4264. */
  4265. public function refresh()
  4266. {
  4267. $key_field = (array) $this->_parent_model->key_field();
  4268. foreach($key_field as $key)
  4269. {
  4270. $where[$key] = $this->$key;
  4271. }
  4272. $data = $this->_parent_model->find_one($where, NULL, 'array');
  4273. $this->fill($data);
  4274. }
  4275. // --------------------------------------------------------------------
  4276. /**
  4277. * Will load another model's record object and is often used in custom derived attributes.
  4278. *
  4279. <code>
  4280. public function get_spaceship()
  4281. {
  4282. $ship = $this->lazy_load(array('email' => 'hsolo@milleniumfalcon.com'), 'spacehips_model', FALSE);
  4283. return $ship;
  4284. }
  4285. </code>
  4286. *
  4287. * @access public
  4288. * @param mixed where conditions
  4289. * @param string model name
  4290. * @param boolean return 1 or many
  4291. * @param array an array of extra parameters to pass to the query method. Also, can pass assoc_key and return_ (e.g. join, order_by, limit, etc)
  4292. * @param string the key to the _obj property to store the lazy loaded object
  4293. * @return array
  4294. */
  4295. public function lazy_load($where, $model, $multiple = FALSE, $params = array(), $cache_key = '')
  4296. {
  4297. // call this first so that the model value is set for the cache_key
  4298. $model = $this->_parent_model->load_model($model);
  4299. if ($cache_key == '')
  4300. {
  4301. $model_str = '';
  4302. if (is_array($model))
  4303. {
  4304. $model_str = key($model);
  4305. $model_str .= current($model);
  4306. }
  4307. elseif (is_string($model))
  4308. {
  4309. $model_str = $model;
  4310. }
  4311. if (is_array($where))
  4312. {
  4313. $cache_key = implode('_', array_keys($where)).'_'.$model_str;
  4314. }
  4315. else
  4316. {
  4317. $cache_key = str_replace(' ', '_', $where).'_'.$model_str;
  4318. }
  4319. }
  4320. if (!empty($this->_objs[$cache_key]) AND $cache_key !== FALSE) return $this->_objs[$cache_key];
  4321. // set the readonly to the callers
  4322. $this->_CI->$model->readonly = $this->_parent_model->readonly;
  4323. $foreign_key = $this->_CI->$model->table_name().'.'.$this->_CI->$model->key_field();
  4324. if (is_string($where))
  4325. {
  4326. $params['where'] = array($foreign_key => $this->$where);
  4327. }
  4328. else
  4329. {
  4330. $params['where'] = $where;
  4331. }
  4332. $assoc_key = (isset($params['assoc_key'])) ? $params['assoc_key'] : NULL;
  4333. $return_method = (isset($params['return_method'])) ? $params['return_method'] : NULL;
  4334. $use_common_query = (isset($params['use_common_query'])) ? $params['use_common_query'] : TRUE;
  4335. $this->_CI->$model->query($params, FALSE);
  4336. $query = $this->_CI->$model->get($multiple, $return_method, $assoc_key, $use_common_query);
  4337. $data = $query->result();
  4338. $this->_objs[$cache_key] = $data;
  4339. // create an empty object
  4340. if (empty($this->_objs[$cache_key]))
  4341. {
  4342. return FALSE;
  4343. }
  4344. return $this->_objs[$cache_key];
  4345. }
  4346. // --------------------------------------------------------------------
  4347. /**
  4348. * Returns the parent model object
  4349. *
  4350. <code>
  4351. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4352. // Same as above
  4353. $record->parent_model()->find_one(array('email' => 'dvader@deathstar.com');
  4354. </code>
  4355. *
  4356. * @access public
  4357. * @return object
  4358. */
  4359. public function parent_model()
  4360. {
  4361. return $this->_parent_model;
  4362. }
  4363. // --------------------------------------------------------------------
  4364. /**
  4365. * Tests whether a property exists on the record
  4366. *
  4367. <code>
  4368. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4369. if ($record->prop_exists('email')))
  4370. {
  4371. echo $record->email;
  4372. }
  4373. </code>
  4374. *
  4375. * @access public
  4376. * @param key field names
  4377. * @return boolean
  4378. */
  4379. public function prop_exists($key)
  4380. {
  4381. return (array_key_exists($key, $this->_fields) OR property_exists($this, $key));
  4382. }
  4383. // --------------------------------------------------------------------
  4384. /**
  4385. * Is empty check
  4386. *
  4387. <code>
  4388. </code>
  4389. *
  4390. * @access public
  4391. * @return boolean
  4392. */
  4393. public function is_empty()
  4394. {
  4395. return empty($this->_fields) AND get_class_vars(__CLASS_);
  4396. }
  4397. // --------------------------------------------------------------------
  4398. /**
  4399. * Prints to the screen the last query run by the parent model. An alias to the parent model's debug_query method.
  4400. *
  4401. <code>
  4402. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4403. $record->debug_query()))
  4404. </code>
  4405. *
  4406. * @access public
  4407. * @param object parent model object
  4408. * @param array field names
  4409. * @return array
  4410. */
  4411. public function debug_query()
  4412. {
  4413. $this->_parent_model->db()->debug_query();
  4414. }
  4415. // --------------------------------------------------------------------
  4416. /**
  4417. * Prints to the screen the property values of the of the object.
  4418. *
  4419. <code>
  4420. $record = $this->examples_model->find_one(array('email' => 'dvader@deathstar.com'));
  4421. $record->debug_data()))
  4422. </code>
  4423. *
  4424. * @access public
  4425. * @return void
  4426. */
  4427. public function debug_data()
  4428. {
  4429. echo $this->__toString();
  4430. }
  4431. // --------------------------------------------------------------------
  4432. /**
  4433. * Placeholder - executed after insertion of data
  4434. *
  4435. * @access public
  4436. * @param array field values
  4437. * @return void
  4438. */
  4439. public function on_after_insert($values)
  4440. {
  4441. $key_field = $this->_parent_model->key_field();
  4442. if (is_string($key_field))
  4443. {
  4444. $this->_fields[$key_field] = $this->_db->insert_id();
  4445. }
  4446. }
  4447. // --------------------------------------------------------------------
  4448. /**
  4449. * Placeholder - executed after update of data
  4450. *
  4451. * @access public
  4452. * @param array field values
  4453. * @return void
  4454. */
  4455. public function on_after_update($values)
  4456. {
  4457. }
  4458. // --------------------------------------------------------------------
  4459. /**
  4460. * Placeholder - executed after initialization
  4461. *
  4462. * @access public
  4463. * @return vpod
  4464. */
  4465. public function on_init()
  4466. {
  4467. }
  4468. // --------------------------------------------------------------------
  4469. /**
  4470. * Placeholder - to execute before a magic method get
  4471. *
  4472. * @access public
  4473. * @param string output from get comand
  4474. * @param string field name
  4475. * @return mixed
  4476. */
  4477. public function before_set($output, $var)
  4478. {
  4479. return $output;
  4480. }
  4481. // --------------------------------------------------------------------
  4482. /**
  4483. * Placeholder - to execute after a magic method get
  4484. *
  4485. * @access public
  4486. * @param string output from get comand
  4487. * @param string field name
  4488. * @return mixed
  4489. */
  4490. public function after_get($output, $var)
  4491. {
  4492. return $output;
  4493. }
  4494. // --------------------------------------------------------------------
  4495. /**
  4496. * Returns a JSON encoded string of the record values
  4497. *
  4498. * @access public
  4499. * @param boolean Determins whether to include derived attributes (those starting with get_)
  4500. * @return string
  4501. */
  4502. public function to_json($include_derived = TRUE)
  4503. {
  4504. $values = $this->values($include_derived);
  4505. return json_encode($values);
  4506. }
  4507. // --------------------------------------------------------------------
  4508. /**
  4509. * Will format a particular field given the functions passed to it
  4510. *
  4511. * @access public
  4512. * @param string the field name
  4513. * @param mixed an array or string value representing one ore more functions to use for formatting
  4514. * @param array additional arguments to pass to the formatting function. Not applicable when passing multiple formatting functions (optional)
  4515. * @return string
  4516. */
  4517. public function format($field, $funcs = array(), $args = array())
  4518. {
  4519. $formatters = $this->_parent_model->formatters;
  4520. if (!isset($this->_fields[$field]))
  4521. {
  4522. return NULL;
  4523. }
  4524. $value = $this->_fields[$field];
  4525. $type = NULL;
  4526. // look through the list of helpers to find a related function
  4527. $types = array_keys($formatters);
  4528. // check the array first to see if there is a type that matches the fields name
  4529. if (in_array($field, $types))
  4530. {
  4531. $type = $field;
  4532. }
  4533. // otherwise, we'll do a preg_match on the field keys to see if we have any matches
  4534. else
  4535. {
  4536. foreach($types as $t)
  4537. {
  4538. if (preg_match('#'.$t.'#', $field))
  4539. {
  4540. $type = $t;
  4541. break;
  4542. }
  4543. }
  4544. }
  4545. // still no matches?... then we grab the type from the parent model (e.g. string, datetime, number, etc)
  4546. if (empty($type))
  4547. {
  4548. // first check the type value fo the field
  4549. $type = $this->_parent_model->field_type($field);
  4550. }
  4551. // check to make sure the $type exists for the filters
  4552. if (!isset($formatters[$type]))
  4553. {
  4554. return $value;
  4555. }
  4556. $type_formatters = $formatters[$type];
  4557. // if the helpers are a string, then we split it into an array
  4558. if (is_string($type_formatters))
  4559. {
  4560. $type_formatters = preg_split('#,\s*|\|#', $type_formatters);
  4561. // cache the split here so we don't need to split it again
  4562. $this->_parent_model->formatters[$type] = $type_formatters;
  4563. }
  4564. // if the called funcs to apply is a string, then we split into an array
  4565. if (is_string($funcs))
  4566. {
  4567. $funcs = preg_split('#,\s*|\|#', $funcs);
  4568. }
  4569. $func_args = array_merge(array($value), $args);
  4570. if (is_array($funcs))
  4571. {
  4572. foreach($funcs as $func)
  4573. {
  4574. $f = NULL;
  4575. if (isset($type_formatters[$func]))
  4576. {
  4577. $f = $type_formatters[$func];
  4578. // if it is an array, we pass everything after the first argument to be arguments for the function
  4579. if (is_array($f))
  4580. {
  4581. $f_args = $f;
  4582. $f = current($f);
  4583. array_shift($f_args);
  4584. $args = array_merge($f_args, $args);
  4585. }
  4586. }
  4587. else if (in_array($func, $type_formatters))
  4588. {
  4589. $f = $func;
  4590. }
  4591. // check the current record object for a method, and if exists, use that instead
  4592. if (method_exists($this, $f))
  4593. {
  4594. $f = array($this, $f);
  4595. }
  4596. // apply function if it exists to the value
  4597. if (is_callable($f))
  4598. {
  4599. $func_args = array_merge(array($value), $args);
  4600. $value = call_user_func_array($f, $func_args);
  4601. }
  4602. }
  4603. }
  4604. return $value;
  4605. }
  4606. // --------------------------------------------------------------------
  4607. /**
  4608. * String value of this object is it's values in an array format
  4609. *
  4610. * @access public
  4611. * @return string
  4612. */
  4613. public function __toString()
  4614. {
  4615. $str = '<pre>';
  4616. $str .= print_r($this->values(), true);
  4617. $str .= '</pre>';
  4618. return $str;
  4619. }
  4620. // --------------------------------------------------------------------
  4621. /**
  4622. * Magic method for capturing method calls on the record object that don't exist. Allows for "get_{field}" to map to just "{field}" as well as "is_{field}"" and "has_{field}"
  4623. *
  4624. * @access public
  4625. * @param object method name
  4626. * @param array arguments
  4627. * @return array
  4628. */
  4629. public function __call($method, $args)
  4630. {
  4631. if (preg_match( "/^set_(.*)/", $method, $found))
  4632. {
  4633. if (array_key_exists($found[1], $this->_fields))
  4634. {
  4635. $this->_fields[$found[1]] = $args[0];
  4636. return TRUE;
  4637. }
  4638. }
  4639. else if (preg_match("/^get_(.*)/", $method, $found))
  4640. {
  4641. if ($this->_is_relationship_property($found[1], 'has_many'))
  4642. {
  4643. $return_object = (isset($args[0])) ? $args[0] : FALSE;
  4644. return $this->_get_relationship($found[1], $return_object,'has_many');
  4645. }
  4646. else if ($this->_is_relationship_property($found[1], 'belongs_to'))
  4647. {
  4648. $return_object = (isset($args[0])) ? $args[0] : FALSE;
  4649. return $this->_get_relationship($found[1], $return_object,'belongs_to');
  4650. }
  4651. else if (array_key_exists($found[1], $this->_fields))
  4652. {
  4653. return $this->_fields[$found[1]];
  4654. }
  4655. }
  4656. else if (preg_match("/^is_(.*)/", $method, $found))
  4657. {
  4658. if (array_key_exists($found[1], $this->_fields))
  4659. {
  4660. $field = $this->_parent_model->field_info($found[1]);
  4661. if (!empty($field) AND (($field['type'] == 'enum' AND count($field['options']) == 2) OR in_array($found[1], $this->_parent_model->boolean_fields)))
  4662. {
  4663. return is_true_val($this->_fields[$found[1]]);
  4664. }
  4665. }
  4666. }
  4667. else if (preg_match("/^has_(.*)/", $method, $found))
  4668. {
  4669. if (array_key_exists($found[1], $this->_fields))
  4670. {
  4671. return !empty($this->_fields[$found[1]]);
  4672. }
  4673. }
  4674. else
  4675. {
  4676. // // take the field name plus a '_' to get the suffix
  4677. $suffix = substr(strrchr($method, '_'), 1);
  4678. // get the core field name without the suffix (+1 because of underscore)
  4679. $field = substr($method, 0, - (strlen($suffix) + 1));
  4680. return $this->format($field, $suffix, $args);
  4681. }
  4682. return FALSE;
  4683. }
  4684. // --------------------------------------------------------------------
  4685. /**
  4686. * Magic method to set first property, method, then field values
  4687. *
  4688. * @access public
  4689. * @param string field name
  4690. * @param mixed
  4691. * @return void
  4692. */
  4693. public function __set($var, $val)
  4694. {
  4695. $val = $this->before_set($val, $var);
  4696. $foreign_keys = $this->_parent_model->foreign_keys;
  4697. if (property_exists($this, $var))
  4698. {
  4699. $this->$var = $val;
  4700. }
  4701. else if (method_exists($this, 'set_'.$var))
  4702. {
  4703. $set_method = "set_".$var;
  4704. $this->$set_method($val);
  4705. }
  4706. // set in foreign keys only if it is an object
  4707. else if (is_object($val) AND ($val instanceof Data_record) AND in_array($var.'_id', array_keys($foreign_keys)))
  4708. {
  4709. $this->_fields[$var] = $val;
  4710. }
  4711. else if (array_key_exists($var, $this->_fields))
  4712. {
  4713. $this->_fields[$var] = $val;
  4714. }
  4715. else if ($this->_is_relationship_property($var, 'has_many'))
  4716. {
  4717. $this->_fields[$var] = $val;
  4718. }
  4719. else
  4720. {
  4721. throw new Exception('property '.$var.' does not exist.');
  4722. }
  4723. }
  4724. // --------------------------------------------------------------------
  4725. /**
  4726. * Magic method to return first property, method, then field values
  4727. *
  4728. * @access public
  4729. * @param string field name
  4730. * @return mixed
  4731. */
  4732. public function __get($var)
  4733. {
  4734. $output = NULL;
  4735. $foreign_keys = $this->_parent_model->foreign_keys;
  4736. $custom_fields = $this->_parent_model->custom_fields;
  4737. // first class property has precedence
  4738. if (property_exists($this, $var))
  4739. {
  4740. $output = $this->$var;
  4741. }
  4742. // check a get_{method}
  4743. else if (method_exists($this, "get_".$var))
  4744. {
  4745. $get_method = "get_".$var;
  4746. $output = $this->$get_method();
  4747. }
  4748. else if (!empty($custom_fields[$var]))
  4749. {
  4750. $init = array();
  4751. $custom_class = $custom_fields[$var];
  4752. if (is_array($custom_class))
  4753. {
  4754. if (isset($custom_class['model']))
  4755. {
  4756. $model = $custom_class['model'];
  4757. }
  4758. else
  4759. {
  4760. $model = current($custom_class);
  4761. }
  4762. if (!empty($custom_class['init']))
  4763. {
  4764. $init = $custom_class['init'];
  4765. }
  4766. }
  4767. else
  4768. {
  4769. $model = $custom_class;
  4770. }
  4771. $model = ucfirst(end(explode('/', $model)));
  4772. $value = (isset($this->_fields[$var])) ? $this->_fields[$var] : NULL;
  4773. $output = new $model($var, $value, $this, $init);
  4774. }
  4775. // then look in foreign keys and lazy load (add _id from the end in search)
  4776. else if (in_array($var.'_id', array_keys($foreign_keys)))
  4777. {
  4778. $var_key = $var.'_id';
  4779. $model = $foreign_keys[$var_key];
  4780. $output = $this->lazy_load($var_key, $model);
  4781. }
  4782. // check if field is for related data via has_many
  4783. else if ($this->_is_relationship_property($var, 'has_many'))
  4784. {
  4785. $output = $this->_get_relationship($var, FALSE, 'has_many');
  4786. }
  4787. // check if field is for related data via belongs_to
  4788. else if ($this->_is_relationship_property($var, 'belongs_to'))
  4789. {
  4790. $output = $this->_get_relationship($var, FALSE, 'belongs_to');
  4791. }
  4792. // finally check values from the database
  4793. else if (array_key_exists($var, $this->_fields))
  4794. {
  4795. $output = $this->_fields[$var];
  4796. }
  4797. else
  4798. {
  4799. // take the field name plus a '_' to get the suffix
  4800. $suffix = substr(strrchr($var, '_'), 1);
  4801. // get the core field name without the suffix (+1 because of underscore)
  4802. $field = substr($var, 0, - (strlen($suffix) + 1));
  4803. // apply formatting to the value
  4804. $output = $this->format($field, $suffix);
  4805. }
  4806. // unserialize any data
  4807. if (!empty($this->_parent_model->serialized_fields))
  4808. {
  4809. $output = $this->_parent_model->unserialize_value($output, $var);
  4810. }
  4811. $output = $this->after_get($output, $var);
  4812. return $output;
  4813. }
  4814. // --------------------------------------------------------------------
  4815. /**
  4816. * Returns an array of relationship data
  4817. *
  4818. * @access protected
  4819. * @param string field name
  4820. * @param boolean whether return the related object or just the data (optional)
  4821. * @param string options are 'has_many' and 'belongs_to'(optional)
  4822. * @return array
  4823. */
  4824. protected function _get_relationship($var, $return_object = FALSE, $relationship_type = 'has_many')
  4825. {
  4826. $valid_rel_types = array('has_many', 'belongs_to');
  4827. if ( ! in_array($relationship_type, $valid_rel_types))
  4828. {
  4829. return FALSE;
  4830. }
  4831. $rel = $this->_parent_model->$relationship_type;
  4832. $fields = $this->_parent_model->relationship_field_names($relationship_type);
  4833. $id_field = '';
  4834. $rel_config = $rel[$var];
  4835. $use_rel_tbl = $this->_parent_model->is_using_relationship_table($rel_config);
  4836. $output = array();
  4837. // load the necessary models
  4838. $foreign_model = $this->_parent_model->load_related_model($rel_config);
  4839. if ($use_rel_tbl)
  4840. {
  4841. $relationships_model = $this->_parent_model->load_model($fields['relationships_model']);
  4842. $id_field = $this->_parent_model->key_field();
  4843. $related_table_name = $this->_CI->$foreign_model->table_name();
  4844. }
  4845. // check that the id field is not an array
  4846. if (!is_string($id_field))
  4847. {
  4848. return FALSE;
  4849. }
  4850. if ($use_rel_tbl == FALSE)
  4851. {
  4852. // Using alternative relationship table
  4853. $this->_CI->$foreign_model->db()->where($rel_config['foreign_key'], $this->id);
  4854. }
  4855. else
  4856. {
  4857. // Using relationship pivot table
  4858. if (strtolower($relationship_type) == 'belongs_to')
  4859. {
  4860. $rel_where = array(
  4861. $fields['candidate_table'] => $related_table_name,
  4862. $fields['foreign_table'] => $this->_parent_model->table_name(),
  4863. $fields['foreign_key'] => $this->$id_field,
  4864. );
  4865. $rel_ids = array_keys($this->_CI->$relationships_model->find_all_array_assoc($fields['candidate_key'], $rel_where, $this->_CI->$relationships_model->key_field()));
  4866. }
  4867. else
  4868. {
  4869. $rel_where = array(
  4870. $fields['candidate_table'] => $this->_parent_model->table_name(),
  4871. $fields['candidate_key'] => $this->$id_field,
  4872. $fields['foreign_table'] => $related_table_name,
  4873. );
  4874. $rel_ids = array_keys($this->_CI->$relationships_model->find_all_array_assoc($fields['foreign_key'], $rel_where, $this->_CI->$relationships_model->key_field()));
  4875. }
  4876. if ( ! empty($rel_ids))
  4877. {
  4878. // construct the method name
  4879. $this->_CI->$foreign_model->db()->where_in("{$related_table_name}.".$id_field, $rel_ids);
  4880. // check if there is a where condition an apply that too
  4881. if (is_array($rel_config))
  4882. {
  4883. if (! empty($rel_config['where']))
  4884. {
  4885. $this->_CI->$foreign_model->db()->where($rel_config['where']);
  4886. }
  4887. if (! empty($rel_config['order']))
  4888. {
  4889. $this->_CI->$foreign_model->db()->order_by($rel_config['order']);
  4890. }
  4891. }
  4892. }
  4893. else
  4894. {
  4895. return ($return_object) ? FALSE : array();
  4896. }
  4897. }
  4898. // if return object is set to TRUE, then do just that with the where_in already applied
  4899. if ($return_object)
  4900. {
  4901. return $this->_CI->$foreign_model;
  4902. }
  4903. // otherwise we do a find all with the where_in already applied
  4904. else
  4905. {
  4906. $foreign_data = $this->_CI->$foreign_model->find_all();
  4907. }
  4908. if ( ! empty($foreign_data))
  4909. {
  4910. // maintain the order of the related data
  4911. if (!empty($rel_ids))
  4912. {
  4913. $rel_ids_flipped = array_flip($rel_ids);
  4914. foreach ($foreign_data as $row)
  4915. {
  4916. $position = $rel_ids_flipped[$row->id];
  4917. $output[$position] = $row;
  4918. }
  4919. ksort($output);
  4920. }
  4921. else
  4922. {
  4923. $output = $foreign_data;
  4924. }
  4925. }
  4926. return $output;
  4927. }
  4928. // --------------------------------------------------------------------
  4929. /**
  4930. * Returns whether the property name represents a relationship property
  4931. *
  4932. * @access protected
  4933. * @param string field name
  4934. * @param string relationship type
  4935. * @return boolean
  4936. */
  4937. protected function _is_relationship_property($var, $type = 'has_many')
  4938. {
  4939. if ( ! empty($this->_parent_model->$type) AND array_key_exists($var, $this->_parent_model->$type))
  4940. {
  4941. $rel = $this->_parent_model->$type;
  4942. if ( ! empty($rel[$var]))
  4943. {
  4944. return TRUE;
  4945. }
  4946. }
  4947. return FALSE;
  4948. }
  4949. // --------------------------------------------------------------------
  4950. /**
  4951. * Magic method to check if a variable is set
  4952. *
  4953. * @access public
  4954. * @param string field to check if it is set
  4955. * @return boolean
  4956. */
  4957. public function __isset($key)
  4958. {
  4959. $obj_vars = get_object_vars($this);
  4960. return (isset($this->_fields[$key]) OR isset($obj_vars[$key]) OR method_exists($this, 'get_'.$key));
  4961. }
  4962. // --------------------------------------------------------------------
  4963. /**
  4964. * Magic method for unset
  4965. *
  4966. * @access public
  4967. * @param string field to delete
  4968. * @return void
  4969. */
  4970. public function __unset($key)
  4971. {
  4972. $obj_vars = get_object_vars($this);
  4973. if (isset($this->_fields[$key]))
  4974. {
  4975. unset($this->_fields[$key]);
  4976. }
  4977. else if(isset($obj_vars[$key]))
  4978. {
  4979. unset($this->$key);
  4980. }
  4981. }
  4982. }
  4983. // --------------------------------------------------------------------
  4984. /**
  4985. * FUEL CMS
  4986. * http://www.getfuelcms.com
  4987. *
  4988. * An open source Content Management System based on the
  4989. * Codeigniter framework (http://codeigniter.com)
  4990. *
  4991. * @package FUEL CMS
  4992. * @author David McReynolds @ Daylight Studio
  4993. * @copyright Copyright (c) 2014, Run for Daylight LLC.
  4994. * @license http://docs.getfuelcms.com/general/license
  4995. * @link http://www.getfuelcms.com
  4996. */
  4997. // ------------------------------------------------------------------------
  4998. /**
  4999. * This class can be extended to return custom field objects for Data_record objects values
  5000. *
  5001. * The Data_record_field class is used to create custom field objects for a Date_record object values.
  5002. * Data_record_field objects provides a greater level of flexibility with your models by allowing associate
  5003. * an object as the value of a field. This class is <strong>optional</strong>.
  5004. *
  5005. * @package FUEL CMS
  5006. * @subpackage Libraries
  5007. * @category Libraries
  5008. * @author David McReynolds @ Daylight Studio
  5009. * @link http://docs.getfuelcms.com/libraries/my_model
  5010. * @prefix $record->
  5011. */
  5012. class Data_record_field {
  5013. public $field = NULL; // actual field value
  5014. public $value = NULL; // the value of the field
  5015. protected $_CI = NULL; // global CI object
  5016. protected $_parent_model = NULL; // the name of the parent model
  5017. protected $_inited = FALSE; // returns whether the object has been initiated or not
  5018. /**
  5019. * Constructor - requires a result set from MY_Model.
  5020. * @param string the name of the field (optional)
  5021. * @param mixed the value of the field (optional)
  5022. * @param object parent object (optional)
  5023. * @param array initialization parameters (optional)
  5024. */
  5025. public function __construct($field = NULL, $value = NULL, &$parent = NULL, $params = array())
  5026. {
  5027. $this->_CI =& get_instance();
  5028. if (!empty($parent))
  5029. {
  5030. $this->initialize($field, $value, $parent, $params);
  5031. }
  5032. }
  5033. // --------------------------------------------------------------------
  5034. /**
  5035. * Initializes the class with the field name, it's value, parent model and any additional parameters needed for the class
  5036. *
  5037. * @access public
  5038. * @param string the name of the field
  5039. * @param mixed the value of the field
  5040. * @param object parent object
  5041. * @param array initialization parameters
  5042. * @return array
  5043. */
  5044. public function initialize($field, $value, &$parent, $params = array())
  5045. {
  5046. $this->_parent_model = $parent;
  5047. $this->field = $field;
  5048. $this->value = $value;
  5049. if (!empty($params))
  5050. {
  5051. foreach($params as $key => $val)
  5052. {
  5053. if (isset($this->$key) AND substr($key, 0, 1) != '_')
  5054. {
  5055. $this->$key = $val;
  5056. }
  5057. }
  5058. }
  5059. $this->on_init();
  5060. $this->_inited = TRUE;
  5061. }
  5062. // --------------------------------------------------------------------
  5063. /**
  5064. * Placeholder - executed after initialization
  5065. *
  5066. * @access public
  5067. * @return vpod
  5068. */
  5069. public function on_init()
  5070. {
  5071. }
  5072. // --------------------------------------------------------------------
  5073. /**
  5074. * This method returns either <dfn>TRUE</dfn> or <dfn>FALSE</dfn> depending on if the field class has been properly intialized.
  5075. *
  5076. * @access public
  5077. * @return boolean
  5078. */
  5079. public function is_initialized()
  5080. {
  5081. return $this->_inited;
  5082. }
  5083. // --------------------------------------------------------------------
  5084. /**
  5085. * Returns the parent model object
  5086. *
  5087. * @access public
  5088. * @return object
  5089. */
  5090. public function &parent_model()
  5091. {
  5092. return $this->_parent_model;
  5093. }
  5094. // --------------------------------------------------------------------
  5095. /**
  5096. * Returns the table model object
  5097. *
  5098. * @access public
  5099. * @return object
  5100. */
  5101. public function table_model()
  5102. {
  5103. return $this->parent_model()->parent_model();
  5104. }
  5105. // --------------------------------------------------------------------
  5106. /**
  5107. * Is empty check
  5108. *
  5109. * @access public
  5110. * @return boolean
  5111. */
  5112. public function is_empty()
  5113. {
  5114. return empty($this->value);
  5115. }
  5116. // --------------------------------------------------------------------
  5117. /**
  5118. * Placeholder - to execute after a magic method get
  5119. *
  5120. * @access public
  5121. * @param string output from get comand
  5122. * @param string field name
  5123. * @return mixed
  5124. */
  5125. public function after_get($output, $var)
  5126. {
  5127. return $output;
  5128. }
  5129. // --------------------------------------------------------------------
  5130. /**
  5131. * Magic method to return first property then method
  5132. *
  5133. * @access public
  5134. * @param string field name
  5135. * @return mixed
  5136. */
  5137. public function __get($var)
  5138. {
  5139. $output = NULL;
  5140. // first class property has precedence
  5141. if (property_exists($this, $var))
  5142. {
  5143. $output = $this->$var;
  5144. }
  5145. // check a get_{method}
  5146. else if (method_exists($this, "get_".$var))
  5147. {
  5148. $get_method = "get_".$var;
  5149. $output = $this->$get_method();
  5150. }
  5151. $output = $this->after_get($output, $var);
  5152. return $output;
  5153. }
  5154. // --------------------------------------------------------------------
  5155. /**
  5156. * What to print when echoing out this object
  5157. *
  5158. * @access public
  5159. * @return string
  5160. */
  5161. public function __toString()
  5162. {
  5163. $value = $this->value;
  5164. if (is_string($value))
  5165. {
  5166. return $value;
  5167. }
  5168. else
  5169. {
  5170. return '';
  5171. }
  5172. }
  5173. // --------------------------------------------------------------------
  5174. /**
  5175. * Magic method to check if a variable is set
  5176. *
  5177. * @access public
  5178. * @param string field to check if it is set
  5179. * @return boolean
  5180. */
  5181. public function __isset($key)
  5182. {
  5183. $obj_vars = get_object_vars($this);
  5184. return (isset($obj_vars[$key]) OR method_exists($this, 'get_'.$key));
  5185. }
  5186. // --------------------------------------------------------------------
  5187. /**
  5188. * Magic method for unset
  5189. *
  5190. * @access public
  5191. * @param string field to delete
  5192. * @return void
  5193. */
  5194. public function __unset($key)
  5195. {
  5196. unset($this->parent_model()->$key);
  5197. }
  5198. }
  5199. /* End of file MY_Model.php */
  5200. /* Location: ./modules/fuel/core/MY_Model.php */