root/tags/0.1_RC4/ignitedrecord.php
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers<?php
/*
* Created on 2008 Mar 28
* by Martin Wernstahl <m4rw3r@gmail.com>
*/
/**
* @page BSD_LICENSE BSD License
* @code
*
* Copyright (c) 2008, Martin Wernstahl
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The name of Martin Wernstahl may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Martin Wernstahl ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Martin Wernstahl BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @endcode
*/
/**
* @addtogroup IgnitedRecord
* @{
* An Object-Relational-Mapper model similar to Ruby on Rails ActiveRecord class.
*
* @version 0.1 RC 4
* @author Martin Wernstahl <m4rw3r@gmail.com>
* @par Copyright
* Copyright (c) 2008, Martin Wernstahl <m4rw3r@gmail.com>
* @par License:
* Released under the BSD Lisence: @ref BSD_LICENSE
*
* @par PHP Version
* PHP 4 or greater
*/
/**
* @page IgnitedRecord_Change_Log Change Log
* @anchor IgnitedRecord_Change_Log
* <hr>
* @section sec Version History
* @section sec 4 0.1 RC 4
* <ul>
* <li>Remade the structure for storing the relations, now supports multiple relations to the same table with the same type of relations (provoded their relation names are different)</li>
* <li>Added error messages if a method was not found in the <dfn>__call()</dfn> methods</li>
* <li>Loading of relations logs a warning message if a relation between the tables does not exist</li>
* <li>Improved the <dfn>_strip_data()</dfn> method a bit</li>
* <li>Bugfixes:
* <ul>
* <li><dfn>hooks()</dfn> argument missing</li>
* <li>instance property not found</li>
* <li>__relation_properties is not array</li>
* </ul>
* </li>
* </ul>
* @section sec 3 0.1 RC 3
* <ul>
* <li>Modified the hooks system, so references do work (if the recieving method takes the arguments by reference)</li>
* <li>Corrected the notice "Only variable references should be returned by reference"</li>
* <li>Made a check with references under PHP 4 (got db working, so now I can really test with PHP 4), so I believe they work as they should</li>
* </ul>
* @section sec2 0.1 RC 2
* <ul>
* <li>Finally a name for this model, IgnitedRecord!</li>
* <li>The data is now sanitized before insert/update, it removes all properties that doesn't match the field names</li>
* <li>Added the <dfn>get()</dfn> method</li>
* <li>Added the <dfn>factory()</dfn> method, creates a new model instance from supplied settings</li>
* <li>Added the <dfn>in_db()</dfn> method (IgnitedRecord_record)</li>
* <li>Added Act_As, to enable easier customization of the IgnitedRecord class.</li>
* <li>Added a Child_class_helper array which contains all helper class names to be assigned to the child classes.</li>
* <li>Implemented a basic hook system.</li>
* <li>Added a lot of hooks into the main IgnitedRecord class.</li>
* <li>Added the ability to name relations, this enables the use of relations referring to self and has_many, belongs_to, has_one and has_and_belongs_to_many can relate to the same table without them overwriting eachother (useful for the Adjacency tree).</li>
* <li>Created a MPTtree behaviour, IgnitedRecord_tree (requires <a href="http://www.cibase.info/index.php/code/details/11">MPTtree</a>)</li>
* </ul>
* @section sec1 0.1 RC 1
* <ul>
* <li><b>Initial Release</b></li>
* </ul>
*/
/**
* An ORM object representing a database record, extend to add extra functionality.
*
* Interacts closely with an IgnitedRecord model
*
* @author Martin Wernstahl <m4rw3r@gmail.com>
* @par Copyright
* Copyright (c) 2008, Martin Wernstahl <m4rw3r@gmail.com>
*
* @todo Add a property which contains the original data, and compare to this one on save (if not modified, do not save)
*/
class IgnitedRecord_record{
/**
* The IgnitedRecord instance that manages this record.
*/
var $__instance;
/**
* The table used storing this data.
*/
var $__table;
/**
* The corresponding database id for this objectt's row.
*
* null if no corresponding row exists
*/
var $__id = null;
/**
* An asociative array containing all loaded relationships.
*
* Tablename is key in the array
*/
var $__relations = array();
/**
* Stores the names of the properties storing relations.
*/
var $__relation_properties = array();
/**
* Constructor.
*
* @param $instance The IgnitedRecord instance
* @param $data The data this object shall be created from (only data from database)
*/
function IgnitedRecord_record(&$instance,$data = false){
$this->__instance =& $instance;
$this->__table = $instance->__table;
if($data != false){
$this->__id = $data[$instance->__id_col];
unset($data[$instance->__id_col]);
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
else{
$this->__id = null;
}
}
/**
* Returns true if this row is in the database, false otherwise.
*/
function in_db(){
return isset($this->__id);
}
/**
* Saves this object in the database using the IgnitedRecord instance.
*
* If this object does not exists in the database, an insert is performed,
* otherwise an update is performed.
*/
function save(){
if(isset($this->__instance))
$this->__instance->save($this);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Updates the data in this object.
*
* Not to be confused with the SQL update, this function
* refreshes the data in this object.
*
* @note All loaded relations will need to be reloaded after an update
* (if you want to use them).
*/
function update(){
if(isset($this->__instance))
$this->__instance->update($this);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Deletes this object from the database.
*
* Removes all relations and bindings to the database.
* Can then be inserted into the table again, using save().
*
* @note No data in this object (apart from relations and database id) are lost.
*/
function delete(){
if(isset($this->__instance) && $this->__id != null)
$this->__instance->delete($this);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Loads all associated objects/rows in relationships.
*
* @param $name Set to the name of the relation to load, omit if to load all relationships
*/
function load_rel($name = false){
if(isset($this->__instance) && $this->__id != null)
$this->__instance->load_rel($this,$name);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Loads the associated IgnitedRecord_records from the table $table.
*
* @param $name The name of the relation to load
*/
function load_related($name = false){
$this->load_rel($name);
}
/**
* Returns the related $table objects.
*
* Depending on if it is a Belongs To, Has Many, Has One or Has And Belongs To Many,
* the result can be an array or an IgnitedRecord_record. \n
* If the relation is not loaded, it gets loaded.
*
* @param $table The tablename to get related objects from
* @return An array or an IgnitedRecord_record, false if no relation is found
*/
function &get_related($table){
if(!isset($this->__relations[$table]))
$this->load_rel($table);
if(isset($this->__relations[$table]))
return $this->__relations[$table];
$false = false;
return $false;
}
/**
* Establishes a relationship with the supplied IgnitedRecord_record object.
*
* Depending on the settings of the IgnitedRecord instance tide to this object,
* the type of relation can vary. \n
* Ex. \n
* If you have defined two relations, one to "posts" with Has Many
* and one to "moderators" with Has And Belongs To Many,
* and you pass an object of the type "posts", a Has Many relation
* will be established. \n
* If there are no relation defined, no relationship will be formed.
*
* @param $object An IgnitedRecord_record
* @param $rel_name The name of the relation to add the object through, if omitted, IgnitedRecord will try to figure it out
*/
function add_relationship(&$object, $rel_name = false){
if(isset($this->__instance))
$this->__instance->establish_relationship($this, $object, $rel_name);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Somewhat of a shorthand for add_relationship(), but needs the relationship name.
*
* Example:
* @code
* $obj->add('child',$object);
* @endcode
*
* @param $rel_name The name of the relationship
* @param $object An IgnitedRecord_record to establish a relation with
*/
function add($rel_name, &$object){
$this->add_relationship($object, $rel_name);
}
/**
* Removes the relationship between $this and $object.
*
* If a relationship exists between the two, it is removed.
*
* @param $object An IgnitedRecord_record
* @param $rel_name The name of the relation to remove the object from
*/
function remove_relationship(&$object, $rel_name = false){
if(isset($this->__instance))
$this->__instance->remove_relationship($this, $object, $rel_name);
else
log_message('error','IgnitedRecord_record: No model is associated with the table '.$this->__table);
}
/**
* Somewhat of a shorthand for remove_relationship(), but needs the relationship name.
*
* Example:
* @code
* $obj->remove('child',$object);
* @endcode
*
* @param $rel_name The name of the relationship
* @param $object An IgnitedRecord_record to break a relation with
*/
function remove($rel_name, &$object){
$this->remove_relationship($object, $rel_name);
}
/**
* Aggregates the child helpers into this object, only PHP 5.
*
* @param $method The method called
* @param $args The argument array sent to the method
*
* @return Whatever the helper method returns
*/
function __call($method,$args){
foreach(array_keys($this->__instance->__child_class_helpers) as $property){
if(method_exists($this->$property, $method))
return call_user_func_array(array($this->$property, $method),$args);
}
show_error("IgnitedRecord_record: Method $method is not found.");
}
}
/**
* A model for ORM, extend to use.
*
* Uses the CodeIgniter inflector helper to pluralize / singularize modelname / tablename.
*
* Hooks:
* @code
* Hooks for the IgnitedRecord class:
* name args
* --------------------- ---------------------
*
* pre_find find id
* post_find find id
* reference to generated object
* pre_get
* post_get reference to generated object
* pre_find_by reference to the where array
* post_find_by where array
* reference t generated object
* pre_find_by_sql reference to sql string
* post_find_by_sql sql string
* reference to generated object
* pre_find_all
* post_find_all reference to return array
* pre_find_all_by reference to where array
* post_find_all_by where array
* reference to return array
* pre_find_all_by_sql reference to sql string
* post_find_all_by_sql sql string
* reference to return array
* post_new_record reference to generated object
* save_pre_strip_data reference to object to save
* save_post_strip_data reference to data array to be saved
* save_pre_insert reference to data array to be saved
* save_post_insert reference to object which has been inserted
* save_pre_update reference to data array to be saved
* save_post_update reference to object which has been updated
* post_save reference to object which has been updated
* pre_update reference to object to refreh
* post_update reference to newly created object
* pre_delete reference to object to be deleted
* pre_delete_query reference to object to be deleted
* post_delete reference to deleted object
* @endcode
*
* @author Martin Wernstahl <m4rw3r@gmail.com>
* @par Copyright
* Copyright (c) 2008, Martin Wernstahl <m4rw3r@gmail.com>
*
* @todo Make __auto_load_relations use joins instead, then we are loading two or more records at the same time
* @todo Maybe instead of auto_load_relations, make separate functions which utilize JOINs
* @todo Replace the call to CI's db_result::result() with calls to mysql_fetch_object(conn_id,'IgnitedRecord_record',array(&$this)), look in CI's DB_result and in PHP manual for mysql_fetch_object()
*/
class IgnitedRecord extends Model{
/**
* The table that stores all database rows.
*
* Default is plural of classname (using CodeIgniters Inflector helper) \n
* Override to specifically define the tablename
*/
var $__table = null;
/**
* The id column in database.
*
* Expected to be an auto_incremental unsigned int, IgnitedRecord never sets this column.
*/
var $__id_col = 'id';
/**
* The classname of the classes produced by this factory.
*
* Override normally to use another class as IgnitedRecord_record (preferably a descendant class)
*/
var $__child_class = 'IgnitedRecord_record';
/**
* Set to true to load all relationships automaticly when an IgnitedRecord_record is instantiated.
*
* Warning, can cause endless loops because of a related table having $__auto_load_relationships
* switched on and relates back to this table.
*/
var $__auto_load_relationships = false;
/**
* Defines how the database should behave.
*
* Packaged with IgnitedRecord by default: tree (list, rev (revision handling) are not finished)
*
* Set to empty array (default) if to not use any special behaviour.
*/
var $__act_as = array();
/**
* Data for a Belongs To relationships.
*
* There are three types of values that can be entered:
*
* @arg Tablename, as string or multiple in array
* @arg An array where the key is the tablename and value is the column with tablename_id
* (multiple entries are supported)
* @arg An array where the key is the tablename and value is an array with this structure:
* @code
* var $__belongs_to = array('tablename' => array('name' => 'property_name', (the name this reference)
* 'model' => 'modelname',
* 'col' => 'foreign_key_column'));
* @endcode
* This one can also contain multiple entries.
*
* Default values are the following:
* @arg tablename => as specified
* @arg name => modelname or singular(tablename)
* @arg model => tablename or singular(tablename), if a model with that name exists
* @arg col => tablename_id
*
* @note The foreign key is stored in this table, with default column name othertablename_id
* @note Needs to be defined before constructor kicks in.
*/
var $__belongs_to;
/**
* Data for a Has Many relationships.
*
* There are three types of values that can be entered:
*
* @arg Tablename, as string or multiple in array
* @arg An array where the key is the tablename and value is the column with tablename_id
* (multiple entries are supported)
* @arg An array where the key is the tablename and value is an array with this structure:
* @code
* var $__has_many = array('tablename' => array('name' => 'property_name', (the name this reference)
* 'model' => 'modelname',
* 'col' => 'foreign_key_column'));
* @endcode
* This one can also contain multiple entries.
*
* Default values are the following:
* @arg tablename => as specified
* @arg name => tablename
* @arg model => tablename or singular(tablename), if a model with that name exists
* @arg col => {$this->__table}_id
*
* @note The foreign key is stored in the other table, with default column name thistablename_id
* @note Needs to be defined before constructor kicks in.
*/
var $__has_many;
/**
* Data for a Has One relationships.
*
* There are three types of values that can be entered:
*
* @arg Tablename, as string or multiple in array
* @arg An array where the key is the tablename and value is the column with tablename_id
* (multiple entries are supported)
* @arg An array where the key is the tablename and value is an array with this structure:
* @code
* var $__has_one = array('tablename' => array('name' => 'property_name', (the name this reference)
* 'model' => 'modelname',
* 'col' => 'foreign_key_column'));
* @endcode
* This one can also contain multiple entries.
*
* Default values are the following:
* @arg tablename => as specified
* @arg name => modelname or singular(tablename)
* @arg model => tablename or singular(tablename), if a model with that name exists
* @arg col => {$this->__table}_id
*
* @note The foreign key is stored in the other table, with default column name thistablename_id
* @note Needs to be defined before constructor kicks in.
*/
var $__has_one;
/**
* Data for a Has And Belongs To many relationships.
*
* There are three types of values that can be entered:
*
* @arg Tablename, as string or multiple in array
* @arg An array where the key is the tablename and value is the name of the relations table
* (multiple entries are supported)
* @arg An array where the key is the tablename and value is an array with this structure:
* @code
* var $__has_and_belongs_to_many = array('othertablename' => array('name' => 'relation_name',
* 'table' => 'relation_tablename',
* 'this_col' => 'this_table_foreign_key_column',
* 'other_col' => 'other_table_foreign_key_column',
* 'model' => 'modelname_for_other_table'
* ));
* @endcode
* This one can also contain multiple entries.
*
* Default values are the following:
* @arg name => tablename
* @arg table => tablename_{$this->__table} or {$this->__table}_tablename (arranged in alphabetical order)
* @arg this_col => {$this->__table}_id
* @arg other_col => tablename_id
* @arg model => tablename or singular(tablename), if a model with that name exists
*
* @note Needs to be defined before constructor kicks in.
*/
var $__has_and_belongs_to_many;
/**
* Shorthand for $__has_and_belongs_to_many.
*
* @access private
*/
var $__habtm;
/**
* Stores the names of the related tables and their relation name.
*
* @access private
*/
var $__related_tables;
/**
* Cache for the table field metadata.
*
* Stores the names of the columns.
* If you define this by hand, you save one query
*/
var $__columns;
/**
* Child class helpers.
*
* These are assigned to properties of the child class.
* They should be a class whose constructor takes the IgnitedRecord_record.
*
* Is assigned by behaviours.
* key => propertyname, value => classname
*
* @access private
* (accessed by Behaviours)
*/
var $__child_class_helpers = array();
/**
* Stores the names of the loaded behaviours
*
* @access private
*/
var $__loaded_behaviours = array();
/**
* Stores all the registered hooks.
*
* @access private
*/
var $__hooks;
/**
* Constructor.
*
* Loads Behaviours.
* Sets the tablename if not already set and normalizes relationship data.
* Also loads the CodeIgniter inflector helper (if not loaded).
*/
function IgnitedRecord(){
parent::Model();
$this->_load_behaviours();
$this->load->helper('inflector');
if($this->__table == null){
$class = get_class($this);
if(strtolower(substr($class,-6)) == '_model')
$class = substr($class,0,-6); // remove '_model' from classname if it exists
$this->__table = plural($class);
}
// normalize relationship data
$this->_normalize_rel_belong($this->__belongs_to);
$this->_normalize_rel($this->__has_many,false);
$this->_normalize_rel($this->__has_one,true);
$this->_normalize_rel_habtm($this->__has_and_belongs_to_many);
// shorthand
$this->__habtm =& $this->__has_and_belongs_to_many;
}
/**
* Aggregates the behaviours into this object, only PHP 5.
*
* @param $method The method called
* @param $args The argument array sent to the method
*
* @return Whatever the helper method returns
*/
function __call($method,$args){
foreach($this->__loaded_behaviours as $property){
if(method_exists($this->$property, $method))
return call_user_func_array(array($this->$property, $method),$args);
}
show_error("IgnitedRecord: Method $method is not found.");
}
/**
* Adds a hook to the specified name and priority.
*
* @param $name The name of the hook
* @param $function The function to be called, or the array(classobj,function) to be called
* @param $priority The priority of the function lower = higher priority
*
* @return void
*/
function add_hook($name, $function, $priority = 10){
$this->__hooks[$name][$priority][] = $function;
}
/**
* Runs the hook(s) registred with the name $name.
*
* The return values will be in the order of their priority and
* false retuns will be skipped.
*
* @param $name The name of the hook to be called
* @param $data The array containing the data to pass on to registered functions
*
* @return empty array if no hook was run or if all hooks returned false,
* otherwise an array containing the value(s) from the registred function(s) is returned
*
* @todo Is the return values really needed?
*/
function hook($name,$data = array()){
// Have we got a hook for this specific event?
if (!isset($this->__hooks[$name])) {
// No, do nothing
return array();
}
else{
// Yes, sort the list by priority
ksort($this->__hooks[$name]);
}
$ret = array();
foreach($this->__hooks[$name] as $priority => $functions){
if(is_array($functions)){
foreach($functions as $func){
$tmp =& call_user_func_array($func,$data);
if($tmp !== false || $tmp != null)
$ret[] = $tmp;
}
}
}
return $ret;
}
/**
* Loads the behaviours specified in $__act_as.
*
* Expects the behaviours to be localized in files whith the names like:
* ignitedrecord_behaviourname.php and to have the class name linke this: IgnitedRecord_behaviourname
* If the behaviour class already exists, no loading of file takes place.
* The behaviourname is always lowercase
*/
function _load_behaviours(){
foreach((Array) $this->__act_as as $key => $act){
if(!is_numeric($key)){
$opt = $act;
$act = $key;
}
else{
$opt = array();
}
$act = strtolower($act);
$exists = false;
$class_name = 'IgnitedRecord_'.$act;
// is loaded, if not, try to load
if(!class_exists($class_name)){
if(file_exists(APPPATH.'models/ignitedrecord_'.$act.'.php')){
include_once(APPPATH.'models/ignitedrecord_'.$act.'.php');
if(class_exists($class_name))
$exists = true;
}
else{
log_message('warning',"IgnitedRecord: Behaviour file IgnitedRecord_$act.php does not exists in the models dir. Cannot load $act.");
}
}
else{
$exists = true;
}
if($exists == true){
// Check if a reference to a library, model or anything else
// exists in $this, all properties in this class are
// prefixed by '__' so there are no risk of overwriting them
if(isset($this->$act)){
// unset removes reference, prevents overwrite of the original data
unset($this->$act);
}
// load behaviour
$this->$act = new $class_name($this,$opt); // PHP 4: $this->$act =& new $class_name($this,$opt);
$this->__loaded_behaviours[] = $act;
}
else{
log_message('warning',"IgnitedRecord: Behaviour class IgnitedRecord_$act does not exists. Cannot load $act.");
}
}
}
/**
* Creates a new model from the parameters sent.
*
* uses eval() and (un)serialize()
*
* @param $name The name of the generated model.
* @param $settings The settings of this model (stored as propertyname as key and value as value)
* @param $str_return If to return the string generating that class, defaut: false
*
* @return A new derived class, or a string capable of creating the derived class
*/
function &factory($name, $settings = array(), $str_return = false){
$str = serialize($settings);
$data = "class {$name} extends IgnitedRecord{
function {$name}(){
\$settings = unserialize('{$str}');
foreach(\$settings as \$key => \$value){
\$this->\$key = \$value;
}
parent::IgnitedRecord();
}
}";
if($str_return)
return $data;
eval($data);
$model = new $name(); // PHP 4: $model =& new $name();
return $model;
}
//////////////////////////////////////////////
// Update and Find methods
//////////////////////////////////////////////
/**
* Fetches an IgnitedRecord_record from the db.
*
* To be used with CodeIgniter's ActiveRecord class to sort and filter the query.
* This method makes only one call, which fetches one row of data:
* @code
* $this->db->get($this->__table,1);
* @endcode
*
* @return A populated IgnitedRecord_record if result is found, false otherwise
*/
function &get(){
$this->hook('pre_get');
$query = $this->db->get($this->__table,1);
if(!$query->num_rows()){
$false = false;
return $false;
}
$obj =& $this->_dbobj2ORM($query->row_array());
$this->hook('post_get',array(&$obj));
return $obj;
}
/**
* Fetches the IgnitedRecord_record object with the id $id.
*
* @param $id The id of the row
*
* @return A populated IgnitedRecord_record if result is found, false otherwise
*/
function &find($id){
$this->hook('pre_find',array($id));
$query = $this->db->get_where($this->__table,array($this->__id_col => $id),1);
if(!$query->num_rows()){
$false = false;
return $false;
}
$obj =& $this->_dbobj2ORM($query->row_array());
$this->hook('post_find',array($id,&$obj));
return $obj;
}
/**
* Fetches the IgnitedRecord_record object where the $column contains $data.
*
* This function merges the $column and $data into one array,
* where the $column becomes the key(s) in the array and
* the $data becomes the value(s), using _array_combine(). \n
* This array is then passed into CodeIgniter's db::get_where()
* method as the where clause.
*
* @param $column The column name(s) to match, typecasted to array if not already
* @param $data The data value(s) to match, typecasted to array if not already
*
* @return A populated IgnitedRecord_record if result is found, false otherwise
*/
function &find_by($column, $data){
if(($where = $this->_array_combine((Array)$column, (Array)$data)) === false){
log_message('error','IgnitedRecord: Number of columns and number of data argumnents does not match in call to find_by.');
$false = false;
return $false;
}
$this->hook('pre_find_by',array(&$where));
$query = $this->db->get_where($this->__table, $where, 1);
if(!$query->num_rows()){
$false = false;
return $false;
}
$obj =& $this->_dbobj2ORM($query->row_array());
$this->hook('post_find_by',array($where,&$obj));
return $obj;
}
/**
* Fetches the IgnitedRecord_record object with the sql supplied.
*
* @param $sql The sql query, needs escaping of values (must start with SELECT * or equivalent)
*
* @return A populated IgnitedRecord_record if a result is found, false otherwise
*/
function &find_by_sql($sql){
$this->hook('pre_find_by_sql',array(&$sql));
$query = $this->db->query($sql);
if(!$query->num_rows()){
$false = false;
return $false;
}
$obj =& $this->_dbobj2ORM($query->row_array());
$this->hook('post_find_by_sql',array($sql,&$obj));
return $obj;
}
/**
* Fetches all IgnitedRecord_record objects in the database.
*
* @return An array with populated IgnitedRecord_records, empty array if table is empty
*/
function &find_all(){
$arr = array();
$this->hook('pre_find_all');
$query = $this->db->get($this->__table);
foreach($query->result_array() as $row){
$arr[] =& $this->_dbobj2ORM($row);
}
$this->hook('post_find_all',array(&$arr));
return $arr;
}
/**
* Fetches all IgnitedRecord_record objects where the $column contains $data.
*
* This function merges the $column and $data into one array,
* where the $column becomes the key(s) in the array and
* the $data becomes the value(s), using _array_combine(). \n
* This array is then passed into CodeIgniter's db::get_where()
* method as the where clause.
*
* @param $column The column name(s) to match
* @param $data The data which will be used to match
*
* @return An array with populated IgnitedRecord_records if results are found, empty array otherwise
*/
function &find_all_by($column, $data){
$arr = array();
if(($where = $this->_array_combine((Array)$column, (Array)$data)) === false){
log_message('error','IgnitedRecord: Number of columns and number of data argumnents does not match in call to find_by.');
return $arr;
}
$this->hook('pre_find_by',array(&$where));
$query = $this->db->get_where($this->__table, $where, 1);
foreach($query->result_array() as $row){
$arr[] =& $this->_dbobj2ORM($row);
}
$this->hook('post_find_all_by',array($where,&$obj));
return $arr;
}
/**
* Fetches all IgnitedRecord_record objects which match the sql supplied.
*
* @param $sql The sql query, needs escaping of values (must start with SELECT * or equivalent)
*
* @return An array with populated IgnitedRecord_records if resultas are found, false otherwise
*/
function &find_all_by_sql($sql){
$this->hook('pre_find_by_sql',array(&$sql));
$arr = array();
$query = $this->query($sql);
foreach($query->result_array() as $row){
$arr[] =& $this->_dbobj2ORM($row);
}
$this->hook('post_find_all_by',array($sql,&$arr));
return $arr;
}
/**
* Creates a new empty IgnitedRecord_record object.
*
* @param $data The data of the new object, is assigned to the new object before it is returned
*
* @return A new IgnitedRecord_record
*/
function &new_record($data = array()){
$ref =& $this->_dbobj2ORM($data);
$this->hook('post_new_record',array(&$ref));
return $ref;
}
/**
* Saves the supplied object in the databse.
*
* Takes a reference of the object (edits the object after insert)'
*
* @note Can only save the $object if it belongs to the same table as this model
*
* @param $object The object to be inserted or updated, passed by reference
*/
function save(&$object){
if($this->__table == $object->__table){
$this->hook('save_pre_strip_data',array(&$object));
// remove some values that shall not belong in the database
$data =& $this->_strip_data($object);
$this->hook('save_post_strip_data',array(&$data));
// check if row exists
if($object->__id == null){
// no row exists in database, insert
$this->hook('save_pre_insert',array(&$data));
$this->db->insert($this->__table,$data);
$object->__id = $this->db->insert_id(); // grabs the id of the inserted row
$this->hook('save_post_insert',array(&$object));
}
else{
// update
$this->hook('save_pre_update',array(&$data));
$this->db->where($this->__id_col,$object->__id);
$this->db->update($this->__table,$data);
$this->hook('save_post_update',array(&$object));
}
$this->hook('post_save',array(&$object));
}
else{
show_error('Incompatible object supplied to '.classname($this).', tables does not match');
}
}
/**
* Updates the data in the supplied object.
*
* @note Can only perform an update if the $object belongs to the same table as this instance
* @note Replaces the object with a clean one, losing all loaded relations
* (you have to load them again with load_rel() if you need them)
*
* @param $object The object to be updated, passed by reference
*/
function update(&$object){
if($this->__table == $object->__instance->__table){
// check if row exists
if($object->__id != null){
$this->hook('pre_update',array(&$object));
$query = $this->db->get_where($this->__table,array($this->__id_col => $object->__id),1);
$object =& $this->_dbobj2ORM($query->row_array());
$this->hook('post_update',array(&$object));
}
}
}
/**
* Deletes the $object from the database if it exists in database.
*
* Also clears all relations with other rows.
*
* @param $object The IgnitedRecord_record to remove from database
*/
function delete(&$object){
if(isset($object->__id)){
$this->hook('pre_delete',array(&$object));
// remove relationships
foreach($object->__relation_properties as $name){
unset($object->$name);
}
// no need to do anything for Belongs To, the data is stored in this table
foreach($this->__has_many as $table => $data){
$this->db->set($data['col'],null);
$this->db->where($data['col'], $obj->__id);
$this->db->update($table);
}
foreach($this->__has_one as $table => $data){
$this->db->set($data['col'],null);
$this->db->where($data['col'], $obj->__id);
$this->db->update($table);
}
foreach($this->__habtm as $table => $data){
$this->db->delete($data['rel_table'],array($data['this_col'] => $object->__id));
}
unset($object->__relations);
unset($object->__relation_properties);
$this->hook('pre_delete_query',array(&$object));
$this->db->delete($this->__table, array($this->__id_col => $object->__id));
unset($object->__id);
$this->hook('post_delete',array(&$object));
}
}
/**
* Removes all data from the associated table, and also all relationships.
*
* @attention All Data Will Be Lost! Including all relations with other tables!
*/
function delete_all(){
$this->db->empty_table($this->__table);
// Belongs to relations are stored in this table, no need to do anything
foreach($this->__has_many as $table => $data){
$this->db->set($data['col'],null);
$this->db->update($table);
}
foreach($this->__has_one as $table => $data){
$this->db->set($data['col'],null);
$this->db->update($table);
}
foreach($this->__habtm as $table => $data){
// remove all rows where this_col is not null
// which is enabling the use of a relations table for more than two tables
$this->db->delete($data['rel_table'],array($data['this_col'].' !=' => null));
}
}
//////////////////////////////////////////////
// Relationship methods
//////////////////////////////////////////////
// These methods are called by objects this class creates,
// so I recommend that you only call these methods if you know what you are doing.
// Please use the methods in IgnitedRecord_record instead
/**
* Loads all associated relationships for the object $obj.
* Primary storage of the loaded relationships are in the $obj->__relations array
* with the relationship name as key. \n
*
* Secondary storage is $obj->$rel_name
*
* @note If any of the secondary storage properties are already occupied an overwrite will not be performed
*
* @param $obj The object to load rrelationships for
* @param $rel_name Set to the tablename to init relationships for only this table, omit if to load all relationships
*/
function load_rel(&$obj,$rel_name = false){
if(!isset($obj->__id))
return; // no id = not stored in database = no relationships
// establish relationships
$CI =& get_instance();
// Belongs To relationship
// Stores the data in obj->modelname (or singular of tablename) as default
if($rel_name == false || in_array($rel_name,array_keys($this->__belongs_to))){
foreach($this->__belongs_to as $name => $data){
if($rel_name != false && $name != $rel_name || isset($obj->__relations[$name]))
continue; // skip if tablename is not false and don't match, or if we already have loaded that one
$table =& $data['table'];
if(isset($obj->$data['col']) && $obj->$data['col'] != ''){
if($data['model'] != ''){
// we have a model, load the specific model
if(!in_array($data['model'],get_object_vars($CI)))
$CI->load->model($data['model']);
// use that model to instantiate the objects
$model_inst =& $CI->$data['model'];
$obj->__relations[$name] =& $model_inst->find($obj->$data['col']);
if(!isset($obj->$name) && $obj->__relations[$name] != false){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
else{
// no model = normal IgnitedRecord_record OBJ
$model = singular($name);
$query = $this->db->get_where($table,array('id' => $obj->$data['col']),1);
$obj->__relations[$name] =& $this->_dbobj2ORM($query->row_array(),false);
if(!isset($obj->$name) && $obj->__relations[$name] != false){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
$obj->__relations[$name]->__table = $table;
unset($obj->__relations[$name]->__instance);
}
}
else{
log_message('warning','IgnitedRecord: no foreign key column specified in belongs to relationship between '.$this->__table.' and '.$data['table']);
}
}
}
// Has Many relationship
// Stores the data in an array in obj->tablename (tablename is plural) as default
if($rel_name == false || in_array($rel_name,array_keys($this->__has_many))){
foreach($this->__has_many as $name => $data){
if($rel_name != false && $name != $rel_name || isset($obj->__relations[$name]))
continue; // skip if tablename is not false and don't match, or if we already have loaded that one
$table =& $data['table'];
if($data['model'] != ''){
// we have a model, load the specific model
if(!in_array($data['model'],get_object_vars($CI)))
$CI->load->model($data['model']);
// use that model to instantiate the objects
$model_inst =& $CI->$data['model'];
// use tablename (plural), because it has 'many'
$obj->__relations[$name] =& $model_inst->find_all_by($data['col'], $obj->__id);
if(!isset($obj->$name) && count($obj->__relations[$name])){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
else{
// no model = normal orm OBJ
// use tablename (plural), because it has 'many'
$obj->__relations[$name] = array();
$query = $this->db->get_where($table,array($data['col'] => $obj->__id));
foreach($query->result_array() as $row){
$ORM =& $this->_dbobj2ORM($row,false);
$ORM->__table = $table;
unset($ORM->__instance);
$obj->__relations[$name][] =& $ORM;
}
if(!isset($obj->$name) && count($obj->__relations[$name])){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
}
}
// Has One relationship
// Stores the data in obj->modelname (or singular of tablename) as default
if($rel_name == false || in_array($rel_name,array_keys($this->__has_one))){
foreach($this->__has_one as $name => $data) {
if($rel_name != false && $name != $rel_name || isset($obj->__relations[$name]))
continue; // skip if tablename is not false and don't match, or if we already have loaded that one
$table =& $data['table'];
if($data['model'] != ''){
// we have a model, load the specific model
if(!in_array($data['model'],get_object_vars($CI)))
$CI->load->model($data['model']);
// use that model to instantiate the objects
$model_inst =& $CI->$data['model'];
$obj->__relations[$name] =& $model_inst->find_by($data['col'],$obj->__id);
if(!isset($obj->$name)){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
else{
// no model = normal orm OBJ
$query = $this->db->get_where($table,array($data['col'] => $obj->__id),1);
$obj->__relations[$name] =& $this->_dbobj2ORM($query->row_array(),false);
$obj->__relations[$name]->__table = $table;
unset($obj->__relations[$name]->__instance);
if(!isset($obj->$name) && $obj->__relations[$name] != false){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
}
}
// Has And Belongs To Many relationship
// Stores the data in an array in obj->tablename (tablename is plural)
if($rel_name == false || in_array($rel_name,array_keys($this->__habtm))){
foreach($this->__habtm as $name => $data){
if($rel_name != false && $name != $rel_name || isset($obj->__relations[$name]))
continue; // skip if tablename is not false and don't match, or if we already have loaded that one
// get data from relations table
$query = $this->db->get_where($data['rel_table'],array($data['this_col'] => $obj->__id));
if($data['model'] != ''){
// we have a model, load the specific model
if(!in_array($data['model'],get_object_vars($CI)))
$CI->load->model($data['model']);
// use that model to instantiate the objects
$model_inst =& $CI->$data['model'];
// use tablename (plural), because it has 'many'
$obj->__relations[$name] = array();
// iterate through and load all rows into objects
foreach($query->result_array() as $row){
$orm =& $model_inst->find($row[$data['other_col']]);
if($orm != false)
$obj->__relations[$name][] =& $orm;
}
if(!isset($obj->$name) && count($obj->__relations[$name])){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
else{
// no model = normal orm OBJ
$ids = array();
// create id array
foreach($query->result_array() as $row){
$ids[] = $row[$data['other_col']];
}
if(count($ids)){
// assumes that id is the unique id
$this->db->where_in('id',$ids);
$query = $this->db->get($data['table']);
// use tablename (plural), because it has 'many'
$obj->__relations[$name] = array();
foreach($query->result_array() as $row){
$ORM =& $this->_dbobj2ORM($row,false);
$ORM->__table = $data['table'];
unset($ORM->__instance);
$obj->__relations[$name][] =& $ORM;
}
if(!isset($obj->$name) && count($obj->__relations[$name])){
$obj->$name =& $obj->__relations[$name];
$obj->__relation_properties[] = $name;
}
}
}
}
}
}
/**
* Establishes a relationship between the two supplied objects.
*
* Determines which method that shall be used; Belongs To, Has Many, Has One or Has And Belongs To Many. \n
* Performs some checking of input
*
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
* @param $rel_name The name of the relationship, leave blank if to auto determine
*/
function establish_relationship(&$child, &$object, $rel_name = false){
if(!isset($child->__instance) || $child->__instance->__table != $this->__table){
log_message('warning','An incompatible object was supplied to an IgnitedRecord object belonging to the table '. $child->__table);
return;
}
if(!isset($object->__instance)){
log_message('warning','An incompatible object was supplied to an IgnitedRecord object belonging to the table '. $object->__table);
return;
}
if($rel_name == false){
$rel_name = isset($this->__related_tables[$object->__table]) ? $this->__related_tables[$object->__table] : $object->__table;
}
// Belongs To
if(in_array($rel_name, array_keys($child->__instance->__belongs_to)))
$this->establish_belongs_to_relationship($child->__instance->__belongs_to[$rel_name],$child,$object);
// Has Many
elseif(in_array($rel_name, array_keys($child->__instance->__has_many)))
$this->establish_has_many_relationship($child->__instance->__has_many[$rel_name], $child,$object);
// Has One
elseif(in_array($rel_name, array_keys($child->__instance->__has_one)))
$this->establish_has_one_relationship($child->__instance->__has_one[$rel_name], $child,$object);
// Has And Belongs To Many
elseif(in_array($rel_name, array_keys($child->__instance->__habtm)))
$this->establish_habtm_relationship($child->__instance->__habtm[$rel_name],$child,$object);
else
log_message('warning',"IgnitedRecord: no relation with the relationname $rel_name was found, no relation established.");
}
/**
* Establishes a Belongs To relationship between $child and $object.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function establish_belongs_to_relationship($rel, &$child, &$object){
// $child Belongs To $object
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
if(!isset($object->__id)){
// object does not exist in database, save it
$object->save();
}
// object now exists in database
$child->$column = $object->__id;
$child->save();
}
/**
* Establishes a Has Many relationship between $child and $object.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function establish_has_many_relationship($rel, &$child, &$object){
// $child Has Many of $object type
// Like an inverted Belongs To relationship
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
if(!isset($child->__id)){
// this object does not exist in database, save it
$child->save();
}
// this object now exists in database
$object->$column = $child->__id;
$object->save();
}
/**
* Establishes a Has One relationship between $child and $object.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function establish_has_one_relationship($rel, &$child, &$object){
// $child Has One of $object type
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
if(!isset($child->__id)){
$child->save();
}
else{
$related =& $object->__instance->find_all_by($column,$child->__id);
// remove all relationships to related objects in that table
foreach($related as $r){
$r->$column = null;
$r->save();
}
}
$object->$column = $child->__id;
$object->save();
}
/**
* Establishes a Has And Belongs To Many relationship between $child and $object.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function establish_habtm_relationship($rel, &$child, &$object){
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
if(!isset($child->__id)){
$child->save();
}
if(!isset($object->__id)){
$object->save();
}
$this->db->where($rel['this_col'],$child->__id);
$this->db->where($rel['other_col'],$object->__id);
$query = $this->db->get($rel['rel_table']);
if(!$query->num_rows()){
// no relationship established
$data[$rel['this_col']] = $child->__id;
$data[$rel['other_col']] = $object->__id;
$this->db->insert($rel['rel_table'],$data);
}
}
/**
* Removes the relationship between the two supplied objects if it exists.
*
* Determines which method that shall be used; Belongs To, Has Many, Has One or Has And Belongs To Many. \n
* Performs some checking of input
*
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
* @param $rel_name The name of the relationship, leave blank if to auto determine
*/
function remove_relationship(&$child, &$object, $rel_name = false){
if(!isset($child->__instance) || $child->__instance->__table != $this->__table){
log_message('warning','An incompatible object was supplied to an IgnitedRecord object belonging to the table '. $child->__table);
return;
}
if(!isset($object->__instance)){
log_message('warning','An incompatible object was supplied to an IgnitedRecord object belonging to the table '. $object->__table);
return;
}
if($rel_name == false){
$rel_name = isset($this->__related_tables[$object->__table]) ? $this->__related_tables[$object->__table] : $object->__table;
}
// Belongs To
if(in_array($rel_name, array_keys($child->__instance->__belongs_to)))
$this->remove_belongs_to_relationship($child->__instance->__belongs_to[$rel_name],$child,$object);
// Has Many
elseif(in_array($rel_name, array_keys($child->__instance->__has_many)))
$this->remove_has_many_relationship($child->__instance->__has_many[$rel_name],$child,$object);
// Has One
elseif(in_array($rel_name, array_keys($child->__instance->__has_one)))
$this->remove_has_one_relationship($child->__instance->__has_one[$rel_name],$child,$object);
elseif(in_array($object->__table, array_keys($child->__instance->__habtm)))
$this->remove_habtm_relationship($child->__instance->__habtm[$rel_name],$child,$object);
else
log_message('warning',"IgnitedRecord: no relation with the relationname $rel_name was found, no relation removed.");
}
/**
* Removes a Belongs To relationship between $child and $object, if it exists.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function remove_belongs_to_relationship($rel, &$child, &$object){
// $child Belongs To $object
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
if(!isset($object->__id) || !isset($child->$column))
return;
if($child->$column == $object->__id){
$child->$column = null;
$child->save();
}
}
/**
* Removes a Has Many relationship between $child and $object, if it exists.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function remove_has_many_relationship($rel, &$child, &$object){
// $child Has Many of $object type
// like an inverted Belongs To relationship
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
if(!isset($child->__id) || !isset($object->$column))
return;
if($object->$column == $child->__id){
$object->$column = null;
$object->save();
}
}
/**
* Removes a Has One relationship between $child and $object, if it exists.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function remove_has_one_relationship($rel, &$child, &$object){
// $child Has One of $object type
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a relation with the table '.$object->__table);
return;
}
$column = $rel['col'];
// works exactly like Has Many (only when establishing a relationship is it different)
if(!isset($child->__id) || !isset($object->$column))
return;
if($object->$column == $child->__id){
$object->$column = null;
$object->save();
}
}
/**
* Removes a Has And Belongs To Many relationship between $child and $object, if it exists.
*
* @note No checking if a relationship are defined between the two.
*
* @param $rel The relationship data array
* @param $child An IgnitedRecord_record created by this IgnitedRecord model
* @param $object Another IgnitedRecord_record (must have an instance tied to it)
*/
function remove_habtm_relationship($rel, &$child, &$object){
if($rel['table'] != $object->__table){
// no matching table
log_message('error','IgnitedRecord: The table '.$child->__table.' has not got a has and belongs to relation with the table '.$object->__table);
return;
}
if(!isset($child->__id) || !isset($object->__id))
return;
$this->db->where($rel['this_col'],$child->__id);
$this->db->where($rel['other_col'],$object->__id);
$this->db->delete($rel['rel_table']);
}
//////////////////////////////////////////////
// Private methods
//////////////////////////////////////////////
/**
* Creates an IgnitedRecord_record object from the supplied db row.
*
* @param $data The data to load from (array)
* @param $dynamic If to load the object dynamically, true uses the user defined class otherwise it uses the IgnitedRecord_record
*
* @return A populated IgnitedRecord_record (or descendant of it)
*/
function &_dbobj2ORM($data,$dynamic = true){
$class = $this->__child_class;
if($dynamic)
$obj = new $class($this,$data); // PHP 4: $obj =& new $class($this,$data);
else
$obj = new IgnitedRecord_record($this,$data); // PHP 4: $obj =& new IgnitedRecord_record($this,$data);
if($this->__auto_load_relationships)
$obj->load_rel();
foreach($this->__child_class_helpers as $name => $hclass){
$obj->$name = new $hclass($obj); // PHP 4: $obj->$name =& new $hclass($obj);
}
return $obj;
}
/**
* Removes all properties that are not data.
*
* Skips the relation properties, the child helpers
* instance variables (tied to IgnitedRecord and other classes)
* and also the id column (all properties which are not table columns.
*
* @param $object The object to be cleaned
*
* @return An associative array containing references to the data in the object,
* key is property name and value is value of the property
*/
function &_strip_data(&$object){
$columns = isset($this->__columns) ? $this->__columns : $this->db->list_fields($this->__table);
$data = array();
foreach($columns as $col){
if(isset($object->$col))
$data[$col] = $object->$col;
}
return $data;
}
/**
* Normalizes the relationship data structure in the $data to a standard for belong to relationships.
*
* By reference.\n
* Normalizes has_* relationship data into this form:
* @code
* array('relation_name' => array('table' => 'tablename',
* 'model' => 'modelname',
* 'col' => 'foreign_key_column_name'),
* 'relation2' => array('table' => 'atable',
* ...)
* );
* @endcode
* from this form:
* @code
* = 'tablename';
* // or multiple:
* = array('tablename','tablename');
* // or the advanced variant:
* = array('name' => 're4lationname',
* 'table' => 'tablename', // all others except this one can be omitted
* 'col' => 'foreign_key_col',
* 'model' => 'modelname'
* );
* // or the encapsulated variant (enables multiple advanced):
* = array(array('name' => 'relationname',
* 'table' => 'tablename',
* 'col' => 'foreign_key_col',
* 'model' => 'modelname'
* ),
* array('table' => 'anothertable'),
* 'atable'
* );
* @endcode
*
*
* If value is not an array, it is assumed that it is the column name for the foreign key
*
* @param $data The data property
* @param $is_singular If the relationship name shall be singular (ie for has one relations)
*/
function _normalize_rel(&$data,$is_singular){
// normalize to arrays
$data = (Array) $data;
if(isset($data['table'])) // we have a complex setting that isn't encapsulated in an array
$data = array($data);
$new_arr = array();
foreach($data as $entry){
if(is_string($entry))
$entry = array('table' => $entry);
if(!isset($entry['table'])){
log_message('warning','IgnitedRecord: No table specified for one of the Has_* relations.');
continue;
}
if(!isset($entry['name'])){
$rel_name = isset($entry['model']) ? $entry['model'] : $this->_rel_name($entry['table'],$is_singular);
}
else{
$rel_name = $entry['name'];
}
if(!isset($entry['model'])){
$entry['model'] = $this->_get_modelname($entry['table']);
}
if(!isset($entry['col'])){
$entry['col'] = $this->__table.'_id';
}
$new_arr[$rel_name] = $entry;
}
$data = $new_arr;
// add the tablenames and their relation name so we can figure out it easy if we need to
foreach($data as $name => $value){
$this->__related_tables[$value['table']] = $name;
}
}
/**
* Normalizes the relationship data structure in the $data to a standard for has- relationships.
*
* By reference.\n
* Normalizes belong to relationship data into this form:
* @code
* array('relation_name' => array('table' => 'tablename',
* 'model' => 'modelname',
* 'col' => 'foreign_key_column_name'),
* 'relation2' => array('table' => 'atable',
* ...)
* );
* @endcode
* from this form:
* @code
* = 'tablename';
* // or multiple:
* = array('tablename','tablename');
* // or the advanced variant:
* = array('name' => 'relationname',
* 'table' => 'tablename', // all others except this one can be omitted
* 'col' => 'foreign_key_col',
* 'model' => 'modelname'
* );
* // or the encapsulated variant (enables multiple advanced):
* = array(array('name' => 'relationname',
* 'table' => 'tablename',
* 'col' => 'foreign_key_col',
* 'model' => 'modelname'
* ),
* array('table' => 'anothertable'),
* 'atable'
* );
* @endcode
* The difference to has relationships is that belong to relationships stores the foreign key in this table
* (and hence the foreign key column differs).
*
* If value is not an array, it is assumed that it is the column name for the foreign key
*
* @param $data The data property
*/
function _normalize_rel_belong(&$data){
// normalize to arrays
$data = (Array) $data;
if(isset($data['table'])) // we have a complex setting that isn't encapsulated in an array
$data = array($data);
$new_arr = array();
foreach($data as $entry){
if(is_string($entry))
$entry = array('table' => $entry);
if(!isset($entry['table'])){
log_message('warning','IgnitedRecord: No table specified for one of the Belongs_To relations.');
continue;
}
if(!isset($entry['name'])){
$rel_name = isset($entry['model']) ? $entry['model'] : $this->_rel_name($entry['table'],true);
}
else{
$rel_name = $entry['name'];
}
if(!isset($entry['model'])){
$entry['model'] = $this->_get_modelname($entry['table']);
}
if(!isset($entry['col'])){
$entry['col'] = $entry['table'].'_id';
}
$new_arr[$rel_name] = $entry;
}
$data = $new_arr;
// add the tablenames and their relation name so we can figure out it easy if we need to
foreach($data as $name => $value){
$this->__related_tables[$value['table']] = $name;
}
}
/**
* Normalizes Has And Belongs To Many relationship data.
*
* By reference.\n
* Normalizes has and belongs to many relationship data into this form:
* @code
* array('relation_name' => array('other_table' => 'othertablename',
* 'table' => 'relation_tablename',
* 'this_col' => 'this_table_foreign_key_column',
* 'other_col' => 'other_table_foreign_key_column',
* 'model' => 'modelname_for_other_table'
* ));
* @endcode
* from this form:
* @code
* = 'tablename';
* // or multiple:
* = array('tablename','tablename');
* // or the advanced variant:
* = array('name' => 'relationname',
* 'table' => 'tablename', // all others except this one can be omitted
* 'rel_table' => 'relation_tablename'
* 'this_col' => 'foreign_key_col',
* 'other_col' => 'foreign_key_col',
* 'model' => 'modelname'
* );
* // or the encapsulated variant (enables multiple advanced):
* = array(array('name' => 'relationname',
* 'table' => 'tablename', // all others except this one can be omitted
* 'rel_table' => 'relation_tablename'
* 'this_col' => 'foreign_key_col',
* 'other_col' => 'foreign_key_col',
* 'model' => 'modelname'
* ),
* array('table' => 'anothertable'),
* 'atable'
* );
* @endcode
*
* If value is not an array, it is assumed that it is the table name for the table storing the relations
*
* @param $data The property holding Has And Belongs To Many data
*/
function _normalize_rel_habtm(&$data){
// normalize to array
$data = (Array) $data;
if(isset($data['table'])) // we have a complex setting that isn't encapsulated in an array
$data = array($data);
$new_arr = array();
foreach($data as $entry){
if(is_string($entry))
$entry = array('table' => $entry);
if(!isset($entry['table'])){
log_message('warning','IgnitedRecord: No table specified for one of the Has_And_Belongs_To_Many relations.');
continue;
}
if(!isset($entry['name'])){
$rel_name = isset($entry['model']) ? $entry['model'] : $this->_rel_name($entry['table'],false);
}
else{
$rel_name = $entry['name'];
}
if(!isset($entry['rel_table'])){
$entry['rel_table'] = strcmp($this->__table, $entry['table']) < 0 ?
$this->__table.'_'.$entry['table'] :
$entry['table'].'_'.$this->__table;
}
if(!isset($entry['this_col'])){
$entry['this_col'] = $this->__table.'_id';
}
if(!isset($entry['other_col'])){
$entry['other_col'] = $entry['table'].'_id';
}
if(!isset($entry['model'])){
$entry['model'] = $this->_get_modelname($entry['table']);
}
$new_arr[$rel_name] = $entry;
}
$data = $new_arr;
// add the tablenames and their relation name so we can figure out it easy if we need to
foreach($data as $name => $value){
$this->__related_tables[$value['table']] = $name;
}
}
/**
* Returns the modelname of the tablename.
*
* Tries if a model with $tablename exists, then tries with singular form of the $tablename. \n
* Called if no model is defined for a table.
*
* @param $tablename The tablename to find a modelname of.
*
* @return The modelname or '' if no model is found
*/
function _get_modelname($tablename){
$tablename = strtolower($tablename);
$model = '';
if(file_exists(APPPATH.'/models/'.$tablename.EXT)){
$model = $tablename;
}
elseif(file_exists(APPPATH.'/models/'.singular($tablename).EXT)){
$model = singular($tablename);
}
return $model;
}
/**
* Returns the default name of the relationship.
*
* @param $name The tablename
* @param $is_singular If the relationship relates to a sigle node
*/
function _rel_name($name,$is_singular){
if($is_singular)
return $this->_get_modelname($name) != '' ? $this->_get_modelname($name) : singular($name);
else
return $name;
}
/**
* Combines two non asociative arrays to one asociative.
*
* Exists in PHP 5, but not in 4
*
* @param $keys The array with the keys
* @param $values The array with the values
*
* @return An asociative array with the keys from $keys and the values from $values
*/
function _array_combine($keys,$values) {
if(function_exists('array_combine'))
return array_combine($keys,$values); // PHP 5
$out = array();
foreach($keys as $key1 => $value1){
$out[$value1] = $values[$key1];
}
return $out;
}
}
/**
* @}
*/
?> |