[ Index ]

PHP Cross Reference of Moodle 310

title

Body

[close]

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

   1  <?php
   2  /*
   3  
   4  @version   v5.20.16  12-Jan-2020
   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.org/
   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_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_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 ADODB_ASSOC_CASE_LOWER:
 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 ADODB_ASSOC_CASE_UPPER:
 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          $db = $this->DB();
1064          if (!$db) {
1065              return false;
1066          }
1067          $table = $this->TableInfo();
1068  
1069          $pkey = $table->keys;
1070  
1071          foreach($table->flds as $name=>$fld) {
1072              $val = $this->$name;
1073              /*
1074              if (is_null($val)) {
1075                  if (isset($fld->not_null) && $fld->not_null) {
1076                      if (isset($fld->default_value) && strlen($fld->default_value)) {
1077                          continue;
1078                      }
1079                      else {
1080                          $this->Error("Cannot update null into $name","Replace");
1081                          return false;
1082                      }
1083                  }
1084              }*/
1085              if (is_null($val) && !empty($fld->auto_increment)) {
1086                  continue;
1087              }
1088              $t = $db->MetaType($fld->type);
1089              $arr[$name] = $this->doquote($db,$val,$t);
1090              $valarr[] = $val;
1091          }
1092  
1093          if (!is_array($pkey)) {
1094              $pkey = array($pkey);
1095          }
1096  
1097  
1098          switch (ADODB_ASSOC_CASE) {
1099              case ADODB_ASSOC_CASE_LOWER:
1100                  foreach($pkey as $k => $v) {
1101                      $pkey[$k] = strtolower($v);
1102                  }
1103                  break;
1104              case ADODB_ASSOC_CASE_UPPER:
1105                  foreach($pkey as $k => $v) {
1106                      $pkey[$k] = strtoupper($v);
1107                  }
1108                  break;
1109          }
1110  
1111          $ok = $db->Replace($this->_table,$arr,$pkey);
1112          if ($ok) {
1113              $this->_saved = true; // 1= update 2=insert
1114              if ($ok == 2) {
1115                  $autoinc = false;
1116                  foreach($table->keys as $k) {
1117                      if (is_null($this->$k)) {
1118                          $autoinc = true;
1119                          break;
1120                      }
1121                  }
1122                  if ($autoinc && sizeof($table->keys) == 1) {
1123                      $k = reset($table->keys);
1124                      $this->$k = $this->LastInsertID($db,$k);
1125                  }
1126              }
1127  
1128              $this->_original = $valarr;
1129          }
1130          return $ok;
1131      }
1132  
1133      // returns 0 on error, 1 on update, -1 if no change in data (no update)
1134  	function Update()
1135      {
1136          $db = $this->DB();
1137          if (!$db) {
1138              return false;
1139          }
1140          $table = $this->TableInfo();
1141  
1142          $where = $this->GenWhere($db, $table);
1143  
1144          if (!$where) {
1145              $this->error("Where missing for table $table", "Update");
1146              return false;
1147          }
1148          $valarr = array();
1149          $neworig = array();
1150          $pairs = array();
1151          $i = -1;
1152          $cnt = 0;
1153          foreach($table->flds as $name=>$fld) {
1154              $i += 1;
1155              $val = $this->$name;
1156              $neworig[] = $val;
1157  
1158              if (isset($table->keys[$name])) {
1159                  continue;
1160              }
1161  
1162              if (is_null($val)) {
1163                  if (isset($fld->not_null) && $fld->not_null) {
1164                      if (isset($fld->default_value) && strlen($fld->default_value)) {
1165                          continue;
1166                      }
1167                      else {
1168                          $this->Error("Cannot set field $name to NULL","Update");
1169                          return false;
1170                      }
1171                  }
1172              }
1173  
1174              if (isset($this->_original[$i]) && $val === $this->_original[$i]) {
1175                  continue;
1176              }
1177              $valarr[] = $val;
1178              $pairs[] = $name.'='.$db->Param($cnt);
1179              $cnt += 1;
1180          }
1181  
1182  
1183          if (!$cnt) {
1184              return -1;
1185          }
1186          $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
1187          $ok = $db->Execute($sql,$valarr);
1188          if ($ok) {
1189              $this->_original = $neworig;
1190              return 1;
1191          }
1192          return 0;
1193      }
1194  
1195  	function GetAttributeNames()
1196      {
1197          $table = $this->TableInfo();
1198          if (!$table) {
1199              return false;
1200          }
1201          return array_keys($table->flds);
1202      }
1203  
1204  };
1205  
1206  function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr,
1207              $extra, $relations)
1208  {
1209      global $_ADODB_ACTIVE_DBS;
1210  
1211          if (empty($extra['loading'])) {
1212              $extra['loading'] = ADODB_LAZY_AR;
1213          }
1214          $save = $db->SetFetchMode(ADODB_FETCH_NUM);
1215          $table = &$tableObj->_table;
1216          $tableInfo =& $tableObj->TableInfo();
1217          if(($k = reset($tableInfo->keys))) {
1218              $myId = $k;
1219          }
1220          else {
1221              $myId = 'id';
1222          }
1223          $index = 0; $found = false;
1224          /** @todo Improve by storing once and for all in table metadata */
1225          /** @todo Also re-use info for hasManyId */
1226          foreach($tableInfo->flds as $fld)
1227          {
1228              if($fld->name == $myId) {
1229                  $found = true;
1230                  break;
1231              }
1232              $index++;
1233          }
1234          if(!$found) {
1235              $db->outp_throw("Unable to locate key $myId for $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
1236          }
1237  
1238          $qry = "select * from ".$table;
1239          if(ADODB_JOIN_AR == $extra['loading']) {
1240              if(!empty($relations['belongsTo'])) {
1241                  foreach($relations['belongsTo'] as $foreignTable) {
1242                      if(($k = reset($foreignTable->TableInfo()->keys))) {
1243                          $belongsToId = $k;
1244                      }
1245                      else {
1246                          $belongsToId = 'id';
1247                      }
1248  
1249                      $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
1250                          $table.'.'.$foreignTable->foreignKey.'='.
1251                          $foreignTable->_table.'.'.$belongsToId;
1252                  }
1253              }
1254              if(!empty($relations['hasMany'])) {
1255                  if(empty($relations['foreignName'])) {
1256                      $db->outp_throw("Missing foreignName is relation specification in GetActiveRecordsClass()",'GetActiveRecordsClass');
1257                  }
1258                  if(($k = reset($tableInfo->keys))) {
1259                      $hasManyId   = $k;
1260                  }
1261                  else {
1262                      $hasManyId   = 'id';
1263                  }
1264  
1265                  foreach($relations['hasMany'] as $foreignTable) {
1266                      $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
1267                          $table.'.'.$hasManyId.'='.
1268                          $foreignTable->_table.'.'.$foreignTable->foreignKey;
1269                  }
1270              }
1271          }
1272          if (!empty($whereOrderBy)) {
1273              $qry .= ' WHERE '.$whereOrderBy;
1274          }
1275          if(isset($extra['limit'])) {
1276              $rows = false;
1277              if(isset($extra['offset'])) {
1278                  $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']);
1279              } else {
1280                  $rs = $db->SelectLimit($qry, $extra['limit']);
1281              }
1282              if ($rs) {
1283                  while (!$rs->EOF) {
1284                      $rows[] = $rs->fields;
1285                      $rs->MoveNext();
1286                  }
1287              }
1288          } else
1289              $rows = $db->GetAll($qry,$bindarr);
1290  
1291          $db->SetFetchMode($save);
1292  
1293          $false = false;
1294  
1295          if ($rows === false) {
1296              return $false;
1297          }
1298  
1299  
1300          if (!isset($_ADODB_ACTIVE_DBS)) {
1301              include (ADODB_DIR.'/adodb-active-record.inc.php');
1302          }
1303          if (!class_exists($class)) {
1304              $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
1305              return $false;
1306          }
1307          $uniqArr = array(); // CFR Keep track of records for relations
1308          $arr = array();
1309          // arrRef will be the structure that knows about our objects.
1310          // It is an associative array.
1311          // We will, however, return arr, preserving regular 0.. order so that
1312          // obj[0] can be used by app developpers.
1313          $arrRef = array();
1314          $bTos = array(); // Will store belongTo's indices if any
1315          foreach($rows as $row) {
1316  
1317              $obj = new $class($table,$primkeyArr,$db);
1318              if ($obj->ErrorNo()){
1319                  $db->_errorMsg = $obj->ErrorMsg();
1320                  return $false;
1321              }
1322              $obj->Set($row);
1323              // CFR: FIXME: Insane assumption here:
1324              // If the first column returned is an integer, then it's a 'id' field
1325              // And to make things a bit worse, I use intval() rather than is_int() because, in fact,
1326              // $row[0] is not an integer.
1327              //
1328              // So, what does this whole block do?
1329              // When relationships are found, we perform JOINs. This is fast. But not accurate:
1330              // instead of returning n objects with their n' associated cousins,
1331              // we get n*n' objects. This code fixes this.
1332              // Note: to-many relationships mess around with the 'limit' parameter
1333              $rowId = intval($row[$index]);
1334  
1335              if(ADODB_WORK_AR == $extra['loading']) {
1336                  $arrRef[$rowId] = $obj;
1337                  $arr[] = &$arrRef[$rowId];
1338                  if(!isset($indices)) {
1339                      $indices = $rowId;
1340                  }
1341                  else {
1342                      $indices .= ','.$rowId;
1343                  }
1344                  if(!empty($relations['belongsTo'])) {
1345                      foreach($relations['belongsTo'] as $foreignTable) {
1346                          $foreignTableRef = $foreignTable->foreignKey;
1347                          // First array: list of foreign ids we are looking for
1348                          if(empty($bTos[$foreignTableRef])) {
1349                              $bTos[$foreignTableRef] = array();
1350                          }
1351                          // Second array: list of ids found
1352                          if(empty($obj->$foreignTableRef)) {
1353                              continue;
1354                          }
1355                          if(empty($bTos[$foreignTableRef][$obj->$foreignTableRef])) {
1356                              $bTos[$foreignTableRef][$obj->$foreignTableRef] = array();
1357                          }
1358                          $bTos[$foreignTableRef][$obj->$foreignTableRef][] = $obj;
1359                      }
1360                  }
1361                  continue;
1362              }
1363  
1364              if($rowId>0) {
1365                  if(ADODB_JOIN_AR == $extra['loading']) {
1366                      $isNewObj = !isset($uniqArr['_'.$row[0]]);
1367                      if($isNewObj) {
1368                          $uniqArr['_'.$row[0]] = $obj;
1369                      }
1370  
1371                      // TODO Copy/paste code below: bad!
1372                      if(!empty($relations['hasMany'])) {
1373                          foreach($relations['hasMany'] as $foreignTable) {
1374                              $foreignName = $foreignTable->foreignName;
1375                              if(!empty($obj->$foreignName)) {
1376                                  $masterObj = &$uniqArr['_'.$row[0]];
1377                                  // Assumption: this property exists in every object since they are instances of the same class
1378                                  if(!is_array($masterObj->$foreignName)) {
1379                                      // Pluck!
1380                                      $foreignObj = $masterObj->$foreignName;
1381                                      $masterObj->$foreignName = array(clone($foreignObj));
1382                                  }
1383                                  else {
1384                                      // Pluck pluck!
1385                                      $foreignObj = $obj->$foreignName;
1386                                      array_push($masterObj->$foreignName, clone($foreignObj));
1387                                  }
1388                              }
1389                          }
1390                      }
1391                      if(!empty($relations['belongsTo'])) {
1392                          foreach($relations['belongsTo'] as $foreignTable) {
1393                              $foreignName = $foreignTable->foreignName;
1394                              if(!empty($obj->$foreignName)) {
1395                                  $masterObj = &$uniqArr['_'.$row[0]];
1396                                  // Assumption: this property exists in every object since they are instances of the same class
1397                                  if(!is_array($masterObj->$foreignName)) {
1398                                      // Pluck!
1399                                      $foreignObj = $masterObj->$foreignName;
1400                                      $masterObj->$foreignName = array(clone($foreignObj));
1401                                  }
1402                                  else {
1403                                      // Pluck pluck!
1404                                      $foreignObj = $obj->$foreignName;
1405                                      array_push($masterObj->$foreignName, clone($foreignObj));
1406                                  }
1407                              }
1408                          }
1409                      }
1410                      if(!$isNewObj) {
1411                          unset($obj); // We do not need this object itself anymore and do not want it re-added to the main array
1412                      }
1413                  }
1414                  else if(ADODB_LAZY_AR == $extra['loading']) {
1415                      // Lazy loading: we need to give AdoDb a hint that we have not really loaded
1416                      // anything, all the while keeping enough information on what we wish to load.
1417                      // Let's do this by keeping the relevant info in our relationship arrays
1418                      // but get rid of the actual properties.
1419                      // We will then use PHP's __get to load these properties on-demand.
1420                      if(!empty($relations['hasMany'])) {
1421                          foreach($relations['hasMany'] as $foreignTable) {
1422                              $foreignName = $foreignTable->foreignName;
1423                              if(!empty($obj->$foreignName)) {
1424                                  unset($obj->$foreignName);
1425                              }
1426                          }
1427                      }
1428                      if(!empty($relations['belongsTo'])) {
1429                          foreach($relations['belongsTo'] as $foreignTable) {
1430                              $foreignName = $foreignTable->foreignName;
1431                              if(!empty($obj->$foreignName)) {
1432                                  unset($obj->$foreignName);
1433                              }
1434                          }
1435                      }
1436                  }
1437              }
1438  
1439              if(isset($obj)) {
1440                  $arr[] = $obj;
1441              }
1442          }
1443  
1444          if(ADODB_WORK_AR == $extra['loading']) {
1445              // The best of both worlds?
1446              // Here, the number of queries is constant: 1 + n*relationship.
1447              // The second query will allow us to perform a good join
1448              // while preserving LIMIT etc.
1449              if(!empty($relations['hasMany'])) {
1450                  foreach($relations['hasMany'] as $foreignTable) {
1451                      $foreignName = $foreignTable->foreignName;
1452                      $className = ucfirst($foreignTable->_singularize($foreignName));
1453                      $obj = new $className();
1454                      $dbClassRef = $foreignTable->foreignKey;
1455                      $objs = $obj->packageFind($dbClassRef.' IN ('.$indices.')');
1456                      foreach($objs as $obj) {
1457                          if(!is_array($arrRef[$obj->$dbClassRef]->$foreignName)) {
1458                              $arrRef[$obj->$dbClassRef]->$foreignName = array();
1459                          }
1460                          array_push($arrRef[$obj->$dbClassRef]->$foreignName, $obj);
1461                      }
1462                  }
1463  
1464              }
1465              if(!empty($relations['belongsTo'])) {
1466                  foreach($relations['belongsTo'] as $foreignTable) {
1467                      $foreignTableRef = $foreignTable->foreignKey;
1468                      if(empty($bTos[$foreignTableRef])) {
1469                          continue;
1470                      }
1471                      if(($k = reset($foreignTable->TableInfo()->keys))) {
1472                          $belongsToId = $k;
1473                      }
1474                      else {
1475                          $belongsToId = 'id';
1476                      }
1477                      $origObjsArr = $bTos[$foreignTableRef];
1478                      $bTosString = implode(',', array_keys($bTos[$foreignTableRef]));
1479                      $foreignName = $foreignTable->foreignName;
1480                      $className = ucfirst($foreignTable->_singularize($foreignName));
1481                      $obj = new $className();
1482                      $objs = $obj->packageFind($belongsToId.' IN ('.$bTosString.')');
1483                      foreach($objs as $obj)
1484                      {
1485                          foreach($origObjsArr[$obj->$belongsToId] as $idx=>$origObj)
1486                          {
1487                              $origObj->$foreignName = $obj;
1488                          }
1489                      }
1490                  }
1491              }
1492          }
1493  
1494          return $arr;
1495  }


Generated: Wed Jan 22 11:59:49 2025 Cross-referenced by PHPXref 0.7.1