Created
August 4, 2011 06:24
-
-
Save tanakahisateru/1124597 to your computer and use it in GitHub Desktop.
Model assosiation support for CodeIgniter
This file contains 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 if ( ! defined('BASEPATH')) exit('Direct script access not allowed'); | |
/** | |
* This library bakes related object(s) into object field (or array key) using CI's ActiveRecord. | |
* | |
* @author Hisateru Tanaka | |
* | |
* Annual issue: | |
* Explicit sql programing is too redundant and too difficult to describe about relations. | |
* | |
* $this->db->where('id', $row->parent_id); | |
* $r = $this->db->get('parents_table'); | |
* $row->parent = $r->row(); | |
* | |
* The solution: | |
* Implicit sql building from RDB constraint helps our coding to be smarter. | |
* | |
* Usage: | |
* $this->load->('relations'); | |
* $this->relations->ref($row, 'parent', 'parents_table', 'parent_id'); | |
* | |
* [before] | |
* stdClass Object | |
* ( | |
* ... | |
* [parent_id] => 1 | |
* ) | |
* | |
* [after] | |
* stdClass Object | |
* ( | |
* ... | |
* [parent_id] => 1, | |
* [parent] => stdClass Object | |
* ( | |
* [id] => 1 | |
* ... | |
* ) | |
* ) | |
* | |
* Supported features: | |
* - Many to One : ref() | |
* - One to Many : has_many() | |
* - Many to Many : medium() and has_many_ref() | |
* - Execution for all rows | |
* - Both of Object and Array row type supported | |
* - Automatic removal of redundant forward key | |
* - Custom SQL sentence using CI's ActiveRecord | |
* | |
* Remarks: | |
* This library is not a O/R mapping framework! Below features are not supported. | |
* - Lazy fetching | |
* - Automatic transaction scope management | |
* - Instance pooling and caching | |
* - SQL optimization | |
* - Predefined inclusive mapping | |
* | |
* But one important advantage than huge O/R mapper is that it doesn't take any startup cost. | |
* You don't have to pay any more for performance and configuration than now. | |
*/ | |
class Relations { | |
var $result_type = 'object'; | |
var $remove_fk = FALSE; | |
var $queries = array(); | |
var $queries_medium = array(); | |
var $medium_def = NULL; | |
var $remain_fixture = FALSE; | |
function _initdb(){ | |
if(!isset($this->db)){ | |
$CI =& get_instance(); | |
$CI->load->database(); | |
$this->db =& $CI->db; | |
} | |
} | |
function _clear_fixtures() { | |
if(!$this->remain_fixture) { | |
$this->queries = array(); | |
$this->queries_medium = array(); | |
$this->medium_def = NULL; | |
} | |
} | |
function _do_arquery(&$queries) { | |
foreach($queries as $eq) { | |
eval('$this->db->' . $eq . ';'); | |
} | |
} | |
function &_get_from(&$obj, $field) | |
{ | |
if(is_object($obj)) { | |
return $obj->$field; | |
} | |
else if(is_array($obj)) { | |
return $obj[$field]; | |
} | |
else { | |
return NULL; | |
} | |
} | |
function _set_to(&$obj, $field, &$value) | |
{ | |
if(is_object($obj)) { | |
$obj->$field = $value; | |
} | |
else if(is_array($obj)) { | |
$obj[$field] = $value; | |
} | |
} | |
function _del_field(&$obj, $field) | |
{ | |
if(is_object($obj) && isset($obj->$field)) { | |
unset($obj->$field); | |
} | |
else if(is_array($obj) && array_key_exists($field, $obj)) { | |
unset($obj[$field]); | |
} | |
} | |
/*-- preparation functions ----------------------------------------------------*/ | |
/** | |
* Sets result row type as 'object' or 'array'. | |
* This setting remains until next call of result_as. | |
*/ | |
function result_as($type) | |
{ | |
$this->result_type = $type; | |
} | |
/** | |
* Revoves forward key which used to connect instances each other. | |
* This setting remains until next call of replace_with_key. | |
*/ | |
function replace_with_key($flag=TRUE) | |
{ | |
$this->remove_fk = $flag; | |
} | |
/** | |
* Appends ActiveRecord command executed before relation baking. | |
* This setting would be cleared on the next call of execution function. | |
* @param command would be sent to "eval" fnction with "$this->db->" prefix. | |
*/ | |
function arquery($command) | |
{ | |
$this->queries[] = $command; | |
} | |
/** | |
* Shortcut for $this->arquery('select("...")'); | |
* Recommended: This is safer than arquery. | |
*/ | |
function select($select = '*', $protect_identifiers = TRUE) | |
{ | |
$this->arquery('select("' . $select . '",' . ($protect_identifiers ? 'TRUE' : 'FALSE') . ')'); | |
} | |
/** | |
* Shortcut for $this->arquery('order_by("...")'); | |
* Recommended: This is safer than arquery. | |
*/ | |
function order_by($orderby, $direction = '') | |
{ | |
$this->arquery('order_by("' . $orderby . '","' . $direction . '")'); | |
} | |
/** | |
* Appends ActiveRecord command executed before fetching medium table. | |
* This setting would be cleared on the next call of execution function. | |
*/ | |
function medium_arquery($command){ | |
$this->queries_medium[] = $command; | |
} | |
/** | |
* Defines a medium table specification for next relation balking. | |
* This setting would be cleared on the next call of execution function. | |
*/ | |
function medium($table, $fk_to_mine, $fk_to_yours) { | |
$this->medium_def = array($table, $fk_to_mine, $fk_to_yours); | |
} | |
/*-- execution functions ----------------------------------------------------*/ | |
/** | |
* This function injects a related object into a row. | |
* It provides Many to One relation. | |
*/ | |
function ref(&$obj, $field, $table, $fk_mine, $pk_yours='id') | |
{ | |
$this->_initdb(); | |
$this->_do_arquery($this->queries); | |
$q = $this->db->get_where($table, array($pk_yours => $this->_get_from($obj, $fk_mine))); | |
$this->_set_to($obj, $field, $q->row(0, $this->result_type)); | |
if($this->remove_fk) { | |
$this->_del_field($obj, $fk_mine); | |
} | |
$this->_clear_fixtures(); | |
} | |
/** | |
* This is array version of ref. | |
*/ | |
function ref_all(&$obj_arr, $field, $table, $fk_mine, $pk_yours='id') | |
{ | |
$this->remain_fixture = TRUE; | |
foreach(array_keys($obj_arr) as $i) { | |
$this->ref($obj_arr[$i], $field, $table, $fk_mine, $pk_yours); | |
} | |
$this->remain_fixture = FALSE; | |
$this->_clear_fixtures(); | |
} | |
/** | |
* This function injects directly related object(s) as array into a row. | |
* It provides One to Many relation. | |
*/ | |
function has_many(&$obj, $field, $table, $fk_yours, $pk_mine='id') | |
{ | |
$this->_initdb(); | |
$this->_do_arquery($this->queries); | |
$q = $this->db->get_where($table, array($fk_yours => $this->_get_from($obj, $pk_mine))); | |
$r = $q->result($this->result_type); | |
if($this->remove_fk) { | |
foreach(array_keys($r) as $i) { | |
$this->_del_field($r[$i], $fk_yours); | |
} | |
} | |
$this->_set_to($obj, $field, $r); | |
$this->_clear_fixtures(); | |
} | |
/** | |
* This is array versioned of has_many. | |
*/ | |
function has_many_all(&$obj_arr, $field, $table, $fk_yours, $pk_mine='id') | |
{ | |
$this->remain_fixture = TRUE; | |
foreach(array_keys($obj_arr) as $i) { | |
$this->has_many($obj_arr[$i], $field, $table, $fk_yours, $pk_mine); | |
} | |
$this->remain_fixture = FALSE; | |
$this->_clear_fixtures(); | |
} | |
/** | |
* This function injects medium table referencing object(s) as array into a row. | |
* It provides Many to Many relation. | |
*/ | |
function has_many_ref(&$obj, $field, $table, $pk_mine='id', $pk_yours='id') | |
{ | |
$this->_initdb(); | |
$this->_do_arquery($this->queries_medium); | |
list($medium_table, $fk_to_mine, $fk_to_yours) = $this->medium_def; | |
$this->db->select($fk_to_yours); | |
$q = $this->db->get_where($medium_table, array($fk_to_mine => $this->_get_from($obj, $pk_mine))); | |
$m = $q->result(); | |
$rels = array(); | |
foreach($m as $rel) { | |
$rels[] = $rel->$fk_to_yours; | |
} | |
$this->_do_arquery($this->queries); | |
$q = $this->db->where_in($pk_yours, $rels); | |
$q = $this->db->get($table); | |
$this->_set_to($obj, $field, $q->result($this->result_type)); | |
if(!$this->remain_fixture) { | |
$this->_clear_fixtures(); | |
} | |
} | |
/** | |
* This is array version of has_many_ref. | |
*/ | |
function has_many_ref_all(&$obj_arr, $field, $table, $pk_mine='id', $pk_yours='id') | |
{ | |
$this->remain_fixture = TRUE; | |
foreach(array_keys($obj_arr) as $i) { | |
$this->has_many_ref($obj_arr[$i], $field, $table, $pk_mine, $pk_yours); | |
} | |
$this->remain_fixture = FALSE; | |
$this->_clear_fixtures(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment