Andy's Blog » » CI ORM (Object Relational Mapper) 对象关系映射 [PHP5]

CI ORM (Object Relational Mapper) 对象关系映射 [PHP5]

作者:沧蓝

[1.5.4] ORM (Object Relational Mapper) 对象关系映射 [PHP5]
注意事项
- 本程序仅支持PHP5。
- 本程序是07年9月份的时候写的,之后一直没再碰过。
- 本程序未开发完成!目前仅能检索数据。
- 发布本程序一是希望抛砖引玉,二是因为这个程序可能不再继续开发了,我可能会转为开发Kohana的版本。
- 程序没有优化过,所以如果看到很小白的地方请见谅啊。

什么是ORM?

对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
 
/**
 * Object Relational Mapper
 * Maps database tables with associations to programming objects
 *
 * Database requirement (optional but recommended, for schema caching):
 *
 *  CREATE TABLE orm_schema (
 *   id INT NOT NULL AUTO_INCREMENT,
 *   table_name VARCHAR(30) NULL,
 *   table_schema TEXT NULL,
 *   PRIMARY KEY(id)
 *  )
 *
 * Please don't forget to set $cache to TRUE in the code
 *
 *
@package CodeIgniter
 *
@subpackage library
 *
@author Fred Wu
 *
@version 0.1.0
 *
@license GNU Lesser General Public License (LGPL) [url]http://www.gnu.org/licenses/lgpl.html[/url]
 **/

class ORM {
        
        
/**
         * CI database object
         *
         *
@access protected
         *
@var object
         **/

        
protected $db;
        
        
/**
         * Table name
         *
         *
@access public
         *
@var string
         **/

        
public $table;
        
        
/**
         * Schema cache
         * uses database to cache the table schema
         *
         *
@access protected
         *
@var bool
         **/

        
protected $cache = FALSE; // <-- set to TRUE to enable caching, FALSE to disable it
        
        
protected $tableMeta;
        
        
/**
         * Constructor
         *
         *
@access public
         *
@param string $table table name
         **/

        
public function __construct($table = NULL)
        
{
                
if ($table == NULL)
                
{
                        
$msg = 'ORM initilization failed, no table specified';
                        
log_message('error', $msg);
                        
show_error($msg);
                
}
                
else
                
{
                        
// calls CodeIgniter's database functions
                        
$this->db =& get_instance()->db;
                        
                        
$this->table = $table;
                        
                        
// loading complete
                        
log_message('debug', 'ORM Initialized for '.ucfirst($table).' table.');
                
}
                
        
}
        
        
/**
         * Table Metadata
         * constructs table metadata
         *
         *
@access private
         *
@param bool   $initialCall initial call to the function (default is true)
         *
@return array                           table metadata
         **/

        
private function _tableMeta($initialCall = TRUE)
        
{
                
$table = $this->table;
                
                
if ($initialCall == TRUE && $this->cache == TRUE)
                
{
                        
$query_schema  = $this->db->query('SELECT * FROM orm_schema WHERE table_name = "'.$table.'"');
                        
$result_schema = $query_schema->result_array();
                
}
                
                
if (isset($result_schema[0]) && $this->cache == TRUE)
                
{
                        
$metadata = unserialize($result_schema[0]['table_schema']);
                
}
                
else
                
{
                        
// SHOW CREATE TABLE query
                        
$query_showTable  = $this->db->query('SHOW CREATE TABLE '.$table);
                        
$result_showTable = $query_showTable->result_array();
                        
$showTable                = $result_showTable[0]['Create Table'];
                        
                        
/*echo '<pre>';
                        print_r($showTable);
                        echo '</pre>';*/

                        
                        
/********************
                          Primary Keys
                        ********************/

                        
                        
// reads the primary key definition line
                        
preg_match('/PRIMARY KEY +(`.*`)/i', $showTable, $match_PKs);
                        
$str_PK = $match_PKs[0];
                        
// cleans up the PK string so that we can split it into a list of primary keys
                        
preg_match('/.*(`(.*)`)/', $str_PK, $str_PKClean);
                        
$PKs = preg_split('/`,`/', $str_PKClean[1]);
                        
// reorganises the primary keys
                        
foreach ($PKs as $PK)
                        
{
                                
// does the primary key auto_increment? (regex in caseless and multiline mode)
                                
if (preg_match('/'.$PK.'.*auto_increment,$/im', $showTable))
                                
{
                                        
$metadata['PKs'][$PK] = 'AUTO_INCREMENT';
                                
}
                                
else
                                
{
                                        
$metadata['PKs'][$PK] = NULL;
                                
}
                        
}
                        
                        
/********************
                          Foreign Keys
                        ********************/

                        
                        
// reads the foreign key definition lines
                        
preg_match_all('/CONSTRAINT.*FOREIGN KEY +(`(.*)`) +REFERENCES +`(.*)` +(`(.*)`).*/i',
                                                  
$showTable, $match_FKs, PREG_SET_ORDER);
                        
// reorganises the foreign keys
                        
$i = 0;
                        
foreach ($match_FKs as $FK)
                        
{
                                
$FK_id = $FK[1];
                                
// the foreign key
                                
$metadata['FKs'][$i]['FK'] = $FK_id;
                                
// reference table of the foreign key
                                
$metadata['FKs'][$i]['ref'] = $FK[2];
                                
// primary key of the referenced table
                                
$metadata['FKs'][$i]['refPK'] = $FK[3];
                                
                                
// reference tables (for the conjunction table)
                                
foreach($PKs as $PK)
                                
{
                                        
if ($FK_id == $PK)
                                        
{
                                                
// is a conjunction table: a list of the referenced tables
                                                
$metadata['isConjunction'][] = $metadata['FKs'][$i]['ref'];
                                        
}
                                
}
                                
// belongsTo tables
                                
if (!isset($metadata['isConjunction']))
                                
{
                                        
$belongsTo_table = $metadata['FKs'][$i]['ref'];
                                        
                                        
$metadata['belongsTo'][$belongsTo_table]['FK']        = $FK_id;
                                        
$metadata['belongsTo'][$belongsTo_table]['refPK'] = $metadata['FKs'][$i]['refPK'];
                                
}
                                
$i++;
                        
}
                        
                        
/********************
                          Primary Key Attributes
                        ********************/

                        
                        
// shows attributes only if they're non-foreign keys and non-AUTO_INCREMENT
                        
foreach ($PKs as $PK)
                        
{
                                
$fields = $this->db->field_data($table);
                                
if (!isset($metadata['isConjunction']) && $metadata['PKs'][$PK] != 'AUTO_INCREMENT')
                                
{
                                        
foreach ($fields as $field)
                                        
{
                                                
if ($PK == $field->name)
                                                
{
                                                        
$metadata['PKs'][$PK][] = $this->_fieldType($table, $PK, 'type');
                                                        
$metadata['PKs'][$PK][] = $this->_fieldType($table, $PK, 'max_length');
                                                
}
                                        
}
                                
}
                        
}
                        
                        
/********************
                          Fields
                        ********************/

                        
                        
if ($initialCall == TRUE && !isset($metadata['isConjunction']))
                        
{
                                
$fields = $this->db->field_data($table);
                                
foreach ($fields as $field)
                                
{
                                        
// lists only non-key fields
                                        
if (array_key_exists($field->name, $metadata['PKs']) OR
                                                
isset($metadata['FKs']) && array_key_exists($field->name, $metadata['FKs']))
                                        
{
                                        
}
                                        
else
                                        
{
                                                
$metadata['fields'][$field->name][] = $this->_fieldType($field->name, 'type');
                                                
$metadata['fields'][$field->name][] = $this->_fieldType($field->name, 'max_length');
                                        
}
                                
}
                        
}
                        
                        
/********************
                          Associations
                        ********************/

                        
                        
// only run this once
                        
if ($initialCall == TRUE && !isset($metadata['isConjunction']))
                        
{
                                
// a list of all tables
                                
$tables_rebuild = $this->db->list_tables();
                                
// rebuilds each individual table
                                
foreach ($tables_rebuild as $table_rebuild)
                                
{
                                        
// prepares to rebuild the data
                                        
$this->table = $table_rebuild;
                                        
$metadata_rebuild[$table_rebuild] = $this->_tableMeta($initialCall = FALSE);
                                        
$this->table = $table;
                                        
$initialCall = TRUE;
                                
}
                                
// goes through each tables
                                
foreach ($metadata_rebuild as $table_rebuild_name => $table_rebuild_data)
                                
{
                                        
// hasOneMany
                                        
if (isset($table_rebuild_data['belongsTo']))
                                        
{
                                                
// goes through each 'belongsTo' relationships
                                                
foreach ($table_rebuild_data['belongsTo'] as $table_associated => $table_associated_PK)
                                                
{
                                                        
if ($table == $table_associated)
                                                        
{
                                                                
foreach ($table_rebuild_data['FKs'] as $table_rebuild_FK)
                                                                
{
                                                                        
if ($table_rebuild_FK['ref'] == $table_associated)
                                                                        
{
                                                                                
$refPK = array_keys($table_rebuild_data['PKs']);
                                                                                
                                                                                
$metadata['HOM'][$table_rebuild_name]['refFK'] = $table_rebuild_FK['FK'];
                                                                                
$metadata['HOM'][$table_rebuild_name]['refPK'] = $refPK[0];
                                                                        
}
                                                                
}
                                                        
}
                                                
}
                                        
}
                                        
// hasAndBelongsToMany
                                        
if (isset($table_rebuild_data['isConjunction']))
                                        
{
                                                
if ($table == $table_rebuild_data['isConjunction'][0])
                                                
{
                                                        
$table_rebuild_data_HABTMTable = $table_rebuild_data['isConjunction'][1];
                                                        
$table_remote_PK                           = $table_rebuild_data['FKs'][1]['refPK'];
                                                
}
                                                
elseif ($table == $table_rebuild_data['isConjunction'][1])
                                                
{
                                                        
$table_rebuild_data_HABTMTable = $table_rebuild_data['isConjunction'][0];
                                                        
$table_remote_PK                           = $table_rebuild_data['FKs'][0]['refPK'];
                                                
}
                                                
if (isset($table_rebuild_data_HABTMTable))
                                                
{
                                                        
foreach ($table_rebuild_data['FKs'] as $table_rebuild_FK)
                                                        
{
                                                                
if ($table_rebuild_FK['ref'] == $table)
                                                                
{
                                                                        
$metadata['HABTM'][$table_rebuild_data_HABTMTable]['conjTable'] = $table_rebuild_name;
                                                                        
$metadata['HABTM'][$table_rebuild_data_HABTMTable]['PK']                = $PK;
                                                                        
$metadata['HABTM'][$table_rebuild_data_HABTMTable]['refFK']         = $table_rebuild_FK['FK'];
                                                                        
                                                                        
$refPKs = array_keys($table_rebuild_data['PKs']);
                                                                        
foreach ($refPKs as $refPK)
                                                                        
{
                                                                                
if ($refPK != $table_rebuild_FK['FK'])
                                                                                
{
                                                                                        
$metadata['HABTM'][$table_rebuild_data_HABTMTable]['refPK'] = $refPK;
                                                                                
}
                                                                        
}
                                                                        
$metadata['HABTM'][$table_rebuild_data_HABTMTable]['refPKPK'] = $table_remote_PK;
                                                                
}
                                                        
}
                                                
}
                                        
}
                                
}
                        
}
                        
                        
if ($initialCall == TRUE && $this->cache == TRUE)
                        
{
                                
// stores metadata to database
                                
$this->db->query('INSERT INTO orm_schema (table_name, table_schema)
                                                                 VALUES ("
'.$table.'", "'.addslashes(serialize($metadata)).'");');
                        
}
                
}
                
                
if ($initialCall == TRUE)
                
{
                        
/*echo '<pre> <b>metadata for '.$table.'</b>
';
                        print_r($metadata);
                        echo '
 
';
                        echo '</pre>';*/

                
}
                
                
$this->tableMeta = $metadata;
                
                
return $metadata;
        
}
        
        
/**
         * Field Type
         * finds field type and max_length
         * this function is needed because MySQL/CodeIgniter's inbuilt function does not work
         *
         *
@access private
         *
@param string $field          field name
         *
@param string $returnType 'type' or 'max_length', default returns the field type string
         *
@return string                         field type data
         **/

        
private function _fieldType($field, $returnType = NULL)
        
{
                
$table = $this->table;
                
                
// SHOW COLUMNS query
                
$query_fieldsData  = $this->db->query('SHOW COLUMNS FROM '.$table);
                
$result_fieldsData = $query_fieldsData->result_array();
                
                
foreach ($result_fieldsData as $fieldData)
                
{
                        
if ($fieldData['Field'] == $field)
                        
{
                                
$fieldType['str'] = $fieldData['Type'];
                        
}
                
}
                
                
// analyses the field type string
                
preg_match('/(D+)((d+)).*/', $fieldType['str'], $match_type);
                
                
// returns type
                
if ($returnType == 'type')
                
{
                        
if (isset($match_type[1]))
                        
{
                                
return $fieldType['type'] = $match_type[1];
                        
}
                        
// for fields like 'text' where there is no brackets and max length
                        
else
                        
{
                                
return $fieldType['str'];
                        
}
                
}
                
// returns max_length
                
elseif ($returnType == 'max_length')
                
{
                        
if (isset($match_type[2]))
                        
{
                                
return $fieldType['max_length'] = $match_type[2];
                        
}
                
}
                
// returns the string
                
else
                
{
                        
return $fieldType['str'];
                
}
        
}
        
        
/**
         * Find
         * finds table data with an optional WHERE query and associated data
         *
         *
@access public
         *
@param string or array $withAssoc   associated tables in an array or 'ALL' for all associated tables
         *
@param string                  $query_where optional WHERE query
         *
@return array                                                table data (objects) in an array
         **/

        
public function find($withAssoc = NULL, $query_where = NULL)
        
{
                
$table = $this->table;
                
                
// initial table data with no associated data
                
if (isset($query_where) && preg_match('/= +w/i', $query_where))
                
{
                        
$query_tableData = $this->db->query("SELECT * FROM $table WHERE $query_where");
                
}
                
// ignores the query if the passed value is NULL or is empty
                
elseif (isset($query_where) AND !preg_match('/= +w/i', $query_where) || stristr($query_where, '= NULL'))
                
{
                        
log_message('info', 'Query error detected and ignored in $this->orm->find() for table: '.$this->table);
                        
return FALSE;
                
}
                
else
                
{
                        
$query_tableData = $this->db->query('SELECT * FROM '.$table);
                
}
                
$result_tableData = $query_tableData->result();
                
                
if ($withAssoc != NULL)
                
{
                        
// table metadata
                        
$tableMeta = $this->_tableMeta($table);
                        
// available associated tables
                        
$assoc_belongsTo = (array)$tableMeta['belongsTo'];
                        
$assoc_HOM           = (array)$tableMeta['HOM'];
                        
$assoc_HABTM         = (array)$tableMeta['HABTM'];
                        
$assoc_All           = @array_merge($assoc_belongsTo, $assoc_HOM, $assoc_HABTM);
                        
                        
if ($withAssoc == 'ALL')
                        
{
                                
$withAssoc_new = $assoc_All;
                        
}
                        
// populates association schema
                        
else
                        
{
                                
foreach ($withAssoc as $assocTable)
                                
{
                                        
if (array_key_exists($assocTable, $assoc_All))
                                        
{
                                                
$withAssoc_new[$assocTable] = $assoc_All[$assocTable];
                                        
}
                                
}
                                
                        
}
                        
$withAssoc = $withAssoc_new;
                        
                        
// applies every associated table
                        
foreach ($withAssoc as $assocTable => $assocSchema)
                        
{
                                
$i = 0;
                                
// applies to every data record
                                
foreach ($result_tableData as $dataRecord)
                                
{
                                        
// belongsTo
                                        
if (array_key_exists($assocTable, $assoc_belongsTo) && $dataRecord->$assocSchema['FK'] != NULL)
                                        
{
                                                
$this->table = $assocTable;
                                                
$assocData   = $this->find(NULL, $assocSchema['refPK'].' = '.$dataRecord->$assocSchema['FK']);
                                                
$this->table = $table;
                                                
                                                
$dataRecord->$assocTable = $assocData[0];
                                        
}
                                        
// hasOneMany
                                        
elseif (array_key_exists($assocTable, $assoc_HOM) && $dataRecord->$assocSchema['refPK'] != NULL)
                                        
{
                                                
$this->table                         = $assocTable;
                                                
$dataRecord->$assocTable = $this->find(NULL, $assocSchema['refFK'].' = '.$dataRecord->$assocSchema['refPK']);
                                                
$this->table                         = $table;
                                        
}
                                        
// hasAndBelongsToMany
                                        
elseif (array_key_exists($assocTable, $assoc_HABTM))
                                        
{
                                                
// fetches data from the conjunction table
                                                
$this->table = $assocSchema['conjTable'];
                                                
$conjData        = $this->find(NULL, $assocSchema['refFK'].' = '.$dataRecord->$assocSchema['PK']);
                                                
// fetches data from the remotely linked table
                                                
if (!empty($conjData))
                                                
{
                                                        
$this->table                         = $assocTable;
                                                        
foreach ($conjData as $conjRecord)
                                                        
{
                                                                
$conjRecordNew = $this->find(NULL, $assocSchema['refPKPK'].' = '.$conjRecord->$assocSchema['refPK']);
                                                                
$conjDataNew[$i][] = $conjRecordNew[0];
                                                        
}
                                                        
$dataRecord->$assocTable = $conjDataNew[$i];
                                                        
$this->table                         = $table;
                                                
}
                                        
}
                                        
                                        
$result_tableData[$i] = $dataRecord;
                                        
$i++;
                                
}
                                
                        
}
                
}
                
                
foreach ($result_tableData as $dataObj)
                
{
                        
//$this->save() = $dataObj->save();
                
}
                
                
                
if (isset($withAssoc))
                
{
                        
echo '<pre> <b>table data for '.$table.'</b>
';
                        
print_r($result_tableData);
                        
echo '
 
';
                        
echo '</pre>';
                
}
                
                
$this->table = $table;
                
                
return $result_tableData;
        
}
        
        
public function add()
        
{
                
$table = $this->table;
                
$tableMeta = $this->_tableMeta($table);
                
                
$yo = $this->db->list_fields($table);
                
echo '<pre>';
                
print_r($tableMeta);
                
echo '</pre>';
        
}
        
        
public function save()
        
{
                
        
}
        
        
public function &__get($prop)
        
{
                
$prop = (string) $prop;
                
                
$fields = $this->tableMeta['fields'];
                
                
if (array_key_exists($fields, $prop))
                
{
                        
echo 'yes';
                
}
                
else
                
{
                        
echo 'no';
                
}
        
}
        
        
public function __set($prop, $value)
        
{
                
$prop = (string) $prop;
                
        
}
        
        
public function __call($method, $args)
        
{
                
        
}
        
}
?>

Incoming search terms:

Tags: CodeIgniter, ORM

本文地址: http://www.21andy.com/new/20080601/1152.html