|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 /* 00003 00004 @version V5.06 29 Sept 2008 (c) 2000-2011 John Lim (jlim#natsoft.com). All rights reserved. 00005 Latest version is available at http://adodb.sourceforge.net 00006 00007 Released under both BSD license and Lesser GPL library license. 00008 Whenever there is any discrepancy between the two licenses, 00009 the BSD license will take precedence. 00010 00011 Active Record implementation. Superset of Zend Framework's. 00012 00013 This is "Active Record eXtended" to support JOIN, WORK and LAZY mode by Chris Ravenscroft chris#voilaweb.com 00014 00015 Version 0.9 00016 00017 See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 00018 for info on Ruby on Rails Active Record implementation 00019 */ 00020 00021 00022 // CFR: Active Records Definitions 00023 define('ADODB_JOIN_AR', 0x01); 00024 define('ADODB_WORK_AR', 0x02); 00025 define('ADODB_LAZY_AR', 0x03); 00026 00027 00028 global $_ADODB_ACTIVE_DBS; 00029 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info 00030 global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks 00031 global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record. 00032 00033 // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat 00034 $_ADODB_ACTIVE_DBS = array(); 00035 $ACTIVE_RECORD_SAFETY = true; // CFR: disabled while playing with relations 00036 $ADODB_ACTIVE_DEFVALS = false; 00037 00038 class ADODB_Active_DB { 00039 var $db; // ADOConnection 00040 var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename 00041 } 00042 00043 class ADODB_Active_Table { 00044 var $name; // table name 00045 var $flds; // assoc array of adofieldobjs, indexed by fieldname 00046 var $keys; // assoc array of primary keys, indexed by fieldname 00047 var $_created; // only used when stored as a cached file 00048 var $_belongsTo = array(); 00049 var $_hasMany = array(); 00050 var $_colsCount; // total columns count, including relations 00051 00052 function updateColsCount() 00053 { 00054 $this->_colsCount = sizeof($this->flds); 00055 foreach($this->_belongsTo as $foreignTable) 00056 $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); 00057 foreach($this->_hasMany as $foreignTable) 00058 $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); 00059 } 00060 } 00061 00062 // returns index into $_ADODB_ACTIVE_DBS 00063 function ADODB_SetDatabaseAdapter(&$db) 00064 { 00065 global $_ADODB_ACTIVE_DBS; 00066 00067 foreach($_ADODB_ACTIVE_DBS as $k => $d) { 00068 if (PHP_VERSION >= 5) { 00069 if ($d->db === $db) return $k; 00070 } else { 00071 if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) 00072 return $k; 00073 } 00074 } 00075 00076 $obj = new ADODB_Active_DB(); 00077 $obj->db = $db; 00078 $obj->tables = array(); 00079 00080 $_ADODB_ACTIVE_DBS[] = $obj; 00081 00082 return sizeof($_ADODB_ACTIVE_DBS)-1; 00083 } 00084 00085 00086 class ADODB_Active_Record { 00087 static $_changeNames = true; // dynamically pluralize table names 00088 static $_foreignSuffix = '_id'; // 00089 var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat] 00090 var $_table; // tablename, if set in class definition then use it as table name 00091 var $_sTable; // singularized table name 00092 var $_pTable; // pluralized table name 00093 var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat] 00094 var $_where; // where clause set in Load() 00095 var $_saved = false; // indicates whether data is already inserted. 00096 var $_lasterr = false; // last error message 00097 var $_original = false; // the original values loaded or inserted, refreshed on update 00098 00099 var $foreignName; // CFR: class name when in a relationship 00100 00101 static function UseDefaultValues($bool=null) 00102 { 00103 global $ADODB_ACTIVE_DEFVALS; 00104 if (isset($bool)) $ADODB_ACTIVE_DEFVALS = $bool; 00105 return $ADODB_ACTIVE_DEFVALS; 00106 } 00107 00108 // should be static 00109 static function SetDatabaseAdapter(&$db) 00110 { 00111 return ADODB_SetDatabaseAdapter($db); 00112 } 00113 00114 00115 public function __set($name, $value) 00116 { 00117 $name = str_replace(' ', '_', $name); 00118 $this->$name = $value; 00119 } 00120 00121 // php5 constructor 00122 // Note: if $table is defined, then we will use it as our table name 00123 // Otherwise we will use our classname... 00124 // In our database, table names are pluralized (because there can be 00125 // more than one row!) 00126 // Similarly, if $table is defined here, it has to be plural form. 00127 // 00128 // $options is an array that allows us to tweak the constructor's behaviour 00129 // if $options['refresh'] is true, we re-scan our metadata information 00130 // if $options['new'] is true, we forget all relations 00131 function __construct($table = false, $pkeyarr=false, $db=false, $options=array()) 00132 { 00133 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS; 00134 00135 if ($db == false && is_object($pkeyarr)) { 00136 $db = $pkeyarr; 00137 $pkeyarr = false; 00138 } 00139 00140 if($table) 00141 { 00142 // table argument exists. It is expected to be 00143 // already plural form. 00144 $this->_pTable = $table; 00145 $this->_sTable = $this->_singularize($this->_pTable); 00146 } 00147 else 00148 { 00149 // We will use current classname as table name. 00150 // We need to pluralize it for the real table name. 00151 $this->_sTable = strtolower(get_class($this)); 00152 $this->_pTable = $this->_pluralize($this->_sTable); 00153 } 00154 $this->_table = &$this->_pTable; 00155 00156 $this->foreignName = $this->_sTable; // CFR: default foreign name (singular) 00157 00158 if ($db) { 00159 $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db); 00160 } else 00161 $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1; 00162 00163 00164 if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor'); 00165 00166 $this->_tableat = $this->_table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future 00167 00168 // CFR: Just added this option because UpdateActiveTable() can refresh its information 00169 // but there was no way to ask it to do that. 00170 $forceUpdate = (isset($options['refresh']) && true === $options['refresh']); 00171 $this->UpdateActiveTable($pkeyarr, $forceUpdate); 00172 if(isset($options['new']) && true === $options['new']) 00173 { 00174 $table =& $this->TableInfo(); 00175 unset($table->_hasMany); 00176 unset($table->_belongsTo); 00177 $table->_hasMany = array(); 00178 $table->_belongsTo = array(); 00179 } 00180 } 00181 00182 function __wakeup() 00183 { 00184 $class = get_class($this); 00185 new $class; 00186 } 00187 00188 // CFR: Constants found in Rails 00189 static $IrregularP = array( 00190 'PERSON' => 'people', 00191 'MAN' => 'men', 00192 'WOMAN' => 'women', 00193 'CHILD' => 'children', 00194 'COW' => 'kine', 00195 ); 00196 00197 static $IrregularS = array( 00198 'PEOPLE' => 'PERSON', 00199 'MEN' => 'man', 00200 'WOMEN' => 'woman', 00201 'CHILDREN' => 'child', 00202 'KINE' => 'cow', 00203 ); 00204 00205 static $WeIsI = array( 00206 'EQUIPMENT' => true, 00207 'INFORMATION' => true, 00208 'RICE' => true, 00209 'MONEY' => true, 00210 'SPECIES' => true, 00211 'SERIES' => true, 00212 'FISH' => true, 00213 'SHEEP' => true, 00214 ); 00215 00216 function _pluralize($table) 00217 { 00218 if (!ADODB_Active_Record::$_changeNames) return $table; 00219 00220 $ut = strtoupper($table); 00221 if(isset(self::$WeIsI[$ut])) 00222 { 00223 return $table; 00224 } 00225 if(isset(self::$IrregularP[$ut])) 00226 { 00227 return self::$IrregularP[$ut]; 00228 } 00229 $len = strlen($table); 00230 $lastc = $ut[$len-1]; 00231 $lastc2 = substr($ut,$len-2); 00232 switch ($lastc) { 00233 case 'S': 00234 return $table.'es'; 00235 case 'Y': 00236 return substr($table,0,$len-1).'ies'; 00237 case 'X': 00238 return $table.'es'; 00239 case 'H': 00240 if ($lastc2 == 'CH' || $lastc2 == 'SH') 00241 return $table.'es'; 00242 default: 00243 return $table.'s'; 00244 } 00245 } 00246 00247 // CFR Lamest singular inflector ever - @todo Make it real! 00248 // Note: There is an assumption here...and it is that the argument's length >= 4 00249 function _singularize($table) 00250 { 00251 00252 if (!ADODB_Active_Record::$_changeNames) return $table; 00253 00254 $ut = strtoupper($table); 00255 if(isset(self::$WeIsI[$ut])) 00256 { 00257 return $table; 00258 } 00259 if(isset(self::$IrregularS[$ut])) 00260 { 00261 return self::$IrregularS[$ut]; 00262 } 00263 $len = strlen($table); 00264 if($ut[$len-1] != 'S') 00265 return $table; // I know...forget oxen 00266 if($ut[$len-2] != 'E') 00267 return substr($table, 0, $len-1); 00268 switch($ut[$len-3]) 00269 { 00270 case 'S': 00271 case 'X': 00272 return substr($table, 0, $len-2); 00273 case 'I': 00274 return substr($table, 0, $len-3) . 'y'; 00275 case 'H'; 00276 if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') 00277 return substr($table, 0, $len-2); 00278 default: 00279 return substr($table, 0, $len-1); // ? 00280 } 00281 } 00282 00283 /* 00284 * ar->foreignName will contain the name of the tables associated with this table because 00285 * these other tables' rows may also be referenced by this table using theirname_id or the provided 00286 * foreign keys (this index name is stored in ar->foreignKey) 00287 * 00288 * this-table.id = other-table-#1.this-table_id 00289 * = other-table-#2.this-table_id 00290 */ 00291 function hasMany($foreignRef,$foreignKey=false) 00292 { 00293 $ar = new ADODB_Active_Record($foreignRef); 00294 $ar->foreignName = $foreignRef; 00295 $ar->UpdateActiveTable(); 00296 $ar->foreignKey = ($foreignKey) ? $foreignKey : strtolower(get_class($this)) . self::$_foreignSuffix; 00297 00298 $table =& $this->TableInfo(); 00299 if(!isset($table->_hasMany[$foreignRef])) 00300 { 00301 $table->_hasMany[$foreignRef] = $ar; 00302 $table->updateColsCount(); 00303 } 00304 # @todo Can I make this guy be lazy? 00305 $this->$foreignRef = $table->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get() 00306 } 00307 00315 function belongsTo($foreignRef,$foreignKey=false) 00316 { 00317 global $inflector; 00318 00319 $ar = new ADODB_Active_Record($this->_pluralize($foreignRef)); 00320 $ar->foreignName = $foreignRef; 00321 $ar->UpdateActiveTable(); 00322 $ar->foreignKey = ($foreignKey) ? $foreignKey : $ar->foreignName . self::$_foreignSuffix; 00323 00324 $table =& $this->TableInfo(); 00325 if(!isset($table->_belongsTo[$foreignRef])) 00326 { 00327 $table->_belongsTo[$foreignRef] = $ar; 00328 $table->updateColsCount(); 00329 } 00330 $this->$foreignRef = $table->_belongsTo[$foreignRef]; 00331 } 00332 00340 function __get($name) 00341 { 00342 return $this->LoadRelations($name, '', -1. -1); 00343 } 00344 00345 function LoadRelations($name, $whereOrderBy, $offset=-1, $limit=-1) 00346 { 00347 $extras = array(); 00348 if($offset >= 0) $extras['offset'] = $offset; 00349 if($limit >= 0) $extras['limit'] = $limit; 00350 $table =& $this->TableInfo(); 00351 00352 if (strlen($whereOrderBy)) 00353 if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy)) 00354 if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy)) 00355 $whereOrderBy = 'AND '.$whereOrderBy; 00356 00357 if(!empty($table->_belongsTo[$name])) 00358 { 00359 $obj = $table->_belongsTo[$name]; 00360 $columnName = $obj->foreignKey; 00361 if(empty($this->$columnName)) 00362 $this->$name = null; 00363 else 00364 { 00365 if(($k = reset($obj->TableInfo()->keys))) 00366 $belongsToId = $k; 00367 else 00368 $belongsToId = 'id'; 00369 00370 $arrayOfOne = 00371 $obj->Find( 00372 $belongsToId.'='.$this->$columnName.' '.$whereOrderBy, false, false, $extras); 00373 $this->$name = $arrayOfOne[0]; 00374 } 00375 return $this->$name; 00376 } 00377 if(!empty($table->_hasMany[$name])) 00378 { 00379 $obj = $table->_hasMany[$name]; 00380 if(($k = reset($table->keys))) 00381 $hasManyId = $k; 00382 else 00383 $hasManyId = 'id'; 00384 00385 $this->$name = 00386 $obj->Find( 00387 $obj->foreignKey.'='.$this->$hasManyId.' '.$whereOrderBy, false, false, $extras); 00388 return $this->$name; 00389 } 00390 } 00392 00393 // update metadata 00394 function UpdateActiveTable($pkeys=false,$forceUpdate=false) 00395 { 00396 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; 00397 global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE; 00398 00399 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00400 00401 $table = $this->_table; 00402 $tables = $activedb->tables; 00403 $tableat = $this->_tableat; 00404 if (!$forceUpdate && !empty($tables[$tableat])) { 00405 00406 $tobj = $tables[$tableat]; 00407 foreach($tobj->flds as $name => $fld) { 00408 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) 00409 $this->$name = $fld->default_value; 00410 else 00411 $this->$name = null; 00412 } 00413 return; 00414 } 00415 00416 $db = $activedb->db; 00417 $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; 00418 if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { 00419 $fp = fopen($fname,'r'); 00420 @flock($fp, LOCK_SH); 00421 $acttab = unserialize(fread($fp,100000)); 00422 fclose($fp); 00423 if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { 00424 // abs(rand()) randomizes deletion, reducing contention to delete/refresh file 00425 // ideally, you should cache at least 32 secs 00426 $activedb->tables[$table] = $acttab; 00427 00428 //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); 00429 return; 00430 } else if ($db->debug) { 00431 ADOConnection::outp("Refreshing cached active record file: $fname"); 00432 } 00433 } 00434 $activetab = new ADODB_Active_Table(); 00435 $activetab->name = $table; 00436 00437 $save = $ADODB_FETCH_MODE; 00438 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 00439 if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false); 00440 00441 $cols = $db->MetaColumns($table); 00442 00443 if (isset($savem)) $db->SetFetchMode($savem); 00444 $ADODB_FETCH_MODE = $save; 00445 00446 if (!$cols) { 00447 $this->Error("Invalid table name: $table",'UpdateActiveTable'); 00448 return false; 00449 } 00450 $fld = reset($cols); 00451 if (!$pkeys) { 00452 if (isset($fld->primary_key)) { 00453 $pkeys = array(); 00454 foreach($cols as $name => $fld) { 00455 if (!empty($fld->primary_key)) $pkeys[] = $name; 00456 } 00457 } else 00458 $pkeys = $this->GetPrimaryKeys($db, $table); 00459 } 00460 if (empty($pkeys)) { 00461 $this->Error("No primary key found for table $table",'UpdateActiveTable'); 00462 return false; 00463 } 00464 00465 $attr = array(); 00466 $keys = array(); 00467 00468 switch($ADODB_ASSOC_CASE) { 00469 case 0: 00470 foreach($cols as $name => $fldobj) { 00471 $name = strtolower($name); 00472 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) 00473 $this->$name = $fldobj->default_value; 00474 else 00475 $this->$name = null; 00476 $attr[$name] = $fldobj; 00477 } 00478 foreach($pkeys as $k => $name) { 00479 $keys[strtolower($name)] = strtolower($name); 00480 } 00481 break; 00482 00483 case 1: 00484 foreach($cols as $name => $fldobj) { 00485 $name = strtoupper($name); 00486 00487 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) 00488 $this->$name = $fldobj->default_value; 00489 else 00490 $this->$name = null; 00491 $attr[$name] = $fldobj; 00492 } 00493 00494 foreach($pkeys as $k => $name) { 00495 $keys[strtoupper($name)] = strtoupper($name); 00496 } 00497 break; 00498 default: 00499 foreach($cols as $name => $fldobj) { 00500 $name = ($fldobj->name); 00501 00502 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) 00503 $this->$name = $fldobj->default_value; 00504 else 00505 $this->$name = null; 00506 $attr[$name] = $fldobj; 00507 } 00508 foreach($pkeys as $k => $name) { 00509 $keys[$name] = $cols[$name]->name; 00510 } 00511 break; 00512 } 00513 00514 $activetab->keys = $keys; 00515 $activetab->flds = $attr; 00516 $activetab->updateColsCount(); 00517 00518 if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) { 00519 $activetab->_created = time(); 00520 $s = serialize($activetab); 00521 if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php'); 00522 adodb_write_file($fname,$s); 00523 } 00524 if (isset($activedb->tables[$table])) { 00525 $oldtab = $activedb->tables[$table]; 00526 00527 if ($oldtab) $activetab->_belongsTo = $oldtab->_belongsTo; 00528 if ($oldtab) $activetab->_hasMany = $oldtab->_hasMany; 00529 } 00530 $activedb->tables[$table] = $activetab; 00531 } 00532 00533 function GetPrimaryKeys(&$db, $table) 00534 { 00535 return $db->MetaPrimaryKeys($table); 00536 } 00537 00538 // error handler for both PHP4+5. 00539 function Error($err,$fn) 00540 { 00541 global $_ADODB_ACTIVE_DBS; 00542 00543 $fn = get_class($this).'::'.$fn; 00544 $this->_lasterr = $fn.': '.$err; 00545 00546 if ($this->_dbat < 0) $db = false; 00547 else { 00548 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00549 $db = $activedb->db; 00550 } 00551 00552 if (function_exists('adodb_throw')) { 00553 if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); 00554 else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); 00555 } else 00556 if (!$db || $db->debug) ADOConnection::outp($this->_lasterr); 00557 00558 } 00559 00560 // return last error message 00561 function ErrorMsg() 00562 { 00563 if (!function_exists('adodb_throw')) { 00564 if ($this->_dbat < 0) $db = false; 00565 else $db = $this->DB(); 00566 00567 // last error could be database error too 00568 if ($db && $db->ErrorMsg()) return $db->ErrorMsg(); 00569 } 00570 return $this->_lasterr; 00571 } 00572 00573 function ErrorNo() 00574 { 00575 if ($this->_dbat < 0) return -9999; // no database connection... 00576 $db = $this->DB(); 00577 00578 return (int) $db->ErrorNo(); 00579 } 00580 00581 00582 // retrieve ADOConnection from _ADODB_Active_DBs 00583 function DB() 00584 { 00585 global $_ADODB_ACTIVE_DBS; 00586 00587 if ($this->_dbat < 0) { 00588 $false = false; 00589 $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB"); 00590 return $false; 00591 } 00592 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00593 $db = $activedb->db; 00594 return $db; 00595 } 00596 00597 // retrieve ADODB_Active_Table 00598 function &TableInfo() 00599 { 00600 global $_ADODB_ACTIVE_DBS; 00601 00602 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00603 $table = $activedb->tables[$this->_tableat]; 00604 return $table; 00605 } 00606 00607 00608 // I have an ON INSERT trigger on a table that sets other columns in the table. 00609 // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook 00610 function Reload() 00611 { 00612 $db =& $this->DB(); if (!$db) return false; 00613 $table =& $this->TableInfo(); 00614 $where = $this->GenWhere($db, $table); 00615 return($this->Load($where)); 00616 } 00617 00618 00619 // set a numeric array (using natural table field ordering) as object properties 00620 function Set(&$row) 00621 { 00622 global $ACTIVE_RECORD_SAFETY; 00623 00624 $db = $this->DB(); 00625 00626 if (!$row) { 00627 $this->_saved = false; 00628 return false; 00629 } 00630 00631 $this->_saved = true; 00632 00633 $table = $this->TableInfo(); 00634 $sizeofFlds = sizeof($table->flds); 00635 $sizeofRow = sizeof($row); 00636 if ($ACTIVE_RECORD_SAFETY && $table->_colsCount != $sizeofRow && $sizeofFlds != $sizeofRow) { 00637 # <AP> 00638 $bad_size = TRUE; 00639 if($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) { 00640 // Only keep string keys 00641 $keys = array_filter(array_keys($row), 'is_string'); 00642 if (sizeof($keys) == sizeof($table->flds)) 00643 $bad_size = FALSE; 00644 } 00645 if ($bad_size) { 00646 $this->Error("Table structure of $this->_table has changed","Load"); 00647 return false; 00648 } 00649 # </AP> 00650 } 00651 else 00652 $keys = array_keys($row); 00653 # <AP> 00654 reset($keys); 00655 $this->_original = array(); 00656 foreach($table->flds as $name=>$fld) 00657 { 00658 $value = $row[current($keys)]; 00659 $this->$name = $value; 00660 $this->_original[] = $value; 00661 if(!next($keys)) break; 00662 } 00663 $table =& $this->TableInfo(); 00664 foreach($table->_belongsTo as $foreignTable) 00665 { 00666 $ft = $foreignTable->TableInfo(); 00667 $propertyName = $ft->name; 00668 foreach($ft->flds as $name=>$fld) 00669 { 00670 $value = $row[current($keys)]; 00671 $foreignTable->$name = $value; 00672 $foreignTable->_original[] = $value; 00673 if(!next($keys)) break; 00674 } 00675 } 00676 foreach($table->_hasMany as $foreignTable) 00677 { 00678 $ft = $foreignTable->TableInfo(); 00679 foreach($ft->flds as $name=>$fld) 00680 { 00681 $value = $row[current($keys)]; 00682 $foreignTable->$name = $value; 00683 $foreignTable->_original[] = $value; 00684 if(!next($keys)) break; 00685 } 00686 } 00687 # </AP> 00688 return true; 00689 } 00690 00691 // get last inserted id for INSERT 00692 function LastInsertID(&$db,$fieldname) 00693 { 00694 if ($db->hasInsertID) 00695 $val = $db->Insert_ID($this->_table,$fieldname); 00696 else 00697 $val = false; 00698 00699 if (is_null($val) || $val === false) { 00700 // this might not work reliably in multi-user environment 00701 return $db->GetOne("select max(".$fieldname.") from ".$this->_table); 00702 } 00703 return $val; 00704 } 00705 00706 // quote data in where clause 00707 function doquote(&$db, $val,$t) 00708 { 00709 switch($t) { 00710 case 'D': 00711 case 'T': 00712 if (empty($val)) return 'null'; 00713 00714 case 'C': 00715 case 'X': 00716 if (is_null($val)) return 'null'; 00717 00718 if (strlen($val)>1 && 00719 (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")) { 00720 return $db->qstr($val); 00721 break; 00722 } 00723 default: 00724 return $val; 00725 break; 00726 } 00727 } 00728 00729 // generate where clause for an UPDATE/SELECT 00730 function GenWhere(&$db, &$table) 00731 { 00732 $keys = $table->keys; 00733 $parr = array(); 00734 00735 foreach($keys as $k) { 00736 $f = $table->flds[$k]; 00737 if ($f) { 00738 $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); 00739 } 00740 } 00741 return implode(' and ', $parr); 00742 } 00743 00744 00745 //------------------------------------------------------------ Public functions below 00746 00747 function Load($where=null,$bindarr=false) 00748 { 00749 $db = $this->DB(); if (!$db) return false; 00750 $this->_where = $where; 00751 00752 $save = $db->SetFetchMode(ADODB_FETCH_NUM); 00753 $qry = "select * from ".$this->_table; 00754 $table =& $this->TableInfo(); 00755 00756 if(($k = reset($table->keys))) 00757 $hasManyId = $k; 00758 else 00759 $hasManyId = 'id'; 00760 00761 foreach($table->_belongsTo as $foreignTable) 00762 { 00763 if(($k = reset($foreignTable->TableInfo()->keys))) 00764 { 00765 $belongsToId = $k; 00766 } 00767 else 00768 { 00769 $belongsToId = 'id'; 00770 } 00771 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 00772 $this->_table.'.'.$foreignTable->foreignKey.'='. 00773 $foreignTable->_table.'.'.$belongsToId; 00774 } 00775 foreach($table->_hasMany as $foreignTable) 00776 { 00777 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 00778 $this->_table.'.'.$hasManyId.'='. 00779 $foreignTable->_table.'.'.$foreignTable->foreignKey; 00780 } 00781 if($where) 00782 $qry .= ' WHERE '.$where; 00783 00784 // Simple case: no relations. Load row and return. 00785 if((count($table->_hasMany) + count($table->_belongsTo)) < 1) 00786 { 00787 $row = $db->GetRow($qry,$bindarr); 00788 if(!$row) 00789 return false; 00790 $db->SetFetchMode($save); 00791 return $this->Set($row); 00792 } 00793 00794 // More complex case when relations have to be collated 00795 $rows = $db->GetAll($qry,$bindarr); 00796 if(!$rows) 00797 return false; 00798 $db->SetFetchMode($save); 00799 if(count($rows) < 1) 00800 return false; 00801 $class = get_class($this); 00802 $isFirstRow = true; 00803 00804 if(($k = reset($this->TableInfo()->keys))) 00805 $myId = $k; 00806 else 00807 $myId = 'id'; 00808 $index = 0; $found = false; 00811 foreach($this->TableInfo()->flds as $fld) 00812 { 00813 if($fld->name == $myId) 00814 { 00815 $found = true; 00816 break; 00817 } 00818 $index++; 00819 } 00820 if(!$found) 00821 $this->outp_throw("Unable to locate key $myId for $class in Load()",'Load'); 00822 00823 foreach($rows as $row) 00824 { 00825 $rowId = intval($row[$index]); 00826 if($rowId > 0) 00827 { 00828 if($isFirstRow) 00829 { 00830 $isFirstRow = false; 00831 if(!$this->Set($row)) 00832 return false; 00833 } 00834 $obj = new $class($table,false,$db); 00835 $obj->Set($row); 00836 // TODO Copy/paste code below: bad! 00837 if(count($table->_hasMany) > 0) 00838 { 00839 foreach($table->_hasMany as $foreignTable) 00840 { 00841 $foreignName = $foreignTable->foreignName; 00842 if(!empty($obj->$foreignName)) 00843 { 00844 if(!is_array($this->$foreignName)) 00845 { 00846 $foreignObj = $this->$foreignName; 00847 $this->$foreignName = array(clone($foreignObj)); 00848 } 00849 else 00850 { 00851 $foreignObj = $obj->$foreignName; 00852 array_push($this->$foreignName, clone($foreignObj)); 00853 } 00854 } 00855 } 00856 } 00857 if(count($table->_belongsTo) > 0) 00858 { 00859 foreach($table->_belongsTo as $foreignTable) 00860 { 00861 $foreignName = $foreignTable->foreignName; 00862 if(!empty($obj->$foreignName)) 00863 { 00864 if(!is_array($this->$foreignName)) 00865 { 00866 $foreignObj = $this->$foreignName; 00867 $this->$foreignName = array(clone($foreignObj)); 00868 } 00869 else 00870 { 00871 $foreignObj = $obj->$foreignName; 00872 array_push($this->$foreignName, clone($foreignObj)); 00873 } 00874 } 00875 } 00876 } 00877 } 00878 } 00879 return true; 00880 } 00881 00882 // false on error 00883 function Save() 00884 { 00885 if ($this->_saved) $ok = $this->Update(); 00886 else $ok = $this->Insert(); 00887 00888 return $ok; 00889 } 00890 00891 // CFR: Sometimes we may wish to consider that an object is not to be replaced but inserted. 00892 // Sample use case: an 'undo' command object (after a delete()) 00893 function Dirty() 00894 { 00895 $this->_saved = false; 00896 } 00897 00898 // false on error 00899 function Insert() 00900 { 00901 $db = $this->DB(); if (!$db) return false; 00902 $cnt = 0; 00903 $table = $this->TableInfo(); 00904 00905 $valarr = array(); 00906 $names = array(); 00907 $valstr = array(); 00908 00909 foreach($table->flds as $name=>$fld) { 00910 $val = $this->$name; 00911 if(!is_null($val) || !array_key_exists($name, $table->keys)) { 00912 $valarr[] = $val; 00913 $names[] = $name; 00914 $valstr[] = $db->Param($cnt); 00915 $cnt += 1; 00916 } 00917 } 00918 00919 if (empty($names)){ 00920 foreach($table->flds as $name=>$fld) { 00921 $valarr[] = null; 00922 $names[] = $name; 00923 $valstr[] = $db->Param($cnt); 00924 $cnt += 1; 00925 } 00926 } 00927 $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; 00928 $ok = $db->Execute($sql,$valarr); 00929 00930 if ($ok) { 00931 $this->_saved = true; 00932 $autoinc = false; 00933 foreach($table->keys as $k) { 00934 if (is_null($this->$k)) { 00935 $autoinc = true; 00936 break; 00937 } 00938 } 00939 if ($autoinc && sizeof($table->keys) == 1) { 00940 $k = reset($table->keys); 00941 $this->$k = $this->LastInsertID($db,$k); 00942 } 00943 } 00944 00945 $this->_original = $valarr; 00946 return !empty($ok); 00947 } 00948 00949 function Delete() 00950 { 00951 $db = $this->DB(); if (!$db) return false; 00952 $table = $this->TableInfo(); 00953 00954 $where = $this->GenWhere($db,$table); 00955 $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; 00956 $ok = $db->Execute($sql); 00957 00958 return $ok ? true : false; 00959 } 00960 00961 // returns an array of active record objects 00962 function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) 00963 { 00964 $db = $this->DB(); if (!$db || empty($this->_table)) return false; 00965 $table =& $this->TableInfo(); 00966 $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, 00967 array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); 00968 return $arr; 00969 } 00970 00971 // CFR: In introduced this method to ensure that inner workings are not disturbed by 00972 // subclasses...for instance when GetActiveRecordsClass invokes Find() 00973 // Why am I not invoking parent::Find? 00974 // Shockingly because I want to preserve PHP4 compatibility. 00975 function packageFind($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) 00976 { 00977 $db = $this->DB(); if (!$db || empty($this->_table)) return false; 00978 $table =& $this->TableInfo(); 00979 $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, 00980 array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); 00981 return $arr; 00982 } 00983 00984 // returns 0 on error, 1 on update, 2 on insert 00985 function Replace() 00986 { 00987 global $ADODB_ASSOC_CASE; 00988 00989 $db = $this->DB(); if (!$db) return false; 00990 $table = $this->TableInfo(); 00991 00992 $pkey = $table->keys; 00993 00994 foreach($table->flds as $name=>$fld) { 00995 $val = $this->$name; 00996 /* 00997 if (is_null($val)) { 00998 if (isset($fld->not_null) && $fld->not_null) { 00999 if (isset($fld->default_value) && strlen($fld->default_value)) continue; 01000 else { 01001 $this->Error("Cannot update null into $name","Replace"); 01002 return false; 01003 } 01004 } 01005 }*/ 01006 if (is_null($val) && !empty($fld->auto_increment)) { 01007 continue; 01008 } 01009 $t = $db->MetaType($fld->type); 01010 $arr[$name] = $this->doquote($db,$val,$t); 01011 $valarr[] = $val; 01012 } 01013 01014 if (!is_array($pkey)) $pkey = array($pkey); 01015 01016 01017 if ($ADODB_ASSOC_CASE == 0) 01018 foreach($pkey as $k => $v) 01019 $pkey[$k] = strtolower($v); 01020 elseif ($ADODB_ASSOC_CASE == 1) 01021 foreach($pkey as $k => $v) 01022 $pkey[$k] = strtoupper($v); 01023 01024 $ok = $db->Replace($this->_table,$arr,$pkey); 01025 if ($ok) { 01026 $this->_saved = true; // 1= update 2=insert 01027 if ($ok == 2) { 01028 $autoinc = false; 01029 foreach($table->keys as $k) { 01030 if (is_null($this->$k)) { 01031 $autoinc = true; 01032 break; 01033 } 01034 } 01035 if ($autoinc && sizeof($table->keys) == 1) { 01036 $k = reset($table->keys); 01037 $this->$k = $this->LastInsertID($db,$k); 01038 } 01039 } 01040 01041 $this->_original = $valarr; 01042 } 01043 return $ok; 01044 } 01045 01046 // returns 0 on error, 1 on update, -1 if no change in data (no update) 01047 function Update() 01048 { 01049 $db = $this->DB(); if (!$db) return false; 01050 $table = $this->TableInfo(); 01051 01052 $where = $this->GenWhere($db, $table); 01053 01054 if (!$where) { 01055 $this->error("Where missing for table $table", "Update"); 01056 return false; 01057 } 01058 $valarr = array(); 01059 $neworig = array(); 01060 $pairs = array(); 01061 $i = -1; 01062 $cnt = 0; 01063 foreach($table->flds as $name=>$fld) { 01064 $i += 1; 01065 $val = $this->$name; 01066 $neworig[] = $val; 01067 01068 if (isset($table->keys[$name])) { 01069 continue; 01070 } 01071 01072 if (is_null($val)) { 01073 if (isset($fld->not_null) && $fld->not_null) { 01074 if (isset($fld->default_value) && strlen($fld->default_value)) continue; 01075 else { 01076 $this->Error("Cannot set field $name to NULL","Update"); 01077 return false; 01078 } 01079 } 01080 } 01081 01082 if (isset($this->_original[$i]) && $val == $this->_original[$i]) { 01083 continue; 01084 } 01085 $valarr[] = $val; 01086 $pairs[] = $name.'='.$db->Param($cnt); 01087 $cnt += 1; 01088 } 01089 01090 01091 if (!$cnt) return -1; 01092 $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; 01093 $ok = $db->Execute($sql,$valarr); 01094 if ($ok) { 01095 $this->_original = $neworig; 01096 return 1; 01097 } 01098 return 0; 01099 } 01100 01101 function GetAttributeNames() 01102 { 01103 $table = $this->TableInfo(); 01104 if (!$table) return false; 01105 return array_keys($table->flds); 01106 } 01107 01108 }; 01109 01110 function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr, 01111 $extra, $relations) 01112 { 01113 global $_ADODB_ACTIVE_DBS; 01114 01115 if (empty($extra['loading'])) $extra['loading'] = ADODB_LAZY_AR; 01116 01117 $save = $db->SetFetchMode(ADODB_FETCH_NUM); 01118 $table = &$tableObj->_table; 01119 $tableInfo =& $tableObj->TableInfo(); 01120 if(($k = reset($tableInfo->keys))) 01121 $myId = $k; 01122 else 01123 $myId = 'id'; 01124 $index = 0; $found = false; 01127 foreach($tableInfo->flds as $fld) 01128 { 01129 if($fld->name == $myId) 01130 { 01131 $found = true; 01132 break; 01133 } 01134 $index++; 01135 } 01136 if(!$found) 01137 $db->outp_throw("Unable to locate key $myId for $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); 01138 01139 $qry = "select * from ".$table; 01140 if(ADODB_JOIN_AR == $extra['loading']) 01141 { 01142 if(!empty($relations['belongsTo'])) 01143 { 01144 foreach($relations['belongsTo'] as $foreignTable) 01145 { 01146 if(($k = reset($foreignTable->TableInfo()->keys))) 01147 { 01148 $belongsToId = $k; 01149 } 01150 else 01151 { 01152 $belongsToId = 'id'; 01153 } 01154 01155 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 01156 $table.'.'.$foreignTable->foreignKey.'='. 01157 $foreignTable->_table.'.'.$belongsToId; 01158 } 01159 } 01160 if(!empty($relations['hasMany'])) 01161 { 01162 if(empty($relations['foreignName'])) 01163 $db->outp_throw("Missing foreignName is relation specification in GetActiveRecordsClass()",'GetActiveRecordsClass'); 01164 if(($k = reset($tableInfo->keys))) 01165 $hasManyId = $k; 01166 else 01167 $hasManyId = 'id'; 01168 01169 foreach($relations['hasMany'] as $foreignTable) 01170 { 01171 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 01172 $table.'.'.$hasManyId.'='. 01173 $foreignTable->_table.'.'.$foreignTable->foreignKey; 01174 } 01175 } 01176 } 01177 if (!empty($whereOrderBy)) 01178 $qry .= ' WHERE '.$whereOrderBy; 01179 if(isset($extra['limit'])) 01180 { 01181 $rows = false; 01182 if(isset($extra['offset'])) { 01183 $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']); 01184 } else { 01185 $rs = $db->SelectLimit($qry, $extra['limit']); 01186 } 01187 if ($rs) { 01188 while (!$rs->EOF) { 01189 $rows[] = $rs->fields; 01190 $rs->MoveNext(); 01191 } 01192 } 01193 } else 01194 $rows = $db->GetAll($qry,$bindarr); 01195 01196 $db->SetFetchMode($save); 01197 01198 $false = false; 01199 01200 if ($rows === false) { 01201 return $false; 01202 } 01203 01204 01205 if (!isset($_ADODB_ACTIVE_DBS)) { 01206 include(ADODB_DIR.'/adodb-active-record.inc.php'); 01207 } 01208 if (!class_exists($class)) { 01209 $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); 01210 return $false; 01211 } 01212 $uniqArr = array(); // CFR Keep track of records for relations 01213 $arr = array(); 01214 // arrRef will be the structure that knows about our objects. 01215 // It is an associative array. 01216 // We will, however, return arr, preserving regular 0.. order so that 01217 // obj[0] can be used by app developpers. 01218 $arrRef = array(); 01219 $bTos = array(); // Will store belongTo's indices if any 01220 foreach($rows as $row) { 01221 01222 $obj = new $class($table,$primkeyArr,$db); 01223 if ($obj->ErrorNo()){ 01224 $db->_errorMsg = $obj->ErrorMsg(); 01225 return $false; 01226 } 01227 $obj->Set($row); 01228 // CFR: FIXME: Insane assumption here: 01229 // If the first column returned is an integer, then it's a 'id' field 01230 // And to make things a bit worse, I use intval() rather than is_int() because, in fact, 01231 // $row[0] is not an integer. 01232 // 01233 // So, what does this whole block do? 01234 // When relationships are found, we perform JOINs. This is fast. But not accurate: 01235 // instead of returning n objects with their n' associated cousins, 01236 // we get n*n' objects. This code fixes this. 01237 // Note: to-many relationships mess around with the 'limit' parameter 01238 $rowId = intval($row[$index]); 01239 01240 if(ADODB_WORK_AR == $extra['loading']) 01241 { 01242 $arrRef[$rowId] = $obj; 01243 $arr[] = &$arrRef[$rowId]; 01244 if(!isset($indices)) 01245 $indices = $rowId; 01246 else 01247 $indices .= ','.$rowId; 01248 if(!empty($relations['belongsTo'])) 01249 { 01250 foreach($relations['belongsTo'] as $foreignTable) 01251 { 01252 $foreignTableRef = $foreignTable->foreignKey; 01253 // First array: list of foreign ids we are looking for 01254 if(empty($bTos[$foreignTableRef])) 01255 $bTos[$foreignTableRef] = array(); 01256 // Second array: list of ids found 01257 if(empty($obj->$foreignTableRef)) 01258 continue; 01259 if(empty($bTos[$foreignTableRef][$obj->$foreignTableRef])) 01260 $bTos[$foreignTableRef][$obj->$foreignTableRef] = array(); 01261 $bTos[$foreignTableRef][$obj->$foreignTableRef][] = $obj; 01262 } 01263 } 01264 continue; 01265 } 01266 01267 if($rowId>0) 01268 { 01269 if(ADODB_JOIN_AR == $extra['loading']) 01270 { 01271 $isNewObj = !isset($uniqArr['_'.$row[0]]); 01272 if($isNewObj) 01273 $uniqArr['_'.$row[0]] = $obj; 01274 01275 // TODO Copy/paste code below: bad! 01276 if(!empty($relations['hasMany'])) 01277 { 01278 foreach($relations['hasMany'] as $foreignTable) 01279 { 01280 $foreignName = $foreignTable->foreignName; 01281 if(!empty($obj->$foreignName)) 01282 { 01283 $masterObj = &$uniqArr['_'.$row[0]]; 01284 // Assumption: this property exists in every object since they are instances of the same class 01285 if(!is_array($masterObj->$foreignName)) 01286 { 01287 // Pluck! 01288 $foreignObj = $masterObj->$foreignName; 01289 $masterObj->$foreignName = array(clone($foreignObj)); 01290 } 01291 else 01292 { 01293 // Pluck pluck! 01294 $foreignObj = $obj->$foreignName; 01295 array_push($masterObj->$foreignName, clone($foreignObj)); 01296 } 01297 } 01298 } 01299 } 01300 if(!empty($relations['belongsTo'])) 01301 { 01302 foreach($relations['belongsTo'] as $foreignTable) 01303 { 01304 $foreignName = $foreignTable->foreignName; 01305 if(!empty($obj->$foreignName)) 01306 { 01307 $masterObj = &$uniqArr['_'.$row[0]]; 01308 // Assumption: this property exists in every object since they are instances of the same class 01309 if(!is_array($masterObj->$foreignName)) 01310 { 01311 // Pluck! 01312 $foreignObj = $masterObj->$foreignName; 01313 $masterObj->$foreignName = array(clone($foreignObj)); 01314 } 01315 else 01316 { 01317 // Pluck pluck! 01318 $foreignObj = $obj->$foreignName; 01319 array_push($masterObj->$foreignName, clone($foreignObj)); 01320 } 01321 } 01322 } 01323 } 01324 if(!$isNewObj) 01325 unset($obj); // We do not need this object itself anymore and do not want it re-added to the main array 01326 } 01327 else if(ADODB_LAZY_AR == $extra['loading']) 01328 { 01329 // Lazy loading: we need to give AdoDb a hint that we have not really loaded 01330 // anything, all the while keeping enough information on what we wish to load. 01331 // Let's do this by keeping the relevant info in our relationship arrays 01332 // but get rid of the actual properties. 01333 // We will then use PHP's __get to load these properties on-demand. 01334 if(!empty($relations['hasMany'])) 01335 { 01336 foreach($relations['hasMany'] as $foreignTable) 01337 { 01338 $foreignName = $foreignTable->foreignName; 01339 if(!empty($obj->$foreignName)) 01340 { 01341 unset($obj->$foreignName); 01342 } 01343 } 01344 } 01345 if(!empty($relations['belongsTo'])) 01346 { 01347 foreach($relations['belongsTo'] as $foreignTable) 01348 { 01349 $foreignName = $foreignTable->foreignName; 01350 if(!empty($obj->$foreignName)) 01351 { 01352 unset($obj->$foreignName); 01353 } 01354 } 01355 } 01356 } 01357 } 01358 01359 if(isset($obj)) 01360 $arr[] = $obj; 01361 } 01362 01363 if(ADODB_WORK_AR == $extra['loading']) 01364 { 01365 // The best of both worlds? 01366 // Here, the number of queries is constant: 1 + n*relationship. 01367 // The second query will allow us to perform a good join 01368 // while preserving LIMIT etc. 01369 if(!empty($relations['hasMany'])) 01370 { 01371 foreach($relations['hasMany'] as $foreignTable) 01372 { 01373 $foreignName = $foreignTable->foreignName; 01374 $className = ucfirst($foreignTable->_singularize($foreignName)); 01375 $obj = new $className(); 01376 $dbClassRef = $foreignTable->foreignKey; 01377 $objs = $obj->packageFind($dbClassRef.' IN ('.$indices.')'); 01378 foreach($objs as $obj) 01379 { 01380 if(!is_array($arrRef[$obj->$dbClassRef]->$foreignName)) 01381 $arrRef[$obj->$dbClassRef]->$foreignName = array(); 01382 array_push($arrRef[$obj->$dbClassRef]->$foreignName, $obj); 01383 } 01384 } 01385 01386 } 01387 if(!empty($relations['belongsTo'])) 01388 { 01389 foreach($relations['belongsTo'] as $foreignTable) 01390 { 01391 $foreignTableRef = $foreignTable->foreignKey; 01392 if(empty($bTos[$foreignTableRef])) 01393 continue; 01394 if(($k = reset($foreignTable->TableInfo()->keys))) 01395 { 01396 $belongsToId = $k; 01397 } 01398 else 01399 { 01400 $belongsToId = 'id'; 01401 } 01402 $origObjsArr = $bTos[$foreignTableRef]; 01403 $bTosString = implode(',', array_keys($bTos[$foreignTableRef])); 01404 $foreignName = $foreignTable->foreignName; 01405 $className = ucfirst($foreignTable->_singularize($foreignName)); 01406 $obj = new $className(); 01407 $objs = $obj->packageFind($belongsToId.' IN ('.$bTosString.')'); 01408 foreach($objs as $obj) 01409 { 01410 foreach($origObjsArr[$obj->$belongsToId] as $idx=>$origObj) 01411 { 01412 $origObj->$foreignName = $obj; 01413 } 01414 } 01415 } 01416 } 01417 } 01418 01419 return $arr; 01420 } 01421 ?>