[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 4 @version v5.20.3 01-Jan-2016 5 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved. 6 @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community 7 Latest version is available at http://adodb.sourceforge.net 8 9 Released under both BSD license and Lesser GPL library license. 10 Whenever there is any discrepancy between the two licenses, 11 the BSD license will take precedence. 12 13 Active Record implementation. Superset of Zend Framework's. 14 15 This is "Active Record eXtended" to support JOIN, WORK and LAZY mode by Chris Ravenscroft chris#voilaweb.com 16 17 Version 0.9 18 19 See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 20 for info on Ruby on Rails Active Record implementation 21 */ 22 23 24 // CFR: Active Records Definitions 25 define('ADODB_JOIN_AR', 0x01); 26 define('ADODB_WORK_AR', 0x02); 27 define('ADODB_LAZY_AR', 0x03); 28 29 30 global $_ADODB_ACTIVE_DBS; 31 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info 32 global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks 33 global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record. 34 35 // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat 36 $_ADODB_ACTIVE_DBS = array(); 37 $ACTIVE_RECORD_SAFETY = true; // CFR: disabled while playing with relations 38 $ADODB_ACTIVE_DEFVALS = false; 39 40 class ADODB_Active_DB { 41 var $db; // ADOConnection 42 var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename 43 } 44 45 class ADODB_Active_Table { 46 var $name; // table name 47 var $flds; // assoc array of adofieldobjs, indexed by fieldname 48 var $keys; // assoc array of primary keys, indexed by fieldname 49 var $_created; // only used when stored as a cached file 50 var $_belongsTo = array(); 51 var $_hasMany = array(); 52 var $_colsCount; // total columns count, including relations 53 54 function updateColsCount() 55 { 56 $this->_colsCount = sizeof($this->flds); 57 foreach($this->_belongsTo as $foreignTable) 58 $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); 59 foreach($this->_hasMany as $foreignTable) 60 $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); 61 } 62 } 63 64 // returns index into $_ADODB_ACTIVE_DBS 65 function ADODB_SetDatabaseAdapter(&$db) 66 { 67 global $_ADODB_ACTIVE_DBS; 68 69 foreach($_ADODB_ACTIVE_DBS as $k => $d) { 70 if (PHP_VERSION >= 5) { 71 if ($d->db === $db) { 72 return $k; 73 } 74 } else { 75 if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) { 76 return $k; 77 } 78 } 79 } 80 81 $obj = new ADODB_Active_DB(); 82 $obj->db = $db; 83 $obj->tables = array(); 84 85 $_ADODB_ACTIVE_DBS[] = $obj; 86 87 return sizeof($_ADODB_ACTIVE_DBS)-1; 88 } 89 90 91 class ADODB_Active_Record { 92 static $_changeNames = true; // dynamically pluralize table names 93 static $_foreignSuffix = '_id'; // 94 var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat] 95 var $_table; // tablename, if set in class definition then use it as table name 96 var $_sTable; // singularized table name 97 var $_pTable; // pluralized table name 98 var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat] 99 var $_where; // where clause set in Load() 100 var $_saved = false; // indicates whether data is already inserted. 101 var $_lasterr = false; // last error message 102 var $_original = false; // the original values loaded or inserted, refreshed on update 103 104 var $foreignName; // CFR: class name when in a relationship 105 106 static function UseDefaultValues($bool=null) 107 { 108 global $ADODB_ACTIVE_DEFVALS; 109 if (isset($bool)) { 110 $ADODB_ACTIVE_DEFVALS = $bool; 111 } 112 return $ADODB_ACTIVE_DEFVALS; 113 } 114 115 // should be static 116 static function SetDatabaseAdapter(&$db) 117 { 118 return ADODB_SetDatabaseAdapter($db); 119 } 120 121 122 public function __set($name, $value) 123 { 124 $name = str_replace(' ', '_', $name); 125 $this->$name = $value; 126 } 127 128 // php5 constructor 129 // Note: if $table is defined, then we will use it as our table name 130 // Otherwise we will use our classname... 131 // In our database, table names are pluralized (because there can be 132 // more than one row!) 133 // Similarly, if $table is defined here, it has to be plural form. 134 // 135 // $options is an array that allows us to tweak the constructor's behaviour 136 // if $options['refresh'] is true, we re-scan our metadata information 137 // if $options['new'] is true, we forget all relations 138 function __construct($table = false, $pkeyarr=false, $db=false, $options=array()) 139 { 140 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS; 141 142 if ($db == false && is_object($pkeyarr)) { 143 $db = $pkeyarr; 144 $pkeyarr = false; 145 } 146 147 if($table) { 148 // table argument exists. It is expected to be 149 // already plural form. 150 $this->_pTable = $table; 151 $this->_sTable = $this->_singularize($this->_pTable); 152 } 153 else { 154 // We will use current classname as table name. 155 // We need to pluralize it for the real table name. 156 $this->_sTable = strtolower(get_class($this)); 157 $this->_pTable = $this->_pluralize($this->_sTable); 158 } 159 $this->_table = &$this->_pTable; 160 161 $this->foreignName = $this->_sTable; // CFR: default foreign name (singular) 162 163 if ($db) { 164 $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db); 165 } else 166 $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1; 167 168 169 if ($this->_dbat < 0) { 170 $this->Error( 171 "No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)", 172 'ADODB_Active_Record::__constructor' 173 ); 174 } 175 176 $this->_tableat = $this->_table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future 177 178 // CFR: Just added this option because UpdateActiveTable() can refresh its information 179 // but there was no way to ask it to do that. 180 $forceUpdate = (isset($options['refresh']) && true === $options['refresh']); 181 $this->UpdateActiveTable($pkeyarr, $forceUpdate); 182 if(isset($options['new']) && true === $options['new']) { 183 $table =& $this->TableInfo(); 184 unset($table->_hasMany); 185 unset($table->_belongsTo); 186 $table->_hasMany = array(); 187 $table->_belongsTo = array(); 188 } 189 } 190 191 function __wakeup() 192 { 193 $class = get_class($this); 194 new $class; 195 } 196 197 // CFR: Constants found in Rails 198 static $IrregularP = array( 199 'PERSON' => 'people', 200 'MAN' => 'men', 201 'WOMAN' => 'women', 202 'CHILD' => 'children', 203 'COW' => 'kine', 204 ); 205 206 static $IrregularS = array( 207 'PEOPLE' => 'PERSON', 208 'MEN' => 'man', 209 'WOMEN' => 'woman', 210 'CHILDREN' => 'child', 211 'KINE' => 'cow', 212 ); 213 214 static $WeIsI = array( 215 'EQUIPMENT' => true, 216 'INFORMATION' => true, 217 'RICE' => true, 218 'MONEY' => true, 219 'SPECIES' => true, 220 'SERIES' => true, 221 'FISH' => true, 222 'SHEEP' => true, 223 ); 224 225 function _pluralize($table) 226 { 227 if (!ADODB_Active_Record::$_changeNames) { 228 return $table; 229 } 230 $ut = strtoupper($table); 231 if(isset(self::$WeIsI[$ut])) { 232 return $table; 233 } 234 if(isset(self::$IrregularP[$ut])) { 235 return self::$IrregularP[$ut]; 236 } 237 $len = strlen($table); 238 $lastc = $ut[$len-1]; 239 $lastc2 = substr($ut,$len-2); 240 switch ($lastc) { 241 case 'S': 242 return $table.'es'; 243 case 'Y': 244 return substr($table,0,$len-1).'ies'; 245 case 'X': 246 return $table.'es'; 247 case 'H': 248 if ($lastc2 == 'CH' || $lastc2 == 'SH') { 249 return $table.'es'; 250 } 251 default: 252 return $table.'s'; 253 } 254 } 255 256 // CFR Lamest singular inflector ever - @todo Make it real! 257 // Note: There is an assumption here...and it is that the argument's length >= 4 258 function _singularize($table) 259 { 260 261 if (!ADODB_Active_Record::$_changeNames) { 262 return $table; 263 } 264 $ut = strtoupper($table); 265 if(isset(self::$WeIsI[$ut])) { 266 return $table; 267 } 268 if(isset(self::$IrregularS[$ut])) { 269 return self::$IrregularS[$ut]; 270 } 271 $len = strlen($table); 272 if($ut[$len-1] != 'S') { 273 return $table; // I know...forget oxen 274 } 275 if($ut[$len-2] != 'E') { 276 return substr($table, 0, $len-1); 277 } 278 switch($ut[$len-3]) { 279 case 'S': 280 case 'X': 281 return substr($table, 0, $len-2); 282 case 'I': 283 return substr($table, 0, $len-3) . 'y'; 284 case 'H'; 285 if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') { 286 return substr($table, 0, $len-2); 287 } 288 default: 289 return substr($table, 0, $len-1); // ? 290 } 291 } 292 293 /* 294 * ar->foreignName will contain the name of the tables associated with this table because 295 * these other tables' rows may also be referenced by this table using theirname_id or the provided 296 * foreign keys (this index name is stored in ar->foreignKey) 297 * 298 * this-table.id = other-table-#1.this-table_id 299 * = other-table-#2.this-table_id 300 */ 301 function hasMany($foreignRef,$foreignKey=false) 302 { 303 $ar = new ADODB_Active_Record($foreignRef); 304 $ar->foreignName = $foreignRef; 305 $ar->UpdateActiveTable(); 306 $ar->foreignKey = ($foreignKey) ? $foreignKey : strtolower(get_class($this)) . self::$_foreignSuffix; 307 308 $table =& $this->TableInfo(); 309 if(!isset($table->_hasMany[$foreignRef])) { 310 $table->_hasMany[$foreignRef] = $ar; 311 $table->updateColsCount(); 312 } 313 # @todo Can I make this guy be lazy? 314 $this->$foreignRef = $table->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get() 315 } 316 317 /** 318 * ar->foreignName will contain the name of the tables associated with this table because 319 * this table's rows may also be referenced by those tables using thistable_id or the provided 320 * foreign keys (this index name is stored in ar->foreignKey) 321 * 322 * this-table.other-table_id = other-table.id 323 */ 324 function belongsTo($foreignRef,$foreignKey=false) 325 { 326 global $inflector; 327 328 $ar = new ADODB_Active_Record($this->_pluralize($foreignRef)); 329 $ar->foreignName = $foreignRef; 330 $ar->UpdateActiveTable(); 331 $ar->foreignKey = ($foreignKey) ? $foreignKey : $ar->foreignName . self::$_foreignSuffix; 332 333 $table =& $this->TableInfo(); 334 if(!isset($table->_belongsTo[$foreignRef])) { 335 $table->_belongsTo[$foreignRef] = $ar; 336 $table->updateColsCount(); 337 } 338 $this->$foreignRef = $table->_belongsTo[$foreignRef]; 339 } 340 341 /** 342 * __get Access properties - used for lazy loading 343 * 344 * @param mixed $name 345 * @access protected 346 * @return void 347 */ 348 function __get($name) 349 { 350 return $this->LoadRelations($name, '', -1. -1); 351 } 352 353 function LoadRelations($name, $whereOrderBy, $offset=-1, $limit=-1) 354 { 355 $extras = array(); 356 if($offset >= 0) { 357 $extras['offset'] = $offset; 358 } 359 if($limit >= 0) { 360 $extras['limit'] = $limit; 361 } 362 $table =& $this->TableInfo(); 363 364 if (strlen($whereOrderBy)) { 365 if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy)) { 366 if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy)) { 367 $whereOrderBy = 'AND '.$whereOrderBy; 368 } 369 } 370 } 371 372 if(!empty($table->_belongsTo[$name])) { 373 $obj = $table->_belongsTo[$name]; 374 $columnName = $obj->foreignKey; 375 if(empty($this->$columnName)) { 376 $this->$name = null; 377 } 378 else { 379 if(($k = reset($obj->TableInfo()->keys))) { 380 $belongsToId = $k; 381 } 382 else { 383 $belongsToId = 'id'; 384 } 385 386 $arrayOfOne = 387 $obj->Find( 388 $belongsToId.'='.$this->$columnName.' '.$whereOrderBy, false, false, $extras); 389 $this->$name = $arrayOfOne[0]; 390 } 391 return $this->$name; 392 } 393 if(!empty($table->_hasMany[$name])) { 394 $obj = $table->_hasMany[$name]; 395 if(($k = reset($table->keys))) { 396 $hasManyId = $k; 397 } 398 else { 399 $hasManyId = 'id'; 400 } 401 402 $this->$name = 403 $obj->Find( 404 $obj->foreignKey.'='.$this->$hasManyId.' '.$whereOrderBy, false, false, $extras); 405 return $this->$name; 406 } 407 } 408 ////////////////////////////////// 409 410 // update metadata 411 function UpdateActiveTable($pkeys=false,$forceUpdate=false) 412 { 413 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; 414 global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE; 415 416 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 417 418 $table = $this->_table; 419 $tables = $activedb->tables; 420 $tableat = $this->_tableat; 421 if (!$forceUpdate && !empty($tables[$tableat])) { 422 423 $tobj = $tables[$tableat]; 424 foreach($tobj->flds as $name => $fld) { 425 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) { 426 $this->$name = $fld->default_value; 427 } 428 else { 429 $this->$name = null; 430 } 431 } 432 return; 433 } 434 435 $db = $activedb->db; 436 $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; 437 if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { 438 $fp = fopen($fname,'r'); 439 @flock($fp, LOCK_SH); 440 $acttab = unserialize(fread($fp,100000)); 441 fclose($fp); 442 if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { 443 // abs(rand()) randomizes deletion, reducing contention to delete/refresh file 444 // ideally, you should cache at least 32 secs 445 $activedb->tables[$table] = $acttab; 446 447 //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); 448 return; 449 } else if ($db->debug) { 450 ADOConnection::outp("Refreshing cached active record file: $fname"); 451 } 452 } 453 $activetab = new ADODB_Active_Table(); 454 $activetab->name = $table; 455 456 $save = $ADODB_FETCH_MODE; 457 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 458 if ($db->fetchMode !== false) { 459 $savem = $db->SetFetchMode(false); 460 } 461 462 $cols = $db->MetaColumns($table); 463 464 if (isset($savem)) { 465 $db->SetFetchMode($savem); 466 } 467 $ADODB_FETCH_MODE = $save; 468 469 if (!$cols) { 470 $this->Error("Invalid table name: $table",'UpdateActiveTable'); 471 return false; 472 } 473 $fld = reset($cols); 474 if (!$pkeys) { 475 if (isset($fld->primary_key)) { 476 $pkeys = array(); 477 foreach($cols as $name => $fld) { 478 if (!empty($fld->primary_key)) { 479 $pkeys[] = $name; 480 } 481 } 482 } else { 483 $pkeys = $this->GetPrimaryKeys($db, $table); 484 } 485 } 486 if (empty($pkeys)) { 487 $this->Error("No primary key found for table $table",'UpdateActiveTable'); 488 return false; 489 } 490 491 $attr = array(); 492 $keys = array(); 493 494 switch($ADODB_ASSOC_CASE) { 495 case 0: 496 foreach($cols as $name => $fldobj) { 497 $name = strtolower($name); 498 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { 499 $this->$name = $fldobj->default_value; 500 } 501 else { 502 $this->$name = null; 503 } 504 $attr[$name] = $fldobj; 505 } 506 foreach($pkeys as $k => $name) { 507 $keys[strtolower($name)] = strtolower($name); 508 } 509 break; 510 511 case 1: 512 foreach($cols as $name => $fldobj) { 513 $name = strtoupper($name); 514 515 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { 516 $this->$name = $fldobj->default_value; 517 } 518 else { 519 $this->$name = null; 520 } 521 $attr[$name] = $fldobj; 522 } 523 524 foreach($pkeys as $k => $name) { 525 $keys[strtoupper($name)] = strtoupper($name); 526 } 527 break; 528 default: 529 foreach($cols as $name => $fldobj) { 530 $name = ($fldobj->name); 531 532 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { 533 $this->$name = $fldobj->default_value; 534 } 535 else { 536 $this->$name = null; 537 } 538 $attr[$name] = $fldobj; 539 } 540 foreach($pkeys as $k => $name) { 541 $keys[$name] = $cols[$name]->name; 542 } 543 break; 544 } 545 546 $activetab->keys = $keys; 547 $activetab->flds = $attr; 548 $activetab->updateColsCount(); 549 550 if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) { 551 $activetab->_created = time(); 552 $s = serialize($activetab); 553 if (!function_exists('adodb_write_file')) { 554 include (ADODB_DIR.'/adodb-csvlib.inc.php'); 555 } 556 adodb_write_file($fname,$s); 557 } 558 if (isset($activedb->tables[$table])) { 559 $oldtab = $activedb->tables[$table]; 560 561 if ($oldtab) { 562 $activetab->_belongsTo = $oldtab->_belongsTo; 563 $activetab->_hasMany = $oldtab->_hasMany; 564 } 565 } 566 $activedb->tables[$table] = $activetab; 567 } 568 569 function GetPrimaryKeys(&$db, $table) 570 { 571 return $db->MetaPrimaryKeys($table); 572 } 573 574 // error handler for both PHP4+5. 575 function Error($err,$fn) 576 { 577 global $_ADODB_ACTIVE_DBS; 578 579 $fn = get_class($this).'::'.$fn; 580 $this->_lasterr = $fn.': '.$err; 581 582 if ($this->_dbat < 0) { 583 $db = false; 584 } 585 else { 586 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 587 $db = $activedb->db; 588 } 589 590 if (function_exists('adodb_throw')) { 591 if (!$db) { 592 adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); 593 } 594 else { 595 adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); 596 } 597 } else { 598 if (!$db || $db->debug) { 599 ADOConnection::outp($this->_lasterr); 600 } 601 } 602 603 } 604 605 // return last error message 606 function ErrorMsg() 607 { 608 if (!function_exists('adodb_throw')) { 609 if ($this->_dbat < 0) { 610 $db = false; 611 } 612 else { 613 $db = $this->DB(); 614 } 615 616 // last error could be database error too 617 if ($db && $db->ErrorMsg()) { 618 return $db->ErrorMsg(); 619 } 620 } 621 return $this->_lasterr; 622 } 623 624 function ErrorNo() 625 { 626 if ($this->_dbat < 0) { 627 return -9999; // no database connection... 628 } 629 $db = $this->DB(); 630 631 return (int) $db->ErrorNo(); 632 } 633 634 635 // retrieve ADOConnection from _ADODB_Active_DBs 636 function DB() 637 { 638 global $_ADODB_ACTIVE_DBS; 639 640 if ($this->_dbat < 0) { 641 $false = false; 642 $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB"); 643 return $false; 644 } 645 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 646 $db = $activedb->db; 647 return $db; 648 } 649 650 // retrieve ADODB_Active_Table 651 function &TableInfo() 652 { 653 global $_ADODB_ACTIVE_DBS; 654 655 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 656 $table = $activedb->tables[$this->_tableat]; 657 return $table; 658 } 659 660 661 // I have an ON INSERT trigger on a table that sets other columns in the table. 662 // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook 663 function Reload() 664 { 665 $db =& $this->DB(); 666 if (!$db) { 667 return false; 668 } 669 $table =& $this->TableInfo(); 670 $where = $this->GenWhere($db, $table); 671 return($this->Load($where)); 672 } 673 674 675 // set a numeric array (using natural table field ordering) as object properties 676 function Set(&$row) 677 { 678 global $ACTIVE_RECORD_SAFETY; 679 680 $db = $this->DB(); 681 682 if (!$row) { 683 $this->_saved = false; 684 return false; 685 } 686 687 $this->_saved = true; 688 689 $table = $this->TableInfo(); 690 $sizeofFlds = sizeof($table->flds); 691 $sizeofRow = sizeof($row); 692 if ($ACTIVE_RECORD_SAFETY && $table->_colsCount != $sizeofRow && $sizeofFlds != $sizeofRow) { 693 # <AP> 694 $bad_size = TRUE; 695 if($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) { 696 // Only keep string keys 697 $keys = array_filter(array_keys($row), 'is_string'); 698 if (sizeof($keys) == sizeof($table->flds)) { 699 $bad_size = FALSE; 700 } 701 } 702 if ($bad_size) { 703 $this->Error("Table structure of $this->_table has changed","Load"); 704 return false; 705 } 706 # </AP> 707 } 708 else { 709 $keys = array_keys($row); 710 } 711 712 # <AP> 713 reset($keys); 714 $this->_original = array(); 715 foreach($table->flds as $name=>$fld) { 716 $value = $row[current($keys)]; 717 $this->$name = $value; 718 $this->_original[] = $value; 719 if(!next($keys)) { 720 break; 721 } 722 } 723 $table =& $this->TableInfo(); 724 foreach($table->_belongsTo as $foreignTable) { 725 $ft = $foreignTable->TableInfo(); 726 $propertyName = $ft->name; 727 foreach($ft->flds as $name=>$fld) { 728 $value = $row[current($keys)]; 729 $foreignTable->$name = $value; 730 $foreignTable->_original[] = $value; 731 if(!next($keys)) { 732 break; 733 } 734 } 735 } 736 foreach($table->_hasMany as $foreignTable) { 737 $ft = $foreignTable->TableInfo(); 738 foreach($ft->flds as $name=>$fld) { 739 $value = $row[current($keys)]; 740 $foreignTable->$name = $value; 741 $foreignTable->_original[] = $value; 742 if(!next($keys)) { 743 break; 744 } 745 } 746 } 747 # </AP> 748 749 return true; 750 } 751 752 // get last inserted id for INSERT 753 function LastInsertID(&$db,$fieldname) 754 { 755 if ($db->hasInsertID) { 756 $val = $db->Insert_ID($this->_table,$fieldname); 757 } 758 else { 759 $val = false; 760 } 761 762 if (is_null($val) || $val === false) { 763 // this might not work reliably in multi-user environment 764 return $db->GetOne("select max(".$fieldname.") from ".$this->_table); 765 } 766 return $val; 767 } 768 769 // quote data in where clause 770 function doquote(&$db, $val,$t) 771 { 772 switch($t) { 773 case 'D': 774 case 'T': 775 if (empty($val)) { 776 return 'null'; 777 } 778 case 'C': 779 case 'X': 780 if (is_null($val)) { 781 return 'null'; 782 } 783 if (strlen($val)>0 && 784 (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'") 785 ) { 786 return $db->qstr($val); 787 break; 788 } 789 default: 790 return $val; 791 break; 792 } 793 } 794 795 // generate where clause for an UPDATE/SELECT 796 function GenWhere(&$db, &$table) 797 { 798 $keys = $table->keys; 799 $parr = array(); 800 801 foreach($keys as $k) { 802 $f = $table->flds[$k]; 803 if ($f) { 804 $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); 805 } 806 } 807 return implode(' and ', $parr); 808 } 809 810 811 //------------------------------------------------------------ Public functions below 812 813 function Load($where=null,$bindarr=false) 814 { 815 $db = $this->DB(); 816 if (!$db) { 817 return false; 818 } 819 $this->_where = $where; 820 821 $save = $db->SetFetchMode(ADODB_FETCH_NUM); 822 $qry = "select * from ".$this->_table; 823 $table =& $this->TableInfo(); 824 825 if(($k = reset($table->keys))) { 826 $hasManyId = $k; 827 } 828 else { 829 $hasManyId = 'id'; 830 } 831 832 foreach($table->_belongsTo as $foreignTable) { 833 if(($k = reset($foreignTable->TableInfo()->keys))) { 834 $belongsToId = $k; 835 } 836 else { 837 $belongsToId = 'id'; 838 } 839 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 840 $this->_table.'.'.$foreignTable->foreignKey.'='. 841 $foreignTable->_table.'.'.$belongsToId; 842 } 843 foreach($table->_hasMany as $foreignTable) 844 { 845 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 846 $this->_table.'.'.$hasManyId.'='. 847 $foreignTable->_table.'.'.$foreignTable->foreignKey; 848 } 849 if($where) { 850 $qry .= ' WHERE '.$where; 851 } 852 853 // Simple case: no relations. Load row and return. 854 if((count($table->_hasMany) + count($table->_belongsTo)) < 1) { 855 $row = $db->GetRow($qry,$bindarr); 856 if(!$row) { 857 return false; 858 } 859 $db->SetFetchMode($save); 860 return $this->Set($row); 861 } 862 863 // More complex case when relations have to be collated 864 $rows = $db->GetAll($qry,$bindarr); 865 if(!$rows) { 866 return false; 867 } 868 $db->SetFetchMode($save); 869 if(count($rows) < 1) { 870 return false; 871 } 872 $class = get_class($this); 873 $isFirstRow = true; 874 875 if(($k = reset($this->TableInfo()->keys))) { 876 $myId = $k; 877 } 878 else { 879 $myId = 'id'; 880 } 881 $index = 0; $found = false; 882 /** @todo Improve by storing once and for all in table metadata */ 883 /** @todo Also re-use info for hasManyId */ 884 foreach($this->TableInfo()->flds as $fld) { 885 if($fld->name == $myId) { 886 $found = true; 887 break; 888 } 889 $index++; 890 } 891 if(!$found) { 892 $this->outp_throw("Unable to locate key $myId for $class in Load()",'Load'); 893 } 894 895 foreach($rows as $row) { 896 $rowId = intval($row[$index]); 897 if($rowId > 0) { 898 if($isFirstRow) { 899 $isFirstRow = false; 900 if(!$this->Set($row)) { 901 return false; 902 } 903 } 904 $obj = new $class($table,false,$db); 905 $obj->Set($row); 906 // TODO Copy/paste code below: bad! 907 if(count($table->_hasMany) > 0) { 908 foreach($table->_hasMany as $foreignTable) { 909 $foreignName = $foreignTable->foreignName; 910 if(!empty($obj->$foreignName)) { 911 if(!is_array($this->$foreignName)) { 912 $foreignObj = $this->$foreignName; 913 $this->$foreignName = array(clone($foreignObj)); 914 } 915 else { 916 $foreignObj = $obj->$foreignName; 917 array_push($this->$foreignName, clone($foreignObj)); 918 } 919 } 920 } 921 } 922 if(count($table->_belongsTo) > 0) { 923 foreach($table->_belongsTo as $foreignTable) { 924 $foreignName = $foreignTable->foreignName; 925 if(!empty($obj->$foreignName)) { 926 if(!is_array($this->$foreignName)) { 927 $foreignObj = $this->$foreignName; 928 $this->$foreignName = array(clone($foreignObj)); 929 } 930 else { 931 $foreignObj = $obj->$foreignName; 932 array_push($this->$foreignName, clone($foreignObj)); 933 } 934 } 935 } 936 } 937 } 938 } 939 return true; 940 } 941 942 // false on error 943 function Save() 944 { 945 if ($this->_saved) { 946 $ok = $this->Update(); 947 } 948 else { 949 $ok = $this->Insert(); 950 } 951 952 return $ok; 953 } 954 955 // CFR: Sometimes we may wish to consider that an object is not to be replaced but inserted. 956 // Sample use case: an 'undo' command object (after a delete()) 957 function Dirty() 958 { 959 $this->_saved = false; 960 } 961 962 // false on error 963 function Insert() 964 { 965 $db = $this->DB(); 966 if (!$db) { 967 return false; 968 } 969 $cnt = 0; 970 $table = $this->TableInfo(); 971 972 $valarr = array(); 973 $names = array(); 974 $valstr = array(); 975 976 foreach($table->flds as $name=>$fld) { 977 $val = $this->$name; 978 if(!is_null($val) || !array_key_exists($name, $table->keys)) { 979 $valarr[] = $val; 980 $names[] = $name; 981 $valstr[] = $db->Param($cnt); 982 $cnt += 1; 983 } 984 } 985 986 if (empty($names)){ 987 foreach($table->flds as $name=>$fld) { 988 $valarr[] = null; 989 $names[] = $name; 990 $valstr[] = $db->Param($cnt); 991 $cnt += 1; 992 } 993 } 994 $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; 995 $ok = $db->Execute($sql,$valarr); 996 997 if ($ok) { 998 $this->_saved = true; 999 $autoinc = false; 1000 foreach($table->keys as $k) { 1001 if (is_null($this->$k)) { 1002 $autoinc = true; 1003 break; 1004 } 1005 } 1006 if ($autoinc && sizeof($table->keys) == 1) { 1007 $k = reset($table->keys); 1008 $this->$k = $this->LastInsertID($db,$k); 1009 } 1010 } 1011 1012 $this->_original = $valarr; 1013 return !empty($ok); 1014 } 1015 1016 function Delete() 1017 { 1018 $db = $this->DB(); 1019 if (!$db) { 1020 return false; 1021 } 1022 $table = $this->TableInfo(); 1023 1024 $where = $this->GenWhere($db,$table); 1025 $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; 1026 $ok = $db->Execute($sql); 1027 1028 return $ok ? true : false; 1029 } 1030 1031 // returns an array of active record objects 1032 function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) 1033 { 1034 $db = $this->DB(); 1035 if (!$db || empty($this->_table)) { 1036 return false; 1037 } 1038 $table =& $this->TableInfo(); 1039 $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, 1040 array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); 1041 return $arr; 1042 } 1043 1044 // CFR: In introduced this method to ensure that inner workings are not disturbed by 1045 // subclasses...for instance when GetActiveRecordsClass invokes Find() 1046 // Why am I not invoking parent::Find? 1047 // Shockingly because I want to preserve PHP4 compatibility. 1048 function packageFind($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) 1049 { 1050 $db = $this->DB(); 1051 if (!$db || empty($this->_table)) { 1052 return false; 1053 } 1054 $table =& $this->TableInfo(); 1055 $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, 1056 array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); 1057 return $arr; 1058 } 1059 1060 // returns 0 on error, 1 on update, 2 on insert 1061 function Replace() 1062 { 1063 global $ADODB_ASSOC_CASE; 1064 1065 $db = $this->DB(); 1066 if (!$db) { 1067 return false; 1068 } 1069 $table = $this->TableInfo(); 1070 1071 $pkey = $table->keys; 1072 1073 foreach($table->flds as $name=>$fld) { 1074 $val = $this->$name; 1075 /* 1076 if (is_null($val)) { 1077 if (isset($fld->not_null) && $fld->not_null) { 1078 if (isset($fld->default_value) && strlen($fld->default_value)) { 1079 continue; 1080 } 1081 else { 1082 $this->Error("Cannot update null into $name","Replace"); 1083 return false; 1084 } 1085 } 1086 }*/ 1087 if (is_null($val) && !empty($fld->auto_increment)) { 1088 continue; 1089 } 1090 $t = $db->MetaType($fld->type); 1091 $arr[$name] = $this->doquote($db,$val,$t); 1092 $valarr[] = $val; 1093 } 1094 1095 if (!is_array($pkey)) { 1096 $pkey = array($pkey); 1097 } 1098 1099 1100 switch ($ADODB_ASSOC_CASE == 0) { 1101 case ADODB_ASSOC_CASE_LOWER: 1102 foreach($pkey as $k => $v) { 1103 $pkey[$k] = strtolower($v); 1104 } 1105 break; 1106 case ADODB_ASSOC_CASE_UPPER: 1107 foreach($pkey as $k => $v) { 1108 $pkey[$k] = strtoupper($v); 1109 } 1110 break; 1111 } 1112 1113 $ok = $db->Replace($this->_table,$arr,$pkey); 1114 if ($ok) { 1115 $this->_saved = true; // 1= update 2=insert 1116 if ($ok == 2) { 1117 $autoinc = false; 1118 foreach($table->keys as $k) { 1119 if (is_null($this->$k)) { 1120 $autoinc = true; 1121 break; 1122 } 1123 } 1124 if ($autoinc && sizeof($table->keys) == 1) { 1125 $k = reset($table->keys); 1126 $this->$k = $this->LastInsertID($db,$k); 1127 } 1128 } 1129 1130 $this->_original = $valarr; 1131 } 1132 return $ok; 1133 } 1134 1135 // returns 0 on error, 1 on update, -1 if no change in data (no update) 1136 function Update() 1137 { 1138 $db = $this->DB(); 1139 if (!$db) { 1140 return false; 1141 } 1142 $table = $this->TableInfo(); 1143 1144 $where = $this->GenWhere($db, $table); 1145 1146 if (!$where) { 1147 $this->error("Where missing for table $table", "Update"); 1148 return false; 1149 } 1150 $valarr = array(); 1151 $neworig = array(); 1152 $pairs = array(); 1153 $i = -1; 1154 $cnt = 0; 1155 foreach($table->flds as $name=>$fld) { 1156 $i += 1; 1157 $val = $this->$name; 1158 $neworig[] = $val; 1159 1160 if (isset($table->keys[$name])) { 1161 continue; 1162 } 1163 1164 if (is_null($val)) { 1165 if (isset($fld->not_null) && $fld->not_null) { 1166 if (isset($fld->default_value) && strlen($fld->default_value)) { 1167 continue; 1168 } 1169 else { 1170 $this->Error("Cannot set field $name to NULL","Update"); 1171 return false; 1172 } 1173 } 1174 } 1175 1176 if (isset($this->_original[$i]) && $val === $this->_original[$i]) { 1177 continue; 1178 } 1179 $valarr[] = $val; 1180 $pairs[] = $name.'='.$db->Param($cnt); 1181 $cnt += 1; 1182 } 1183 1184 1185 if (!$cnt) { 1186 return -1; 1187 } 1188 $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; 1189 $ok = $db->Execute($sql,$valarr); 1190 if ($ok) { 1191 $this->_original = $neworig; 1192 return 1; 1193 } 1194 return 0; 1195 } 1196 1197 function GetAttributeNames() 1198 { 1199 $table = $this->TableInfo(); 1200 if (!$table) { 1201 return false; 1202 } 1203 return array_keys($table->flds); 1204 } 1205 1206 }; 1207 1208 function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr, 1209 $extra, $relations) 1210 { 1211 global $_ADODB_ACTIVE_DBS; 1212 1213 if (empty($extra['loading'])) { 1214 $extra['loading'] = ADODB_LAZY_AR; 1215 } 1216 $save = $db->SetFetchMode(ADODB_FETCH_NUM); 1217 $table = &$tableObj->_table; 1218 $tableInfo =& $tableObj->TableInfo(); 1219 if(($k = reset($tableInfo->keys))) { 1220 $myId = $k; 1221 } 1222 else { 1223 $myId = 'id'; 1224 } 1225 $index = 0; $found = false; 1226 /** @todo Improve by storing once and for all in table metadata */ 1227 /** @todo Also re-use info for hasManyId */ 1228 foreach($tableInfo->flds as $fld) 1229 { 1230 if($fld->name == $myId) { 1231 $found = true; 1232 break; 1233 } 1234 $index++; 1235 } 1236 if(!$found) { 1237 $db->outp_throw("Unable to locate key $myId for $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); 1238 } 1239 1240 $qry = "select * from ".$table; 1241 if(ADODB_JOIN_AR == $extra['loading']) { 1242 if(!empty($relations['belongsTo'])) { 1243 foreach($relations['belongsTo'] as $foreignTable) { 1244 if(($k = reset($foreignTable->TableInfo()->keys))) { 1245 $belongsToId = $k; 1246 } 1247 else { 1248 $belongsToId = 'id'; 1249 } 1250 1251 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 1252 $table.'.'.$foreignTable->foreignKey.'='. 1253 $foreignTable->_table.'.'.$belongsToId; 1254 } 1255 } 1256 if(!empty($relations['hasMany'])) { 1257 if(empty($relations['foreignName'])) { 1258 $db->outp_throw("Missing foreignName is relation specification in GetActiveRecordsClass()",'GetActiveRecordsClass'); 1259 } 1260 if(($k = reset($tableInfo->keys))) { 1261 $hasManyId = $k; 1262 } 1263 else { 1264 $hasManyId = 'id'; 1265 } 1266 1267 foreach($relations['hasMany'] as $foreignTable) { 1268 $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. 1269 $table.'.'.$hasManyId.'='. 1270 $foreignTable->_table.'.'.$foreignTable->foreignKey; 1271 } 1272 } 1273 } 1274 if (!empty($whereOrderBy)) { 1275 $qry .= ' WHERE '.$whereOrderBy; 1276 } 1277 if(isset($extra['limit'])) { 1278 $rows = false; 1279 if(isset($extra['offset'])) { 1280 $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']); 1281 } else { 1282 $rs = $db->SelectLimit($qry, $extra['limit']); 1283 } 1284 if ($rs) { 1285 while (!$rs->EOF) { 1286 $rows[] = $rs->fields; 1287 $rs->MoveNext(); 1288 } 1289 } 1290 } else 1291 $rows = $db->GetAll($qry,$bindarr); 1292 1293 $db->SetFetchMode($save); 1294 1295 $false = false; 1296 1297 if ($rows === false) { 1298 return $false; 1299 } 1300 1301 1302 if (!isset($_ADODB_ACTIVE_DBS)) { 1303 include (ADODB_DIR.'/adodb-active-record.inc.php'); 1304 } 1305 if (!class_exists($class)) { 1306 $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); 1307 return $false; 1308 } 1309 $uniqArr = array(); // CFR Keep track of records for relations 1310 $arr = array(); 1311 // arrRef will be the structure that knows about our objects. 1312 // It is an associative array. 1313 // We will, however, return arr, preserving regular 0.. order so that 1314 // obj[0] can be used by app developpers. 1315 $arrRef = array(); 1316 $bTos = array(); // Will store belongTo's indices if any 1317 foreach($rows as $row) { 1318 1319 $obj = new $class($table,$primkeyArr,$db); 1320 if ($obj->ErrorNo()){ 1321 $db->_errorMsg = $obj->ErrorMsg(); 1322 return $false; 1323 } 1324 $obj->Set($row); 1325 // CFR: FIXME: Insane assumption here: 1326 // If the first column returned is an integer, then it's a 'id' field 1327 // And to make things a bit worse, I use intval() rather than is_int() because, in fact, 1328 // $row[0] is not an integer. 1329 // 1330 // So, what does this whole block do? 1331 // When relationships are found, we perform JOINs. This is fast. But not accurate: 1332 // instead of returning n objects with their n' associated cousins, 1333 // we get n*n' objects. This code fixes this. 1334 // Note: to-many relationships mess around with the 'limit' parameter 1335 $rowId = intval($row[$index]); 1336 1337 if(ADODB_WORK_AR == $extra['loading']) { 1338 $arrRef[$rowId] = $obj; 1339 $arr[] = &$arrRef[$rowId]; 1340 if(!isset($indices)) { 1341 $indices = $rowId; 1342 } 1343 else { 1344 $indices .= ','.$rowId; 1345 } 1346 if(!empty($relations['belongsTo'])) { 1347 foreach($relations['belongsTo'] as $foreignTable) { 1348 $foreignTableRef = $foreignTable->foreignKey; 1349 // First array: list of foreign ids we are looking for 1350 if(empty($bTos[$foreignTableRef])) { 1351 $bTos[$foreignTableRef] = array(); 1352 } 1353 // Second array: list of ids found 1354 if(empty($obj->$foreignTableRef)) { 1355 continue; 1356 } 1357 if(empty($bTos[$foreignTableRef][$obj->$foreignTableRef])) { 1358 $bTos[$foreignTableRef][$obj->$foreignTableRef] = array(); 1359 } 1360 $bTos[$foreignTableRef][$obj->$foreignTableRef][] = $obj; 1361 } 1362 } 1363 continue; 1364 } 1365 1366 if($rowId>0) { 1367 if(ADODB_JOIN_AR == $extra['loading']) { 1368 $isNewObj = !isset($uniqArr['_'.$row[0]]); 1369 if($isNewObj) { 1370 $uniqArr['_'.$row[0]] = $obj; 1371 } 1372 1373 // TODO Copy/paste code below: bad! 1374 if(!empty($relations['hasMany'])) { 1375 foreach($relations['hasMany'] as $foreignTable) { 1376 $foreignName = $foreignTable->foreignName; 1377 if(!empty($obj->$foreignName)) { 1378 $masterObj = &$uniqArr['_'.$row[0]]; 1379 // Assumption: this property exists in every object since they are instances of the same class 1380 if(!is_array($masterObj->$foreignName)) { 1381 // Pluck! 1382 $foreignObj = $masterObj->$foreignName; 1383 $masterObj->$foreignName = array(clone($foreignObj)); 1384 } 1385 else { 1386 // Pluck pluck! 1387 $foreignObj = $obj->$foreignName; 1388 array_push($masterObj->$foreignName, clone($foreignObj)); 1389 } 1390 } 1391 } 1392 } 1393 if(!empty($relations['belongsTo'])) { 1394 foreach($relations['belongsTo'] as $foreignTable) { 1395 $foreignName = $foreignTable->foreignName; 1396 if(!empty($obj->$foreignName)) { 1397 $masterObj = &$uniqArr['_'.$row[0]]; 1398 // Assumption: this property exists in every object since they are instances of the same class 1399 if(!is_array($masterObj->$foreignName)) { 1400 // Pluck! 1401 $foreignObj = $masterObj->$foreignName; 1402 $masterObj->$foreignName = array(clone($foreignObj)); 1403 } 1404 else { 1405 // Pluck pluck! 1406 $foreignObj = $obj->$foreignName; 1407 array_push($masterObj->$foreignName, clone($foreignObj)); 1408 } 1409 } 1410 } 1411 } 1412 if(!$isNewObj) { 1413 unset($obj); // We do not need this object itself anymore and do not want it re-added to the main array 1414 } 1415 } 1416 else if(ADODB_LAZY_AR == $extra['loading']) { 1417 // Lazy loading: we need to give AdoDb a hint that we have not really loaded 1418 // anything, all the while keeping enough information on what we wish to load. 1419 // Let's do this by keeping the relevant info in our relationship arrays 1420 // but get rid of the actual properties. 1421 // We will then use PHP's __get to load these properties on-demand. 1422 if(!empty($relations['hasMany'])) { 1423 foreach($relations['hasMany'] as $foreignTable) { 1424 $foreignName = $foreignTable->foreignName; 1425 if(!empty($obj->$foreignName)) { 1426 unset($obj->$foreignName); 1427 } 1428 } 1429 } 1430 if(!empty($relations['belongsTo'])) { 1431 foreach($relations['belongsTo'] as $foreignTable) { 1432 $foreignName = $foreignTable->foreignName; 1433 if(!empty($obj->$foreignName)) { 1434 unset($obj->$foreignName); 1435 } 1436 } 1437 } 1438 } 1439 } 1440 1441 if(isset($obj)) { 1442 $arr[] = $obj; 1443 } 1444 } 1445 1446 if(ADODB_WORK_AR == $extra['loading']) { 1447 // The best of both worlds? 1448 // Here, the number of queries is constant: 1 + n*relationship. 1449 // The second query will allow us to perform a good join 1450 // while preserving LIMIT etc. 1451 if(!empty($relations['hasMany'])) { 1452 foreach($relations['hasMany'] as $foreignTable) { 1453 $foreignName = $foreignTable->foreignName; 1454 $className = ucfirst($foreignTable->_singularize($foreignName)); 1455 $obj = new $className(); 1456 $dbClassRef = $foreignTable->foreignKey; 1457 $objs = $obj->packageFind($dbClassRef.' IN ('.$indices.')'); 1458 foreach($objs as $obj) { 1459 if(!is_array($arrRef[$obj->$dbClassRef]->$foreignName)) { 1460 $arrRef[$obj->$dbClassRef]->$foreignName = array(); 1461 } 1462 array_push($arrRef[$obj->$dbClassRef]->$foreignName, $obj); 1463 } 1464 } 1465 1466 } 1467 if(!empty($relations['belongsTo'])) { 1468 foreach($relations['belongsTo'] as $foreignTable) { 1469 $foreignTableRef = $foreignTable->foreignKey; 1470 if(empty($bTos[$foreignTableRef])) { 1471 continue; 1472 } 1473 if(($k = reset($foreignTable->TableInfo()->keys))) { 1474 $belongsToId = $k; 1475 } 1476 else { 1477 $belongsToId = 'id'; 1478 } 1479 $origObjsArr = $bTos[$foreignTableRef]; 1480 $bTosString = implode(',', array_keys($bTos[$foreignTableRef])); 1481 $foreignName = $foreignTable->foreignName; 1482 $className = ucfirst($foreignTable->_singularize($foreignName)); 1483 $obj = new $className(); 1484 $objs = $obj->packageFind($belongsToId.' IN ('.$bTosString.')'); 1485 foreach($objs as $obj) 1486 { 1487 foreach($origObjsArr[$obj->$belongsToId] as $idx=>$origObj) 1488 { 1489 $origObj->$foreignName = $obj; 1490 } 1491 } 1492 } 1493 } 1494 } 1495 1496 return $arr; 1497 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |