[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1