source: moodle/trunk/fuentes/lib/adodb/adodb-active-recordx.inc.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

File size: 37.9 KB
Line 
1<?php
2/*
3
4@version   v5.20.1  06-Dec-2015
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
25define('ADODB_JOIN_AR', 0x01);
26define('ADODB_WORK_AR', 0x02);
27define('ADODB_LAZY_AR', 0x03);
28
29
30global $_ADODB_ACTIVE_DBS;
31global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
32global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
33global $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
40class ADODB_Active_DB {
41        var $db; // ADOConnection
42        var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
43}
44
45class 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
65function 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
91class 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
1208function 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}
Note: See TracBrowser for help on using the repository browser.