CodeIgniter(CI3)MY_Model 模型扩展类 Version 2.0.0

<?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'], '', '&amp;');
			}
			$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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值