[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

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

   1  <?php
   2  /*
   3  
   4  @version V5.19  23-Apr-2014  (c) 2000-2014 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)>0 &&
 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  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1