<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* MY_Model Class
* Version 2.0.0
* 1. It is the base class of model that implements the *pagination*, *validation* and *auto-complete timestamp* features.
* [pagination]
* function result() and result_array() accepts paging parameters like this:
* $pagination = array('page'=>TRUE, 'url'=>base_url("cms/items/index"), 'limit'=>$limit, 'extra'=>$condition);
* [validation]
* public function rules(): Used to define rules.
* public function scenarios(): Used to define scenarios.
* $this->validate($scenario): If the parameter $scenario is a string, use a scene verification temporarily.
* $this->validate($attribute_names): If the parameter $attribute_names is an array, the fields to be verified will be
* further restricted based on the current scene.
* [timestamp]
* The statement "$timestamps = FALSE;" will close timestamps.
* The statement "$timestamps = array('made_at','modified_at','');" will close timestamps for "deleted_at",
* and change timestamp field "created_at" to "made_at", change timestamp field "updated_at" to "modified_at".
* 2. It provides a solution for chained queries. In the inherited subclass, the query-related method should be
* encoded as a chained operation using the statement returning $this, otherwise the result() and result_array() methods
* will not be available. It is recommended that methods supporting chain operations start with obvious words such as get_ and filter_.
*
* 3. It provides a solution for different database connections. The internal database object $this->db can be connected to
* different databases according to the custom database grouping $this->db_group.
*
* 4. Table Alias "a" added automatically, in method like result(), result_array(), fetch_sql() and print_sql(), but not in update() and delete().
*
* 5. It provides scene functionality for validation rules and transaction operations, see rules(), scenarios(), and transactions().
*
* The chain syntax is as follows:
*
*```php
*
* function get_packages_of_this_item($item_part_name)
* {
* $this->db->select('a.part_name, a.part_number, c.package_name')
* ->join('package_item as b', 'b.item_id=a.id')
* ->join('packages as c', 'c.id=b.package_id')
* ->where('a.part_name', $item_part_name);
* return $this;
* }
*
* function get_latest_news_by_type($type)
* {
* $this->db->where('create_time >', date('Y-m-d H:i:s', strtotime('-7 days')))
* ->andWhere('type', $type);
* return $this;
* }
*
* function filter($data)
* {
* if ( ! empty($data['part_name']) ) $this->db->like('part_name', $data['part_name']);
* if ( ! empty($data['part_number']) ) $this->db->like('part_number', $data['part_number']);
* if ( ! empty($data['part_type']) ) $this->db->where('part_type', $data['part_type']);
* return $this;
* }
* ```
* And query related data in the controller, as shown bellow:
*
*```php
* function index()
* {
* $default_limit = 30;
* $limit = $this->input->get('limit') ? intval($this->input->get('limit')) : $default_limit;
* $pagination = array('page'=>TRUE, 'url'=>base_url("cms/news/index"), 'limit'=>$limit);
* $latest_news = $this->news_model->get_latest_news_by_type(2)->result_array($pagination);
* return $this;
* }
*
* function items()
* {
* $default_limit = 30;
* $limit = $this->input->get('limit') ? intval($this->input->get('limit')) : $default_limit;
* $condition = elements(array('part_name', 'part_number', 'part_type'), $this->input->get());
* $condition = array_merge($condition, array('limit'=>$limit));
* $pagination = array('page'=>TRUE, 'url'=>base_url("cms/items/index"), 'limit'=>$limit, 'extra'=>$condition);
* $items = $this->items_model->filter($condition)->result($pagination);
* }
* ```
*
*/
class MY_Model{
const SCENARIO_DEFAULT = 'default';
const DATABASE_GROUP_DEFAULT = 'default';
const SCENARIO_FIELDS_ALL = "all";
/**
* This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
*/
const OP_INSERT = 0x01;
const OP_UPDATE = 0x02;
const OP_DELETE = 0x04;
const OP_ALL = 0x07;
protected $app = NULL;
protected $db = NULL;
protected $db_group = self::DATABASE_GROUP_DEFAULT;
protected $table_name = NULL;
protected $primary_key = 'id';
protected $default_limit = 10;
protected $limit = NULL;
protected $page_links = NULL;
protected $_total_count = NULL;
/**
* $this->timestamps = TRUE | array('made_at','modified_at','removed_at') | array('made_at','modified_at','')
* @var bool | array
* Enables created_at and updated_at fields
*/
protected $timestamps = FALSE;
protected $soft_delete = FALSE;
// $timestamps_format string "timestamp", or datetime format.
protected $timestamps_format = 'Y-m-d H:i:s';
protected $_created_at_field;
protected $_updated_at_field;
protected $_deleted_at_field;
/* validation */
public $scenario = self::SCENARIO_DEFAULT;
private $_rules = [];
private $_scenarios = [];
/**
* Class Constructor.
* Adapt database connection group automatically.
* Priorities for using databases: Transferred database parameters > $CI->db
* When the database $CI->db is not the required database, load the corresponding
* database automatically as the $this->db_group.
*/
function __construct()
{
$this->app = &get_instance();
$this->_connect_database();
$this->_config_timestamp_fields();
$this->table_name = empty($this->table_name) ? $this->_guess_table_name() : $this->db->dbprefix($this->table_name);
$this->_load_scenarios();
$this->_preload_rules();
}
/**
* Attributes that the model does not own will be retrieved from the CI application.
* Equivalent to inheriting CI_Model classes.
*/
function __get($key)
{
return $this->app->$key;
}
/**
* Connects to the database according to the set $db_group
*/
private function _connect_database()
{
if ($this->db_group != "default")
{
if (isset($this->app->{$this->db_group}) && !empty($this->app->{$this->db_group}))
{
$this->db = $this->app->{$this->db_group};
} else {
$this->db = $this->load->database($this->db_group, TRUE);
$this->app->{$this->db_group} = $this->db;
}
} else {
if (isset($this->app->db) && !empty($this->app->db))
{
$this->db = $this->app->db;
} else {
$this->db = $this->app->db = $this->load->database("default", TRUE);
}
}
}
/**
* Guess the table name when $this->table_name is empty.
* @return string table name with prefix.
*/
private function _guess_table_name()
{
$this->load->helper('inflector');
$model_name = get_class($this);
$table_name = plural(preg_replace('/(_m|_model|_mdl|model)?$/', '', strtolower($model_name)));
$table = $this->db->dbprefix($table_name);
$this->_table_exists($table);
return $table;
}
/**
* Checks if the table exists.
* @param $table_name
* @return bool
*/
private function _table_exists($table_name)
{
if (!$this->db->table_exists($table_name)) {
show_error(
sprintf('While trying to figure out the table name, couldn\'t find an existing table named: "%s".', $table_name),
500,
sprintf('Error trying to figure out table name for model "%s"',get_class($this))
);
}
return TRUE;
}
/**
* Automatically configures timestamp fields
*/
private function _config_timestamp_fields()
{
if($this->timestamps !== FALSE)
{
$this->_created_at_field = (is_array($this->timestamps) && isset($this->timestamps[0])) ? $this->timestamps[0] : 'created_at';
$this->_updated_at_field = (is_array($this->timestamps) && isset($this->timestamps[1])) ? $this->timestamps[1] : 'updated_at';
$this->_deleted_at_field = (is_array($this->timestamps) && isset($this->timestamps[2])) ? $this->timestamps[2] : 'deleted_at';
}
return TRUE;
}
/**
* Returns a value representing the date/time depending on the timestamp format.
* @return string
*/
private function _current_timestamp()
{
if($this->timestamps_format=='timestamp')
{
return time();
}
else
{
return date($this->timestamps_format);
}
}
/**
* Configs pagination of the query.
* @param $p Deal with custom configuration options of the pagination.
* @return mixed The desired pagination options
*/
private function _config_pagination($p = array())
{
if (!empty($p['page']))
{
$tmpl_default = array(
'full_tag_open' => "<ul class='pagination pull-right'>",
'full_tag_close' => "</ul>",
'num_tag_open' => "<li>",
'num_tag_close' => "</li>",
'cur_tag_open' => "<li class='disabled'><li class='active'><a href='#'>",
'cur_tag_close' => "<span class='sr-only'></span></a></li>",
'next_tag_open' => "<li>",
'next_tagl_close' => "</li>",
'prev_tag_open' => "<li>",
'prev_tagl_close' => "</li>",
'first_tag_open' => "<li>",
'first_tagl_close' => "</li>",
'last_tag_open' => "<li>",
'last_tagl_close' => "</li>"
);
$tmpl = array_key_exists('tmpl', $p) ? $p['tmpl'] : $tmpl_default;
$p = array_merge($tmpl, $p);
$p['use_page_numbers'] = element('use_page_numbers', $p, TRUE);
$p['page_query_string'] = element('page_query_string', $p, TRUE);
$p['query_string_segment'] = element('query_string_segment', $p, 'page');
$p['base_url'] = element('base_url', $p, current_url());
$p['base_url'] = element('url', $p, $p['base_url']);
$p['per_page'] = element('per_page', $p, $this->default_limit);
$p['per_page'] = element('limit', $p, $p['per_page']);
$this->limit = $p['per_page'];
if (isset($p['tmpl'])){
unset($p['tmpl']);
}
if (!empty($p['extra']))
{
$p['extra'] = http_build_query($p['extra'], '', '&');
}
$p['base_url'] = empty($p['extra']) ? $p['base_url'] : $p['base_url']."?".$p['extra'];
}
return $p;
}
/**
* generates paging connections
* @param $config pagination config
* @return mixed paging connection of HTML format
*/
private function _pagination($config)
{
if (!isset($this->pagination))
{
$this->load->library('pagination');
}
$this->pagination->initialize($config);
$page_links = $this->pagination->create_links();
return $page_links;
}
/**
* Paging query
* @param int $page Current page
* @return $this To implement chain query.
*/
private function _paginate($page, $limit = 0)
{
$limit = empty($limit) ? $this->limit : $limit;
$page = (int)$page;
$page = $page>0 ? $page : 1;
$offset = ($page - 1) * ($limit);
$this->db->limit($limit, $offset);
return $this;
}
/**
* loads scenarios
*/
private function _load_scenarios()
{
$this->_scenarios = $this->scenarios();
$this->_scenarios = empty($this->_scenarios) ? array(self::SCENARIO_DEFAULT): $this->_scenarios;
if (!array_key_exists(self::SCENARIO_DEFAULT, $this->_scenarios))
{
$this->_scenarios[self::SCENARIO_DEFAULT] = self::SCENARIO_FIELDS_ALL;
}
}
/**
* Preload rules
*/
private function _preload_rules()
{
$this->_rules = $this->rules();
if (!is_array($this->_rules))
{
show_error(
sprintf('While trying to load validation rules, rule definition error occurs.'),
500,
sprintf('Error trying to load validation rules for model "%s"',get_class($this))
);
}
}
/**
* Verifies if an array is associative or not
* @param array $array
* @return bool
*/
protected function is_assoc(array $array) {
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
/**
* Returns a value indicating whether the specified operation is transactional in the current [[$scenario]].
* @param int $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]].
* @return bool whether the specified operation is transactional in the current [[scenario]].
*/
public function is_transactional($operation, $scenario = NULL)
{
$scenario = $scenario ? $scenario : $this->scenario;
$transactions = $this->transactions();
return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation);
}
/**
* Verify the validity of the rule
* @param $rule
*/
protected function check_rule($rule)
{
$keys = array_keys($rule);
foreach ($keys as $index => $key)
{
if (!is_numeric($key))
{
unset($keys[$index]);
}
}
$keys_not_assoc = count($keys);
if ($keys_not_assoc != 2)
{
show_error(
sprintf('While trying to load validation rules, rule definition error occurs.'),
500,
sprintf('Error trying to load validation rules for model "%s"',get_class($this))
);
}
}
/**
* Extracts rules based on a defined rule $rule and the current scene fields $scenario_fields.
* @param $rule
* @param $scenario_fields
* @return array
*/
protected function extract_rule($rule, $scenario_fields)
{
$rules = array();
$temp_fields = is_array($rule[0]) ? $rule[0] : array($rule[0]);
foreach ($temp_fields as $temp_field)
{
if ($scenario_fields == self::SCENARIO_FIELDS_ALL || in_array($temp_field, $scenario_fields))
{
if (isset($rule['label']))
{
$label = is_array($rule['label']) ? (isset($rule['label'][$temp_field]) ? $rule['label'][$temp_field] : humanize($temp_field)) : humanize($temp_field);
} else {
$label = humanize($temp_field);
}
if (isset($rule['errors']) && is_array($rule['errors'])) {
$rules[] = array(
'field' => $temp_field,
'label' => $label,
'rules' => $rule[1],
'errors' => $rule['errors']
);
}
else
{
$rules[] = array(
'field' => $temp_field,
'label' => $label,
'rules' => $rule[1]
);
}
}// ignore else.
}
return $rules;
}
/**
* Loads rules according to the option $options
* @param null $options
* @return array
*/
protected function load_rules($options = NULL)
{
$scenario = $this->scenario;
$scenarios = $this->_scenarios;
$scenario_fields = self::SCENARIO_FIELDS_ALL;
if (empty($options)) // default parameter
{
$attribute_names = NULL; // disable attribute_names
}
elseif (is_array($options)) // attributes parameter
{
$attribute_names = $options;
}
elseif (is_string($options)) // scenario parameter
{
if (!array_key_exists($options, $scenarios))
{
show_error(
sprintf('While trying to load validation rules, couldn\'t get the rules from the temporary verification scenario named: "%s".',$options),
500,
sprintf('Error trying to load validation rules for model "%s"',get_class($this))
);
}
$attribute_names = NULL; // disable attribute_names
$scenario = $options;
} else { // parameter error
show_error(
sprintf('While trying to load validation rules, the $options parameter of validate($options) couldn\'t be parsed correctly.'),
500,
sprintf('Error trying to load validation rules for model "%s"',get_class($this))
);
}
if (!array_key_exists($scenario, $scenarios))
{
show_error(
sprintf('While trying to load validation rules, couldn\'t get the rules from scenario named: "%s".',empty($scenario) ? "Empty Scenario" : $scenario),
500,
sprintf('Error trying to load validation rules for model "%s"',get_class($this))
);
} elseif($scenario == self::SCENARIO_DEFAULT) {
$scenario_fields = self::SCENARIO_FIELDS_ALL;
} else {
$scenario_fields = $scenarios[$scenario];
}
if (!empty($attribute_names) ) // && is_array($attribute_names)
{
if (is_string($scenario_fields)) //default scenario
{
$scenario_fields = ($scenario_fields == self::SCENARIO_FIELDS_ALL) ? $attribute_names : array_intersect(array($scenario_fields), $attribute_names);
} elseif (is_array($scenario_fields)) {
$scenario_fields = array_intersect($scenario_fields, $attribute_names);
} else {
show_error(
sprintf('While trying to load validation scenarios, scenario definition error occurs.'),
500,
sprintf('Error trying to load validation scenarios for model "%s"',get_class($this))
);
}
}
$this->load->helper('inflector');
$rules = array();
foreach ($this->_rules as $rule)
{
$this->check_rule($rule);
if (isset($rule['on']))
{
if (is_array($rule['on']) ? in_array($scenario, $rule['on']) : $rule['on'] == $scenario)
{
$extracted_rule = $this->extract_rule($rule, $scenario_fields);
if (!empty($extracted_rule))
{
$rules += $extracted_rule;
}
}
}
elseif (isset($rule['except']))
{
if (is_array($rule['except']) ? !in_array($scenario, $rule['except']) : $rule['except'] != $scenario)
{
$extracted_rule = $this->extract_rule($rule, $scenario_fields);
if (!empty($extracted_rule))
{
$rules = array_merge($extracted_rule, $rules);
}
}
}
else { // If the on or except attribute is not specified, the rule will be applied in all scenarios.
$extracted_rule = $this->extract_rule($rule, $scenario_fields);
if (!empty($extracted_rule))
{
$rules = array_merge($extracted_rule, $rules);
}
}
}
return $rules;
}
/**
* Will be rewritten in the subclass.
*
* ```php
* function scenarios()
* {
* return [
* 'login' => ['username', 'password'],
* 'register' => ['username', 'email', 'password'],
* ];
* }
* ```
* @return two-dimensional array
*/
public function scenarios()
{
return [];
}
/**
* Will be rewritten in the subclass.
*
* ```php
* function rules()
* {
* return [
* [['username', 'email', 'password'], 'required', 'on' => 'register'],
* [['username', 'password'], 'required', 'on' => 'login'],
* ['username', 'alpha_numeric|callback_check_func_of_controller[parameter]', 'label'=>'User Name', 'errors'=>array('check_func_of_controller'=>'The error message.')],
* ['username', array('alpha_numeric', array('model_func_rule_name', array($this, 'model_func'))), 'label'=>'User Name', 'errors'=>array('model_func_rule_name'=>'The error message.')],
* ['username, 'required', 'on' => 'login', 'label'=>'User Name', 'errors'=>array('required'=>'Please enter your {field}')],
* ];
* }
* ```
* @return two-dimensional array
*/
public function rules()
{
return [];
}
/**
* Declares which DB operations should be performed within a transaction in different scenarios.
* The supported DB operations are: [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]],
* which correspond to the [[insert()]], [[update()]] and [[delete()]] methods, respectively.
* By default, these methods are NOT enclosed in a DB transaction.
*
* In some scenarios, to ensure data consistency, you may want to enclose some or all of them
* in transactions. You can do so by overriding this method and returning the operations
* that need to be transactional. For example,
*
* ```php
* return [
* 'admin' => self::OP_INSERT,
* 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
* // the above is equivalent to the following:
* // 'api' => self::OP_ALL,
*
* ];
* ```
*
* The above declaration specifies that in the "admin" scenario, the insert operation ([[insert()]])
* should be done in a transaction; and in the "api" scenario, all the operations should be done
* in a transaction.
*
* @return array the declarations of transactional operations. The array keys are scenarios names,
* and the array values are the corresponding transaction operations.
*/
public function transactions()
{
return [];
}
/**
* Form validation based on field constraints or temporary scenario.
* @param null $options
* @return mixed
*/
public function validate($options = NULL) //string for scenario, array for attribute_names
{
if (!empty($options) && !is_array($options) && !is_string($options))
{
show_error(
sprintf('The parameters of model validation method validate() must be NULL, string or array.'),
500,
sprintf('Error trying to validate for model "%s"',get_class($this))
);
}
if (isset($this->form_validation)){
unset($this->form_validation);
}
$this->load->library('form_validation');
$rules = $this->load_rules($options);
$this->form_validation->set_rules($rules);
return $this->form_validation->run();
}
/**
* An example of the simplest chain query method, often used to inherit subclasses
* Support chain operation, and parameters are equivalent to $this->db->where()
* @param mixed i.e. $this->get($id)
* @param mixed
* @param bool
* @return $this to implement chain query
*/
function get($key = NULL, $value = NULL, $escape = NULL)
{
if (empty($key))
{
return $this;
}
if (is_numeric($key))
{
$this->db->where('id', $key);
} else {
$this->db->where($key, $value, $escape);
}
return $this;
}
/**
* Common query function
* 1.Implementing Paging Function
* 2.To Return Paging Connection, used by get_page_link()
* @param array $pagination configuration for pagination
* @param bool $result_array If TRUE to return array results, Else to return object results.
* @return array of query result
*/
function result($pagination = array(), $result_array = FALSE)
{
$sql = $this->db->get_compiled_select("{$this->table_name} as a", FALSE);
if (!empty($pagination['page']))
{
$pagination = $this->_config_pagination($pagination);
$query = $this->db->query($sql);
$total = $query->num_rows();
$this->_total_count = $total;
if ($total == 0)
{
$this->db->reset_query();
$pagination['total_rows'] = 0;
$this->page_links = "";
return array();
}
$pagination['total_rows'] = $total;
$this->_paginate($this->input->get('page'));
$this->page_links = $this->_pagination($pagination);
}
$query = $this->db->get();
return $result_array ? $query->result_array() : $query->result();
}
/**
* @param array $pagination configuration for pagination
* @return array of query result
*/
function result_array($pagination = array())
{
return $this->result($pagination, TRUE);
}
/**
* Unlike get_row(), row() participates in the query chain, and there is no where condition.
* @param bool $result_array
* @return mixed
*/
function row($result_array = FALSE)
{
$query = $this->db->limit(1)->get($this->table_name);
return $result_array ? $query->row_array(): $query->row();
}
/**
* Unlike get_row_array(), row() participates in the query chain, and there is no where condition.
* @return mixed
*/
function row_array()
{
return $this->row(TRUE);
}
/**
* return total count of result() or result_array()
* @return int | NULL
*/
function get_total_count()
{
return $this->_total_count;
}
/**
* Gets the pagination link
* Not support chain operation
* @return string pagination link
*/
function create_links()
{
return $this->page_links;
}
/**
* Gets the query result of row, used to key-query.
* Instructions: Not compatible with chain operation.
* @param mixed $key i.e. '12500' or like $this->where($key, $value = NULL, $escape = NULL)
* @param mixed $value
* @param mixed $escape
* @param bool $result_array If TRUE to return array results, Else to return object results
* @return object or array of row result
*/
function get_row($key = NULL, $value = NULL, $escape = NULL, $result_array = FALSE)
{
if (is_numeric($key))
{
$this->db->where('id', $key);
}
elseif (!empty($key))
{
$this->db->where($key, $value, $escape);
}
$query = $this->db->get($this->table_name);
return $result_array ? $query->row_array(): $query->row();
}
/**
* Gets the query result of row, used to key-query.
* Not compatible with chain operation
* @param mixed $key i.e. '12500' or like $this->where($key, $value = NULL, $escape = NULL)
* @param mixed $value
* @param mixed $escape
* @return array of row result
*/
function get_row_array($key = NULL, $value = NULL, $escape = NULL)
{
return $this->get_row($key, $value, $escape, TRUE);
}
/**
* Checks if primary key record exists.
* Parameters are equivalent to $this->db->where()
* @param mixed i.e. '12500' or array('email'=>'12500') or array('email'=>'email@hotmail.com', 'name'=>"example")
* @param mixed $value i.e. $this->exist('email', 'email@hotmail.com')
* @param bool
* @return bool return if exist
*/
function exist($key = NULL, $value = NULL, $escape = NULL)
{
if (is_numeric($key))
{
$this->db->where('id', $key);
}
elseif (!empty($key))
{
$this->db->where($key, $value, $escape);
}
$query = $this->db->limit(1)->get($this->table_name);
return empty($query->row()) ? FALSE : TRUE;
}
/**
* Instructions: $this->get($where)->count();
* @return int Number of query records.
*/
function count()
{
return $this->db->count_all_results($this->table_name);
}
/**
* Inserts a record or records to the table for internal.
* Not compatible with chain operation
* @param array $data.
* @return false | int inserted record id | affected rows.
*/
protected function insert_internal($data)
{
$count = count($data);
$count_recursive = count($data, COUNT_RECURSIVE);
if ($count != $count_recursive) { //multi-line data
if ($this->timestamps !== FALSE && !empty($this->_created_at_field)) {
foreach ($data as &$row) {
$row[$this->_created_at_field] = $this->_current_timestamp();
}
}
$result = $this->insert_batch($this->table_name, $data);
$result = $result ? $result : 0;
return $result;
} else { //single row data
if($this->timestamps !== FALSE && !empty($this->_created_at_field))
{
$data[$this->_created_at_field] = $this->_current_timestamp();
}
foreach ($data as $key => $value) {
$this->db->set($key, $value);
}
$this->db->insert($this->table_name);
if ($this->db->affected_rows() > 0)
{
return $this->db->insert_id();
} else {
return FALSE;
}
}
}
/**
* Inserts a row or rows into the associated database table using the temporary scene.
* Not compatible with chain operation
* @param array $data Row data.
* @param null $scenario Temporary scene.
* @return false|int
*/
function add($data = array(), $scenario = NULL)
{
if (!$this->is_transactional(self::OP_INSERT, $scenario ? $scenario : $this->scenario)) {
return $this->insert_internal($data);
}
$this->db->trans_begin();
$result = $this->insert_internal($data);
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
$result = FALSE;
} else {
$this->db->trans_commit();
}
return $result;
}
/**
* Updates data of meeting condition for internal.
* Instructions: $this->get($where)->update($data);
* @param array $data Parameters for updating data
* @return int updated record count.
*/
protected function update_internal($data = array(), $where_key = '')
{
$count = count($data);
$count_recursive = count($data, COUNT_RECURSIVE);
if ($count != $count_recursive) //multi-line data
{
if($this->timestamps !== FALSE && !empty($this->_updated_at_field))
{
foreach ($data as &$row)
{
$row[$this->_updated_at_field] = $this->_current_timestamp();
}
}
$result = $this->update_batch($this->table_name, $data, $where_key);
$result = $result ? $result : 0;
} else { //single row data
if($this->timestamps !== FALSE && !empty($this->_updated_at_field))
{
$data[$this->_updated_at_field] = $this->_current_timestamp();
}
foreach ($data as $key => $value) {
$this->db->set($key, $value);
}
$this->db->update($this->table_name);
$result = $this->db->affected_rows();
}
return $result;
}
/**
* Updates data of meeting condition using the temporary scene.
* Instructions: $this->get($where)->update($data);
* @param array $data Row data.
* @param null $scenario Temporary scene.
* @param string $where_key where_key when batch updating.
* @return bool|int
*/
function update($data = array(), $scenario = NULL, $where_key = '')
{
if (!$this->is_transactional(self::OP_UPDATE, $scenario ? $scenario : $this->scenario)) {
return $this->update_internal($data, $where_key);
}
$this->db->trans_begin();
$result = $this->update_internal($data, $where_key);
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
$result = FALSE;
} else {
$this->db->trans_commit();
}
return $result;
}
/**
* Deletes data of meeting condition for internal.
* Instructions: $this->get($where)->delete();
* @return int affected rows.
*/
protected function delete_internal()
{
$affected_rows = 0;
if ($this->soft_delete == TRUE)
{
$to_update = array();
foreach($this->result() as $row)
{
$to_update[] = array($this->primary_key => $row->{$this->primary_key});
}
if(isset($to_update)&& count($to_update) > 0)
{
foreach($to_update as &$row)
{
//$row = $this->trigger('before_soft_delete',$row);
$row[$this->_deleted_at_field] = $this->_current_timestamp();
}
$affected_rows = $this->db->update_batch($this->table_name, $to_update, $this->primary_key);
}
return $affected_rows;
} else {
if ($this->db->delete($this->table_name))
{
$affected_rows = $this->db->affected_rows();
}
return $affected_rows;
}
}
/**
* Deletes data of meeting condition for internal using temporary scene.
* Instructions: $this->get($where)->delete();
* @return int affected rows.
*/
function delete($scenario = NULL)
{
if (!$this->is_transactional(self::OP_DELETE, $scenario ? $scenario : $this->scenario)) {
return $this->delete_internal();
}
$this->db->trans_begin();
$result = $this->delete_internal();
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
$result = FALSE;
} else {
$this->db->trans_commit();
}
return $result;
}
/**
* Not implemented
* Used to query the method chain, and return results with trashed.
* @return $this
*/
function with_trashed()
{
//todo: This method is reloaded when the soft delete is enabled for the query chain.
return $this;
}
/**
* Not implemented
* Used to query the method chain, and return results without trashed.
* @return $this
*/
function without_trashed()
{
//todo: This method is reloaded when the soft delete is enabled for the query chain.
return $this;
}
/**
* Not implemented
* Used to query the method chain, and return results only trashed.
* @return $this
*/
function only_trashed()
{
//todo: This method is reloaded when the soft delete is enabled for the query chain.
return $this;
}
/**
* resets the query.
*/
function reset_query()
{
$this->db->reset_query();
}
/**
* Manual escape
* @param $value
* @return mixed
*/
function escape($value)
{
return $this->db->escape($value);
}
/**
* debug function
* Instructions: $sql = $this->get($where)->fetch_sql();
*/
function fetch_sql($reset_query = TRUE)
{
$sql = $this->db->get_compiled_select("{$this->table_name} as a", FALSE);
$reset_query === TRUE ? $this->db->reset_query() : NULL;
return $sql;
}
/**
* debug function
* Instructions: $this->get($where)->print_sql(); //default, abort view rendering.
*/
function print_sql($break_off = TRUE, $reset_query = TRUE)
{
$sql = $this->db->get_compiled_select("{$this->table_name} as a", FALSE);
$reset_query === TRUE ? $this->db->reset_query() : NULL;
if ($break_off)
{
exit($sql);
} else {
echo($sql);
}
}
} // end of class
Datamapper ORM for Codeigniter
In case you need an alternative, here are some 1. GasORM 2. Doctrine - Probably the best to work with large data models but have to do some work to integrate with codeigniter 3. Propel – RanjanaLK