PageRenderTime 64ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

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

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