[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/adodb/ -> adodb-active-recordx.inc.php (source)

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


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7