Completing a Model for the CodeIgniter PHP Framework - The generic model class's full source code (Page 4 of 4 )
If you thought that the development of this generic model class would never finish, take a deep breath and check out the example below. I listed its entire source code, this time including the new methods discussed in the previous segment and a few others that extend its functionality. Here is how the complete class now looks:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter
*
* An open source application development framework for PHP 4.3.2 or newer
*
* @package CodeIgniter
* @author ExpressionEngine Dev Team
* @copyright Copyright (c) 2006, EllisLab, Inc.
* @license http://codeigniter.com/user_guide/license.html
* @link http://codeigniter.com
* @since Version 1.0
* @filesource
*/
// ------------------------------------------------------------------------
/**
* CodeIgniter Abstract Model Class
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
* @author ExpressionEngine Dev Team
* @link http://codeigniter.com/user_guide/libraries/config.html
*/
abstract class MY_Model extends Model
{
protected $table = ''; // table associated to the model
protected $fields = array(); // fields of table associated to the model
protected $id = NULL; // value of the primary key of the table associated to the model
protected $data = array(); // model input data
protected $insertID = NULL; // insertion ID
protected $numRows = NULL; // number of rows returned by SELECTS
protected $validation = array(); // model validation rules
protected $errors = array(); // model errors
/**
* Constructor
*
* @access protected
*/
protected function __construct()
{
parent::Model();
// get CI superobject as a model property
$this->ci =& get_instance();
}
/**
* Sets associated table data for the model
*
* @author Alejandro Gervasio
* @return void
* @access public
*/
public function setTableData($table = 'default')
{
if ($this->db->table_exists($table))
{
$this->table = $table;
$this->fields = $this->db->field_names($this->table);
}
}
/**
* Sets value of primary key of the associated table for the model
*
* @author Alejandro Gervasio
* @param integer
* @return void
* @access public
*/
public function setID($id)
{
$this->id = is_integer($id) AND $id > 0 ? $id : 1;
}
/**
* Gets value of primary key of the associated table for the model
*
* @author Alejandro Gervasio
* @return integer
* @access public
*/
public function getID()
{
return $this->id;
}
/** sets input data for the model
*
* @author Alejandro Gervasio
* @param array
* @return void
* @access public
*/
public function setData($data)
{
if ( is_array($data) AND count($data) > 0)
{
foreach ($data as $key => $value)
{
if (array_search($key, $this->fields) === FALSE)
{
unset($data[$key]);
}
}
$this->data = $data;
}
}
/**
* Sets validation rules for model data
*
* @author Alejandro Gervasio
* @param array
* @return void
* @access public
*/
public function setValidation($validation)
{
if ( is_array($validation) AND count($validation) > 0)
{
foreach ($validation as $field => $rule)
{
if (array_search($field, $this->fields) === FALSE)
{
unset($validation[$key]);
}
}
$this->validation = $validation;
}
}
/**
* Returns a result set with specified fields according to given conditions.
*
* @author Alejandro Gervasio
* @return query result on success - Boolean FALSE on failure
* @access public
*/
public function fetch($fields = '*', $where = NULL, $order = 'id ASC', $limit = NULL, $offset = 0, $join = NULL)
{
if ($fields != '*')
{
$this->db->select($fields);
}
if ($this->id != NULL)
{
$this->db->where('id', $this->id);
}
elseif($where != NULL)
{
$this->db->where($where);
}
if ($order != 'id ASC')
{
$this->db->orderby($order);
}
if ($limit != NULL)
{
$this->db->limit($limit, $offset);
}
if( $join != NULL)
{
$this->db->join($join);
}
$query = $this->db->get($this->table);
$this->numRows = $query->num_rows();
if ($this->numRows > 0)
{
return ($this->numRows > 1 ) ? $query->result() : $query->row();
}
$this->errors[] ='No rows were returned by the query.';
return FALSE;
}
/** Saves model data into associated table (validation rules are applied to input data)
*
*
* @author Alejandro Gervasio
* @return integer on success - Boolean FALSE on failure
* @access public
*/
public function save()
{
if ($this->data == NULL)
{
$this->errors[] = 'Error saving row.';
return FALSE;
}
// validate input data
if( !$this->validate())
{
return FALSE;
}
// Insert new row if ID was not set in the model
if ($this->id == NULL)
{
$this->db->insert($this->table, $this->data);
$this->insertID = $this->db->insert_id();
return $this->insertID;
}
// Otherwise update existing row
else
{
$this->db->where('id', $this->id)->update($this->table, $this->data);
return $this->db->affected_rows();
}
}
/** Deletes model data from associated table (validation rules are applied to input data)
*
*
* @author Alejandro Gervasio
* @return Boolean TRUE on success - Boolean FALSE on failure
* @access public
*/
public function delete()
{
if ($this->id == NULL)
{
$this->errors[] = 'Error deleting row.';
return FALSE;
}
$this->db->where('id', $this->id)->delete($this->table);
return TRUE;
}
/**
* Counts all rows in associated table
*
* @author Alejandro Gervasio
* @return integer (if the associated table is not empty)
* @access public
*/
public function countAll()
{
return $this->db->count_all($this->table);
}
/**
* Gets insertion ID
*
* @author alejandro gervasio
* @return integer
* @access public
*/
public function getInsertID()
{
return $this->insertID;
}
/**
* Executes a given query
*
* @author Alejandro Gervasio
* @param string
* @return result set/Boolean TRUE on success - Boolean FALSE on failure
* @access public
*/
public function query($query)
{
if (is_string($query))
{
if (FALSE === ($query = $this->db->query($query)))
{
$this->errors[] = 'Error performing query.';
}
return $query;
}
}
/**
* Gets number of rows returned by fetch() method
*
* @author Alejandro Gervasio
* @return integer
* @access public
*/
public function getNumRows()
{
return $this->numRows;
}
/** Gets insertion ID
*
* @author Alejandro Gervasio
* @return integer
* @access public
*/
public function getInsertID()
{
return $this->insertID;
}
/**
* Gets model errors
*
* @author Alejandro Gervasio
* @param string
* @return string
* @access public
*/
public function getErrors($errPrefix = '<p>')
{
// if no errors where found return an empty string
if( count($this->errors) === 0)
{
return '';
}
// otherwise build and return error string
$errorString = '';
$errorSufix = str_replace( '<', '</' , $errPrefix);
foreach ($this->errors as $error)
{
$errorString .= $errPrefix . $error . $errorSufix;
}
return $errorString;
}
/**
* Clears model properties
*
* @author Alejandro Gervasio
* @return void
* @access public
*/
public function clear()
{
$this->id = NULL;
$this->data = array();
$this->errors = array();
$this->validation = array();
}
/**
* Validates model input data
*
* @author Alejandro Gervasio
* @return Boolean TRUE on success - FALSE on failure
* @access protected
*/
protected function validate()
{
// If no validation rules or no model were provided data set appropriate error
if (count($this->validation) === 0)
{
$this->errors[] = 'No validation rules were set for the model.';
return FALSE;
}
// Loads CI validation library
$this->ci->load->library('validation');
// Load CI language file for validation
$this->ci->lang->load('validation');
// resets error messages
$this->errors = array();
foreach ($this->validation as $field => $rules)
{
$expRules = explode('|', $rules);
// if the field is not required check next one
if (! in_array('required', $expRules, TRUE))
{
continue;
}
// Iterates through the validation rules
foreach ($expRules as $rule)
{
// Removes the parameter from the rule (when specified)
$param = FALSE;
if (preg_match("/(.*?)[(.*?)]/", $rule, $match))
{
$rule = $match[1];
$param = $match[2];
}
// Calls the validation method that corresponds to the rule
if (method_exists($this->ci->validation, $rule))
{
$result = $this->ci->validation->$rule($this->data[$field], $param);
}
else
{
// Tries to run a native PHP function if method of CI validation class doesn't exist
if (function_exists($rule))
{
$result = $rule($this->data[$field]);
}
}
// if an offending field was found store error message in error array
if ($result === FALSE)
{
$this->errors[] = sprintf($this->ci->lang->line($rule),$field);
}
}
}
return (count($this->errors)) === 0 ? TRUE : FALSE;
}
}
// END MY_Model Class
/* End of file MY_Model.php */
/* Location: ./system/application/libraries/MY_Model.php */
That was pretty lengthy, right? Nonetheless, don’t feel intimidated by the class’s signature. As you’ve seem in the preceding tutorials, each of its methods is quite easy to follow. Also, aside from including the methods that you learned in the previous section, there are others that perform simple tasks, such as counting the number of rows present in the associated database table, returning the insertion ID and so forth. But, since these are extremely intuitive, I won’t waste more of your time explaining how they work.
Final thoughts
In this seventh part of the series, I finally completed the development of this generic model class for CodeIgniter. Of course, as is true with each piece of software available nowadays, the class can be largely improved with minor efforts. It encapsulates most of the functionality required for building database-driven applications, except for the fact that it can’t directly handle common relationships like one-to-one, one-to-many, etc. If you need this functionality, don’t forget that these relationships can be implemented in the controllers as well.
Now that the generic model has been completed, I’m going to dedicate the last installment of the series to showing you how to use it within a simple MySQL-driven application. Therefore, don’t miss the last tutorial!
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.
Please enable JavaScript to view the comments powered by Disqus. blog comments powered by