-
-
Save nteej/b544904973f3cdaac086ed4c73c396f9 to your computer and use it in GitHub Desktop.
CodeIgniter generic model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* A base model with a series of CRUD functions (powered by CI's query builder), | |
* validation-in-model support, event callbacks and more. | |
* | |
* @link http://github.com/jamierumbelow/codeigniter-base-model | |
* @copyright Copyright (c) 2012, Jamie Rumbelow <http://jamierumbelow.net> | |
*/ | |
class MY_Model extends CI_Model | |
{ | |
/* -------------------------------------------------------------- | |
* VARIABLES | |
* ------------------------------------------------------------ */ | |
/** | |
* This model's default database table. Automatically | |
* guessed by pluralising the model name. | |
*/ | |
protected $_table; | |
/** | |
* The database connection object. Will be set to the default | |
* connection. This allows individual models to use different DBs | |
* without overwriting CI's global $this->db connection. | |
*/ | |
public $_database; | |
/** | |
* This model's default primary key or unique identifier. | |
* Used by the get(), update() and delete() functions. | |
*/ | |
protected $primary_key = 'id'; | |
/** | |
* Support for soft deletes and this model's 'deleted' key | |
*/ | |
protected $soft_delete = FALSE; | |
protected $soft_delete_key = 'deleted'; | |
protected $_temporary_with_deleted = FALSE; | |
protected $_temporary_only_deleted = FALSE; | |
/** | |
* The various callbacks available to the model. Each are | |
* simple lists of method names (methods will be run on $this). | |
*/ | |
protected $before_create = array(); | |
protected $after_create = array(); | |
protected $before_update = array(); | |
protected $after_update = array(); | |
protected $before_get = array(); | |
protected $after_get = array(); | |
protected $before_delete = array(); | |
protected $after_delete = array(); | |
protected $callback_parameters = array(); | |
/** | |
* Protected, non-modifiable attributes | |
*/ | |
protected $protected_attributes = array(); | |
/** | |
* Relationship arrays. Use flat strings for defaults or string | |
* => array to customise the class name and primary key | |
*/ | |
protected $belongs_to = array(); | |
protected $has_many = array(); | |
protected $_with = array(); | |
/** | |
* An array of validation rules. This needs to be the same format | |
* as validation rules passed to the Form_validation library. | |
*/ | |
protected $validate = array(); | |
/** | |
* Optionally skip the validation. Used in conjunction with | |
* skip_validation() to skip data validation for any future calls. | |
*/ | |
protected $skip_validation = FALSE; | |
/** | |
* By default we return our results as objects. If we need to override | |
* this, we can, or, we could use the `as_array()` and `as_object()` scopes. | |
*/ | |
protected $return_type = 'object'; | |
protected $_temporary_return_type = NULL; | |
/* -------------------------------------------------------------- | |
* GENERIC METHODS | |
* ------------------------------------------------------------ */ | |
/** | |
* Initialise the model, tie into the CodeIgniter superobject and | |
* try our best to guess the table name. | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
$this->load->helper('inflector'); | |
$this->_fetch_table(); | |
$this->_database = $this->db; | |
array_unshift($this->before_create, 'protect_attributes'); | |
array_unshift($this->before_update, 'protect_attributes'); | |
$this->_temporary_return_type = $this->return_type; | |
} | |
/* -------------------------------------------------------------- | |
* CRUD INTERFACE | |
* ------------------------------------------------------------ */ | |
/** | |
* Fetch a single record based on the primary key. Returns an object. | |
*/ | |
public function get($primary_value) | |
{ | |
return $this->get_by($this->primary_key, $primary_value); | |
} | |
/** | |
* Fetch a single record based on an arbitrary WHERE call. Can be | |
* any valid value to $this->_database->where(). | |
*/ | |
public function get_by() | |
{ | |
$where = func_get_args(); | |
if ($this->soft_delete && $this->_temporary_with_deleted !== TRUE) | |
{ | |
$this->_database->where($this->soft_delete_key, (bool)$this->_temporary_only_deleted); | |
} | |
$this->_set_where($where); | |
$this->trigger('before_get'); | |
$row = $this->_database->get($this->_table) | |
->{$this->_return_type()}(); | |
$this->_temporary_return_type = $this->return_type; | |
$row = $this->trigger('after_get', $row); | |
$this->_with = array(); | |
return $row; | |
} | |
/** | |
* Fetch an array of records based on an array of primary values. | |
*/ | |
public function get_many($values) | |
{ | |
$this->_database->where_in($this->primary_key, $values); | |
return $this->get_all(); | |
} | |
/** | |
* Fetch an array of records based on an arbitrary WHERE call. | |
*/ | |
public function get_many_by() | |
{ | |
$where = func_get_args(); | |
$this->_set_where($where); | |
return $this->get_all(); | |
} | |
/** | |
* Fetch all the records in the table. Can be used as a generic call | |
* to $this->_database->get() with scoped methods. | |
*/ | |
public function get_all() | |
{ | |
$this->trigger('before_get'); | |
if ($this->soft_delete && $this->_temporary_with_deleted !== TRUE) | |
{ | |
$this->_database->where($this->soft_delete_key, (bool)$this->_temporary_only_deleted); | |
} | |
$result = $this->_database->get($this->_table) | |
->{$this->_return_type(1)}(); | |
$this->_temporary_return_type = $this->return_type; | |
foreach ($result as $key => &$row) | |
{ | |
$row = $this->trigger('after_get', $row, ($key == count($result) - 1)); | |
} | |
$this->_with = array(); | |
return $result; | |
} | |
/** | |
* Insert a new row into the table. $data should be an associative array | |
* of data to be inserted. Returns newly created ID. | |
*/ | |
public function insert($data, $skip_validation = FALSE) | |
{ | |
if ($skip_validation === FALSE) | |
{ | |
$data = $this->validate($data); | |
} | |
if ($data !== FALSE) | |
{ | |
$data = $this->trigger('before_create', $data); | |
$this->_database->insert($this->_table, $data); | |
$insert_id = $this->_database->insert_id(); | |
$this->trigger('after_create', $insert_id); | |
return $insert_id; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
/** | |
* Insert multiple rows into the table. Returns an array of multiple IDs. | |
*/ | |
public function insert_many($data, $skip_validation = FALSE) | |
{ | |
$ids = array(); | |
foreach ($data as $key => $row) | |
{ | |
$ids[] = $this->insert($row, $skip_validation, ($key == count($data) - 1)); | |
} | |
return $ids; | |
} | |
/** | |
* Updated a record based on the primary value. | |
*/ | |
public function update($primary_value, $data, $skip_validation = FALSE) | |
{ | |
$data = $this->trigger('before_update', $data); | |
if ($skip_validation === FALSE) | |
{ | |
$data = $this->validate($data); | |
} | |
if ($data !== FALSE) | |
{ | |
$result = $this->_database->where($this->primary_key, $primary_value) | |
->set($data) | |
->update($this->_table); | |
$this->trigger('after_update', array($data, $result)); | |
return $result; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
/** | |
* Update many records, based on an array of primary values. | |
*/ | |
public function update_many($primary_values, $data, $skip_validation = FALSE) | |
{ | |
$data = $this->trigger('before_update', $data); | |
if ($skip_validation === FALSE) | |
{ | |
$data = $this->validate($data); | |
} | |
if ($data !== FALSE) | |
{ | |
$result = $this->_database->where_in($this->primary_key, $primary_values) | |
->set($data) | |
->update($this->_table); | |
$this->trigger('after_update', array($data, $result)); | |
return $result; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
/** | |
* Updated a record based on an arbitrary WHERE clause. | |
*/ | |
public function update_by() | |
{ | |
$args = func_get_args(); | |
$data = array_pop($args); | |
$data = $this->trigger('before_update', $data); | |
if ($this->validate($data) !== FALSE) | |
{ | |
$this->_set_where($args); | |
$result = $this->_database->set($data) | |
->update($this->_table); | |
$this->trigger('after_update', array($data, $result)); | |
return $result; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
/** | |
* Update all records | |
*/ | |
public function update_all($data) | |
{ | |
$data = $this->trigger('before_update', $data); | |
$result = $this->_database->set($data) | |
->update($this->_table); | |
$this->trigger('after_update', array($data, $result)); | |
return $result; | |
} | |
/** | |
* Delete a row from the table by the primary value | |
*/ | |
public function delete($id) | |
{ | |
$this->trigger('before_delete', $id); | |
$this->_database->where($this->primary_key, $id); | |
if ($this->soft_delete) | |
{ | |
$result = $this->_database->update($this->_table, array( $this->soft_delete_key => TRUE )); | |
} | |
else | |
{ | |
$result = $this->_database->delete($this->_table); | |
} | |
$this->trigger('after_delete', $result); | |
return $result; | |
} | |
/** | |
* Delete a row from the database table by an arbitrary WHERE clause | |
*/ | |
public function delete_by() | |
{ | |
$where = func_get_args(); | |
$where = $this->trigger('before_delete', $where); | |
$this->_set_where($where); | |
if ($this->soft_delete) | |
{ | |
$result = $this->_database->update($this->_table, array( $this->soft_delete_key => TRUE )); | |
} | |
else | |
{ | |
$result = $this->_database->delete($this->_table); | |
} | |
$this->trigger('after_delete', $result); | |
return $result; | |
} | |
/** | |
* Delete many rows from the database table by multiple primary values | |
*/ | |
public function delete_many($primary_values) | |
{ | |
$primary_values = $this->trigger('before_delete', $primary_values); | |
$this->_database->where_in($this->primary_key, $primary_values); | |
if ($this->soft_delete) | |
{ | |
$result = $this->_database->update($this->_table, array( $this->soft_delete_key => TRUE )); | |
} | |
else | |
{ | |
$result = $this->_database->delete($this->_table); | |
} | |
$this->trigger('after_delete', $result); | |
return $result; | |
} | |
/** | |
* Truncates the table | |
*/ | |
public function truncate() | |
{ | |
$result = $this->_database->truncate($this->_table); | |
return $result; | |
} | |
/* -------------------------------------------------------------- | |
* RELATIONSHIPS | |
* ------------------------------------------------------------ */ | |
public function with($relationship) | |
{ | |
$this->_with[] = $relationship; | |
if (!in_array('relate', $this->after_get)) | |
{ | |
$this->after_get[] = 'relate'; | |
} | |
return $this; | |
} | |
public function relate($row) | |
{ | |
if (empty($row)) | |
{ | |
return $row; | |
} | |
foreach ($this->belongs_to as $key => $value) | |
{ | |
if (is_string($value)) | |
{ | |
$relationship = $value; | |
$options = array( 'primary_key' => $value . '_id', 'model' => $value . '_model' ); | |
} | |
else | |
{ | |
$relationship = $key; | |
$options = $value; | |
} | |
if (in_array($relationship, $this->_with)) | |
{ | |
$this->load->model($options['model'], $relationship . '_model'); | |
if (is_object($row)) | |
{ | |
$row->{$relationship} = $this->{$relationship . '_model'}->get($row->{$options['primary_key']}); | |
} | |
else | |
{ | |
$row[$relationship] = $this->{$relationship . '_model'}->get($row[$options['primary_key']]); | |
} | |
} | |
} | |
foreach ($this->has_many as $key => $value) | |
{ | |
if (is_string($value)) | |
{ | |
$relationship = $value; | |
$options = array( 'primary_key' => singular($this->_table) . '_id', 'model' => singular($value) . '_model' ); | |
} | |
else | |
{ | |
$relationship = $key; | |
$options = $value; | |
} | |
if (in_array($relationship, $this->_with)) | |
{ | |
$this->load->model($options['model'], $relationship . '_model'); | |
if (is_object($row)) | |
{ | |
$row->{$relationship} = $this->{$relationship . '_model'}->get_many_by($options['primary_key'], $row->{$this->primary_key}); | |
} | |
else | |
{ | |
$row[$relationship] = $this->{$relationship . '_model'}->get_many_by($options['primary_key'], $row[$this->primary_key]); | |
} | |
} | |
} | |
return $row; | |
} | |
/* -------------------------------------------------------------- | |
* UTILITY METHODS | |
* ------------------------------------------------------------ */ | |
/** | |
* Retrieve and generate a form_dropdown friendly array | |
*/ | |
function dropdown() | |
{ | |
$args = func_get_args(); | |
if(count($args) == 2) | |
{ | |
list($key, $value) = $args; | |
} | |
else | |
{ | |
$key = $this->primary_key; | |
$value = $args[0]; | |
} | |
$this->trigger('before_dropdown', array( $key, $value )); | |
if ($this->soft_delete && $this->_temporary_with_deleted !== TRUE) | |
{ | |
$this->_database->where($this->soft_delete_key, FALSE); | |
} | |
$result = $this->_database->select(array($key, $value)) | |
->get($this->_table) | |
->result(); | |
$options = array(); | |
foreach ($result as $row) | |
{ | |
$options[$row->{$key}] = $row->{$value}; | |
} | |
$options = $this->trigger('after_dropdown', $options); | |
return $options; | |
} | |
/** | |
* Fetch a count of rows based on an arbitrary WHERE call. | |
*/ | |
public function count_by() | |
{ | |
if ($this->soft_delete && $this->_temporary_with_deleted !== TRUE) | |
{ | |
$this->_database->where($this->soft_delete_key, (bool)$this->_temporary_only_deleted); | |
} | |
$where = func_get_args(); | |
$this->_set_where($where); | |
return $this->_database->count_all_results($this->_table); | |
} | |
/** | |
* Fetch a total count of rows, disregarding any previous conditions | |
*/ | |
public function count_all() | |
{ | |
if ($this->soft_delete && $this->_temporary_with_deleted !== TRUE) | |
{ | |
$this->_database->where($this->soft_delete_key, (bool)$this->_temporary_only_deleted); | |
} | |
return $this->_database->count_all($this->_table); | |
} | |
/** | |
* Tell the class to skip the insert validation | |
*/ | |
public function skip_validation() | |
{ | |
$this->skip_validation = TRUE; | |
return $this; | |
} | |
/** | |
* Get the skip validation status | |
*/ | |
public function get_skip_validation() | |
{ | |
return $this->skip_validation; | |
} | |
/** | |
* Return the next auto increment of the table. Only tested on MySQL. | |
*/ | |
public function get_next_id() | |
{ | |
return (int) $this->_database->select('AUTO_INCREMENT') | |
->from('information_schema.TABLES') | |
->where('TABLE_NAME', $this->_table) | |
->where('TABLE_SCHEMA', $this->_database->database)->get()->row()->AUTO_INCREMENT; | |
} | |
/** | |
* Getter for the table name | |
*/ | |
public function table() | |
{ | |
return $this->_table; | |
} | |
/* -------------------------------------------------------------- | |
* GLOBAL SCOPES | |
* ------------------------------------------------------------ */ | |
/** | |
* Return the next call as an array rather than an object | |
*/ | |
public function as_array() | |
{ | |
$this->_temporary_return_type = 'array'; | |
return $this; | |
} | |
/** | |
* Return the next call as an object rather than an array | |
*/ | |
public function as_object() | |
{ | |
$this->_temporary_return_type = 'object'; | |
return $this; | |
} | |
/** | |
* Don't care about soft deleted rows on the next call | |
*/ | |
public function with_deleted() | |
{ | |
$this->_temporary_with_deleted = TRUE; | |
return $this; | |
} | |
/** | |
* Only get deleted rows on the next call | |
*/ | |
public function only_deleted() | |
{ | |
$this->_temporary_only_deleted = TRUE; | |
return $this; | |
} | |
/* -------------------------------------------------------------- | |
* OBSERVERS | |
* ------------------------------------------------------------ */ | |
/** | |
* MySQL DATETIME created_at and updated_at | |
*/ | |
public function created_at($row) | |
{ | |
if (is_object($row)) | |
{ | |
$row->created_at = date('Y-m-d H:i:s'); | |
} | |
else | |
{ | |
$row['created_at'] = date('Y-m-d H:i:s'); | |
} | |
return $row; | |
} | |
public function updated_at($row) | |
{ | |
if (is_object($row)) | |
{ | |
$row->updated_at = date('Y-m-d H:i:s'); | |
} | |
else | |
{ | |
$row['updated_at'] = date('Y-m-d H:i:s'); | |
} | |
return $row; | |
} | |
/** | |
* Serialises data for you automatically, allowing you to pass | |
* through objects and let it handle the serialisation in the background | |
*/ | |
public function serialize($row) | |
{ | |
foreach ($this->callback_parameters as $column) | |
{ | |
$row[$column] = serialize($row[$column]); | |
} | |
return $row; | |
} | |
public function unserialize($row) | |
{ | |
foreach ($this->callback_parameters as $column) | |
{ | |
if (is_array($row)) | |
{ | |
$row[$column] = unserialize($row[$column]); | |
} | |
else | |
{ | |
$row->$column = unserialize($row->$column); | |
} | |
} | |
return $row; | |
} | |
/** | |
* Protect attributes by removing them from $row array | |
*/ | |
public function protect_attributes($row) | |
{ | |
foreach ($this->protected_attributes as $attr) | |
{ | |
if (is_object($row)) | |
{ | |
unset($row->$attr); | |
} | |
else | |
{ | |
unset($row[$attr]); | |
} | |
} | |
return $row; | |
} | |
/* -------------------------------------------------------------- | |
* QUERY BUILDER DIRECT ACCESS METHODS | |
* ------------------------------------------------------------ */ | |
/** | |
* A wrapper to $this->_database->order_by() | |
*/ | |
public function order_by($criteria, $order = 'ASC') | |
{ | |
if ( is_array($criteria) ) | |
{ | |
foreach ($criteria as $key => $value) | |
{ | |
$this->_database->order_by($key, $value); | |
} | |
} | |
else | |
{ | |
$this->_database->order_by($criteria, $order); | |
} | |
return $this; | |
} | |
/** | |
* A wrapper to $this->_database->limit() | |
*/ | |
public function limit($limit, $offset = 0) | |
{ | |
$this->_database->limit($limit, $offset); | |
return $this; | |
} | |
/* -------------------------------------------------------------- | |
* INTERNAL METHODS | |
* ------------------------------------------------------------ */ | |
/** | |
* Trigger an event and call its observers. Pass through the event name | |
* (which looks for an instance variable $this->event_name), an array of | |
* parameters to pass through and an optional 'last in interation' boolean | |
*/ | |
public function trigger($event, $data = FALSE, $last = TRUE) | |
{ | |
if (isset($this->$event) && is_array($this->$event)) | |
{ | |
foreach ($this->$event as $method) | |
{ | |
if (strpos($method, '(')) | |
{ | |
preg_match('/([a-zA-Z0-9\_\-]+)(\(([a-zA-Z0-9\_\-\., ]+)\))?/', $method, $matches); | |
$method = $matches[1]; | |
$this->callback_parameters = explode(',', $matches[3]); | |
} | |
$data = call_user_func_array(array($this, $method), array($data, $last)); | |
} | |
} | |
return $data; | |
} | |
/** | |
* Run validation on the passed data | |
*/ | |
public function validate($data) | |
{ | |
if($this->skip_validation) | |
{ | |
return $data; | |
} | |
if(!empty($this->validate)) | |
{ | |
foreach($data as $key => $val) | |
{ | |
$_POST[$key] = $val; | |
} | |
$this->load->library('form_validation'); | |
if(is_array($this->validate)) | |
{ | |
$this->form_validation->set_rules($this->validate); | |
if ($this->form_validation->run() === TRUE) | |
{ | |
return $data; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
else | |
{ | |
if ($this->form_validation->run($this->validate) === TRUE) | |
{ | |
return $data; | |
} | |
else | |
{ | |
return FALSE; | |
} | |
} | |
} | |
else | |
{ | |
return $data; | |
} | |
} | |
/** | |
* Guess the table name by pluralising the model name | |
*/ | |
private function _fetch_table() | |
{ | |
if ($this->_table == NULL) | |
{ | |
$this->_table = plural(preg_replace('/(_m|_model)?$/', '', strtolower(get_class($this)))); | |
} | |
} | |
/** | |
* Guess the primary key for current table | |
*/ | |
private function _fetch_primary_key() | |
{ | |
if($this->primary_key == NULl) | |
{ | |
$this->primary_key = $this->_database->query("SHOW KEYS FROM `".$this->_table."` WHERE Key_name = 'PRIMARY'")->row()->Column_name; | |
} | |
} | |
/** | |
* Set WHERE parameters, cleverly | |
*/ | |
protected function _set_where($params) | |
{ | |
if (count($params) == 1 && is_array($params[0])) | |
{ | |
foreach ($params[0] as $field => $filter) | |
{ | |
if (is_array($filter)) | |
{ | |
$this->_database->where_in($field, $filter); | |
} | |
else | |
{ | |
if (is_int($field)) | |
{ | |
$this->_database->where($filter); | |
} | |
else | |
{ | |
$this->_database->where($field, $filter); | |
} | |
} | |
} | |
} | |
else if (count($params) == 1) | |
{ | |
$this->_database->where($params[0]); | |
} | |
else if(count($params) == 2) | |
{ | |
if (is_array($params[1])) | |
{ | |
$this->_database->where_in($params[0], $params[1]); | |
} | |
else | |
{ | |
$this->_database->where($params[0], $params[1]); | |
} | |
} | |
else if(count($params) == 3) | |
{ | |
$this->_database->where($params[0], $params[1], $params[2]); | |
} | |
else | |
{ | |
if (is_array($params[1])) | |
{ | |
$this->_database->where_in($params[0], $params[1]); | |
} | |
else | |
{ | |
$this->_database->where($params[0], $params[1]); | |
} | |
} | |
} | |
/** | |
* Return the method name for the current return type | |
*/ | |
protected function _return_type($multi = FALSE) | |
{ | |
$method = ($multi) ? 'result' : 'row'; | |
return $this->_temporary_return_type == 'array' ? $method . '_array' : $method; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment