MDL-70959 completion: Fix unit tests for get_data()
[moodle.git] / lib / adodb / adodb-active-record.inc.php
blob304ff068093faef2cac4b56a990c6edf062cbb24
1 <?php
2 /*
4 @version v5.20.16 12-Jan-2020
5 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
6 @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
7 Latest version is available at http://adodb.org/
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.
13 Active Record implementation. Superset of Zend Framework's.
15 Version 0.92
17 See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord
18 for info on Ruby on Rails Active Record implementation
22 global $_ADODB_ACTIVE_DBS;
23 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
24 global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
25 global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record.
27 // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
28 $_ADODB_ACTIVE_DBS = array();
29 $ACTIVE_RECORD_SAFETY = true;
30 $ADODB_ACTIVE_DEFVALS = false;
31 $ADODB_ACTIVE_CACHESECS = 0;
33 class ADODB_Active_DB {
34 var $db; // ADOConnection
35 var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
38 class ADODB_Active_Table {
39 var $name; // table name
40 var $flds; // assoc array of adofieldobjs, indexed by fieldname
41 var $keys; // assoc array of primary keys, indexed by fieldname
42 var $_created; // only used when stored as a cached file
43 var $_belongsTo = array();
44 var $_hasMany = array();
47 // $db = database connection
48 // $index = name of index - can be associative, for an example see
49 // http://phplens.com/lens/lensforum/msgs.php?id=17790
50 // returns index into $_ADODB_ACTIVE_DBS
51 function ADODB_SetDatabaseAdapter(&$db, $index=false)
53 global $_ADODB_ACTIVE_DBS;
55 foreach($_ADODB_ACTIVE_DBS as $k => $d) {
56 if (PHP_VERSION >= 5) {
57 if ($d->db === $db) {
58 return $k;
60 } else {
61 if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) {
62 return $k;
67 $obj = new ADODB_Active_DB();
68 $obj->db = $db;
69 $obj->tables = array();
71 if ($index == false) {
72 $index = sizeof($_ADODB_ACTIVE_DBS);
75 $_ADODB_ACTIVE_DBS[$index] = $obj;
77 return sizeof($_ADODB_ACTIVE_DBS)-1;
81 class ADODB_Active_Record {
82 static $_changeNames = true; // dynamically pluralize table names
83 static $_quoteNames = false;
85 static $_foreignSuffix = '_id'; //
86 var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
87 var $_table; // tablename, if set in class definition then use it as table name
88 var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
89 var $_where; // where clause set in Load()
90 var $_saved = false; // indicates whether data is already inserted.
91 var $_lasterr = false; // last error message
92 var $_original = false; // the original values loaded or inserted, refreshed on update
94 var $foreignName; // CFR: class name when in a relationship
96 var $lockMode = ' for update '; // you might want to change to
98 static function UseDefaultValues($bool=null)
100 global $ADODB_ACTIVE_DEFVALS;
101 if (isset($bool)) {
102 $ADODB_ACTIVE_DEFVALS = $bool;
104 return $ADODB_ACTIVE_DEFVALS;
107 // should be static
108 static function SetDatabaseAdapter(&$db, $index=false)
110 return ADODB_SetDatabaseAdapter($db, $index);
114 public function __set($name, $value)
116 $name = str_replace(' ', '_', $name);
117 $this->$name = $value;
120 // php5 constructor
121 function __construct($table = false, $pkeyarr=false, $db=false)
123 global $_ADODB_ACTIVE_DBS;
125 if ($db == false && is_object($pkeyarr)) {
126 $db = $pkeyarr;
127 $pkeyarr = false;
130 if (!$table) {
131 if (!empty($this->_table)) {
132 $table = $this->_table;
134 else $table = $this->_pluralize(get_class($this));
136 $this->foreignName = strtolower(get_class($this)); // CFR: default foreign name
137 if ($db) {
138 $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
139 } else if (!isset($this->_dbat)) {
140 if (sizeof($_ADODB_ACTIVE_DBS) == 0) {
141 $this->Error(
142 "No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",
143 'ADODB_Active_Record::__constructor'
146 end($_ADODB_ACTIVE_DBS);
147 $this->_dbat = key($_ADODB_ACTIVE_DBS);
150 $this->_table = $table;
151 $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
153 $this->UpdateActiveTable($pkeyarr);
156 function __wakeup()
158 $class = get_class($this);
159 new $class;
162 function _pluralize($table)
164 if (!ADODB_Active_Record::$_changeNames) {
165 return $table;
168 $ut = strtoupper($table);
169 $len = strlen($table);
170 $lastc = $ut[$len-1];
171 $lastc2 = substr($ut,$len-2);
172 switch ($lastc) {
173 case 'S':
174 return $table.'es';
175 case 'Y':
176 return substr($table,0,$len-1).'ies';
177 case 'X':
178 return $table.'es';
179 case 'H':
180 if ($lastc2 == 'CH' || $lastc2 == 'SH') {
181 return $table.'es';
183 default:
184 return $table.'s';
188 // CFR Lamest singular inflector ever - @todo Make it real!
189 // Note: There is an assumption here...and it is that the argument's length >= 4
190 function _singularize($tables)
193 if (!ADODB_Active_Record::$_changeNames) {
194 return $table;
197 $ut = strtoupper($tables);
198 $len = strlen($tables);
199 if($ut[$len-1] != 'S') {
200 return $tables; // I know...forget oxen
202 if($ut[$len-2] != 'E') {
203 return substr($tables, 0, $len-1);
205 switch($ut[$len-3]) {
206 case 'S':
207 case 'X':
208 return substr($tables, 0, $len-2);
209 case 'I':
210 return substr($tables, 0, $len-3) . 'y';
211 case 'H';
212 if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') {
213 return substr($tables, 0, $len-2);
215 default:
216 return substr($tables, 0, $len-1); // ?
220 function hasMany($foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
222 $ar = new $foreignClass($foreignRef);
223 $ar->foreignName = $foreignRef;
224 $ar->UpdateActiveTable();
225 $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
226 $table =& $this->TableInfo();
227 $table->_hasMany[$foreignRef] = $ar;
228 # $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
231 // use when you don't want ADOdb to auto-pluralize tablename
232 static function TableHasMany($table, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
234 $ar = new ADODB_Active_Record($table);
235 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
238 // use when you don't want ADOdb to auto-pluralize tablename
239 static function TableKeyHasMany($table, $tablePKey, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
241 if (!is_array($tablePKey)) {
242 $tablePKey = array($tablePKey);
244 $ar = new ADODB_Active_Record($table,$tablePKey);
245 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
249 // use when you want ADOdb to auto-pluralize tablename for you. Note that the class must already be defined.
250 // e.g. class Person will generate relationship for table Persons
251 static function ClassHasMany($parentclass, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
253 $ar = new $parentclass();
254 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
258 function belongsTo($foreignRef,$foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
260 global $inflector;
262 $ar = new $parentClass($this->_pluralize($foreignRef));
263 $ar->foreignName = $foreignRef;
264 $ar->parentKey = $parentKey;
265 $ar->UpdateActiveTable();
266 $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
268 $table =& $this->TableInfo();
269 $table->_belongsTo[$foreignRef] = $ar;
270 # $this->$foreignRef = $this->_belongsTo[$foreignRef];
273 static function ClassBelongsTo($class, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
275 $ar = new $class();
276 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
279 static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
281 $ar = new ADOdb_Active_Record($table);
282 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
285 static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
287 if (!is_array($tablePKey)) {
288 $tablePKey = array($tablePKey);
290 $ar = new ADOdb_Active_Record($table, $tablePKey);
291 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
296 * __get Access properties - used for lazy loading
298 * @param mixed $name
299 * @access protected
300 * @return mixed
302 function __get($name)
304 return $this->LoadRelations($name, '', -1, -1);
308 * @param string $name
309 * @param string $whereOrderBy : eg. ' AND field1 = value ORDER BY field2'
310 * @param offset
311 * @param limit
312 * @return mixed
314 function LoadRelations($name, $whereOrderBy='', $offset=-1,$limit=-1)
316 $extras = array();
317 $table = $this->TableInfo();
318 if ($limit >= 0) {
319 $extras['limit'] = $limit;
321 if ($offset >= 0) {
322 $extras['offset'] = $offset;
325 if (strlen($whereOrderBy)) {
326 if (!preg_match('/^[ \n\r]*AND/i', $whereOrderBy)) {
327 if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i', $whereOrderBy)) {
328 $whereOrderBy = 'AND ' . $whereOrderBy;
333 if(!empty($table->_belongsTo[$name])) {
334 $obj = $table->_belongsTo[$name];
335 $columnName = $obj->foreignKey;
336 if(empty($this->$columnName)) {
337 $this->$name = null;
339 else {
340 if ($obj->parentKey) {
341 $key = $obj->parentKey;
343 else {
344 $key = reset($table->keys);
347 $arrayOfOne = $obj->Find($key.'='.$this->$columnName.' '.$whereOrderBy,false,false,$extras);
348 if ($arrayOfOne) {
349 $this->$name = $arrayOfOne[0];
350 return $arrayOfOne[0];
354 if(!empty($table->_hasMany[$name])) {
355 $obj = $table->_hasMany[$name];
356 $key = reset($table->keys);
357 $id = @$this->$key;
358 if (!is_numeric($id)) {
359 $db = $this->DB();
360 $id = $db->qstr($id);
362 $objs = $obj->Find($obj->foreignKey.'='.$id. ' '.$whereOrderBy,false,false,$extras);
363 if (!$objs) {
364 $objs = array();
366 $this->$name = $objs;
367 return $objs;
370 return array();
372 //////////////////////////////////
374 // update metadata
375 function UpdateActiveTable($pkeys=false,$forceUpdate=false)
377 global $_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
378 global $ADODB_ACTIVE_DEFVALS,$ADODB_FETCH_MODE;
380 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
382 $table = $this->_table;
383 $tables = $activedb->tables;
384 $tableat = $this->_tableat;
385 if (!$forceUpdate && !empty($tables[$tableat])) {
387 $acttab = $tables[$tableat];
388 foreach($acttab->flds as $name => $fld) {
389 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) {
390 $this->$name = $fld->default_value;
392 else {
393 $this->$name = null;
396 return;
398 $db = $activedb->db;
399 $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
400 if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
401 $fp = fopen($fname,'r');
402 @flock($fp, LOCK_SH);
403 $acttab = unserialize(fread($fp,100000));
404 fclose($fp);
405 if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) {
406 // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
407 // ideally, you should cache at least 32 secs
409 foreach($acttab->flds as $name => $fld) {
410 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) {
411 $this->$name = $fld->default_value;
413 else {
414 $this->$name = null;
418 $activedb->tables[$table] = $acttab;
420 //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
421 return;
422 } else if ($db->debug) {
423 ADOConnection::outp("Refreshing cached active record file: $fname");
426 $activetab = new ADODB_Active_Table();
427 $activetab->name = $table;
429 $save = $ADODB_FETCH_MODE;
430 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
431 if ($db->fetchMode !== false) {
432 $savem = $db->SetFetchMode(false);
435 $cols = $db->MetaColumns($table);
437 if (isset($savem)) {
438 $db->SetFetchMode($savem);
440 $ADODB_FETCH_MODE = $save;
442 if (!$cols) {
443 $this->Error("Invalid table name: $table",'UpdateActiveTable');
444 return false;
446 $fld = reset($cols);
447 if (!$pkeys) {
448 if (isset($fld->primary_key)) {
449 $pkeys = array();
450 foreach($cols as $name => $fld) {
451 if (!empty($fld->primary_key)) {
452 $pkeys[] = $name;
455 } else
456 $pkeys = $this->GetPrimaryKeys($db, $table);
458 if (empty($pkeys)) {
459 $this->Error("No primary key found for table $table",'UpdateActiveTable');
460 return false;
463 $attr = array();
464 $keys = array();
466 switch (ADODB_ASSOC_CASE) {
467 case ADODB_ASSOC_CASE_LOWER:
468 foreach($cols as $name => $fldobj) {
469 $name = strtolower($name);
470 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
471 $this->$name = $fldobj->default_value;
473 else {
474 $this->$name = null;
476 $attr[$name] = $fldobj;
478 foreach($pkeys as $k => $name) {
479 $keys[strtolower($name)] = strtolower($name);
481 break;
483 case ADODB_ASSOC_CASE_UPPER:
484 foreach($cols as $name => $fldobj) {
485 $name = strtoupper($name);
487 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
488 $this->$name = $fldobj->default_value;
490 else {
491 $this->$name = null;
493 $attr[$name] = $fldobj;
496 foreach($pkeys as $k => $name) {
497 $keys[strtoupper($name)] = strtoupper($name);
499 break;
500 default:
501 foreach($cols as $name => $fldobj) {
502 $name = ($fldobj->name);
504 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
505 $this->$name = $fldobj->default_value;
507 else {
508 $this->$name = null;
510 $attr[$name] = $fldobj;
512 foreach($pkeys as $k => $name) {
513 $keys[$name] = $cols[$name]->name;
515 break;
518 $activetab->keys = $keys;
519 $activetab->flds = $attr;
521 if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
522 $activetab->_created = time();
523 $s = serialize($activetab);
524 if (!function_exists('adodb_write_file')) {
525 include(ADODB_DIR.'/adodb-csvlib.inc.php');
527 adodb_write_file($fname,$s);
529 if (isset($activedb->tables[$table])) {
530 $oldtab = $activedb->tables[$table];
532 if ($oldtab) {
533 $activetab->_belongsTo = $oldtab->_belongsTo;
534 $activetab->_hasMany = $oldtab->_hasMany;
537 $activedb->tables[$table] = $activetab;
540 function GetPrimaryKeys(&$db, $table)
542 return $db->MetaPrimaryKeys($table);
545 // error handler for both PHP4+5.
546 function Error($err,$fn)
548 global $_ADODB_ACTIVE_DBS;
550 $fn = get_class($this).'::'.$fn;
551 $this->_lasterr = $fn.': '.$err;
553 if ($this->_dbat < 0) {
554 $db = false;
556 else {
557 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
558 $db = $activedb->db;
561 if (function_exists('adodb_throw')) {
562 if (!$db) {
563 adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
565 else {
566 adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
568 } else {
569 if (!$db || $db->debug) {
570 ADOConnection::outp($this->_lasterr);
576 // return last error message
577 function ErrorMsg()
579 if (!function_exists('adodb_throw')) {
580 if ($this->_dbat < 0) {
581 $db = false;
583 else {
584 $db = $this->DB();
587 // last error could be database error too
588 if ($db && $db->ErrorMsg()) {
589 return $db->ErrorMsg();
592 return $this->_lasterr;
595 function ErrorNo()
597 if ($this->_dbat < 0) {
598 return -9999; // no database connection...
600 $db = $this->DB();
602 return (int) $db->ErrorNo();
606 // retrieve ADOConnection from _ADODB_Active_DBs
607 function DB()
609 global $_ADODB_ACTIVE_DBS;
611 if ($this->_dbat < 0) {
612 $false = false;
613 $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
614 return $false;
616 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
617 $db = $activedb->db;
618 return $db;
621 // retrieve ADODB_Active_Table
622 function &TableInfo()
624 global $_ADODB_ACTIVE_DBS;
625 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
626 $table = $activedb->tables[$this->_tableat];
627 return $table;
631 // I have an ON INSERT trigger on a table that sets other columns in the table.
632 // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
633 function Reload()
635 $db = $this->DB();
636 if (!$db) {
637 return false;
639 $table = $this->TableInfo();
640 $where = $this->GenWhere($db, $table);
641 return($this->Load($where));
645 // set a numeric array (using natural table field ordering) as object properties
646 function Set(&$row)
648 global $ACTIVE_RECORD_SAFETY;
650 $db = $this->DB();
652 if (!$row) {
653 $this->_saved = false;
654 return false;
657 $this->_saved = true;
659 $table = $this->TableInfo();
660 if ($ACTIVE_RECORD_SAFETY && sizeof($table->flds) != sizeof($row)) {
661 # <AP>
662 $bad_size = TRUE;
663 if (sizeof($row) == 2 * sizeof($table->flds)) {
664 // Only keep string keys
665 $keys = array_filter(array_keys($row), 'is_string');
666 if (sizeof($keys) == sizeof($table->flds)) {
667 $bad_size = FALSE;
670 if ($bad_size) {
671 $this->Error("Table structure of $this->_table has changed","Load");
672 return false;
674 # </AP>
676 else
677 $keys = array_keys($row);
679 # <AP>
680 reset($keys);
681 $this->_original = array();
682 foreach($table->flds as $name=>$fld) {
683 $value = $row[current($keys)];
684 $this->$name = $value;
685 $this->_original[] = $value;
686 next($keys);
689 # </AP>
690 return true;
693 // get last inserted id for INSERT
694 function LastInsertID(&$db,$fieldname)
696 if ($db->hasInsertID) {
697 $val = $db->Insert_ID($this->_table,$fieldname);
699 else {
700 $val = false;
703 if (is_null($val) || $val === false) {
704 // this might not work reliably in multi-user environment
705 return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
707 return $val;
710 // quote data in where clause
711 function doquote(&$db, $val,$t)
713 switch($t) {
714 case 'L':
715 if (strpos($db->databaseType,'postgres') !== false) {
716 return $db->qstr($val);
718 case 'D':
719 case 'T':
720 if (empty($val)) {
721 return 'null';
723 case 'B':
724 case 'N':
725 case 'C':
726 case 'X':
727 if (is_null($val)) {
728 return 'null';
731 if (strlen($val)>0 &&
732 (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")
734 return $db->qstr($val);
735 break;
737 default:
738 return $val;
739 break;
743 // generate where clause for an UPDATE/SELECT
744 function GenWhere(&$db, &$table)
746 $keys = $table->keys;
747 $parr = array();
749 foreach($keys as $k) {
750 $f = $table->flds[$k];
751 if ($f) {
752 $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
755 return implode(' and ', $parr);
759 function _QName($n,$db=false)
761 if (!ADODB_Active_Record::$_quoteNames) {
762 return $n;
764 if (!$db) {
765 $db = $this->DB();
766 if (!$db) {
767 return false;
770 return $db->nameQuote.$n.$db->nameQuote;
773 //------------------------------------------------------------ Public functions below
775 function Load($where=null,$bindarr=false, $lock = false)
777 global $ADODB_FETCH_MODE;
779 $db = $this->DB();
780 if (!$db) {
781 return false;
783 $this->_where = $where;
785 $save = $ADODB_FETCH_MODE;
786 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
787 if ($db->fetchMode !== false) {
788 $savem = $db->SetFetchMode(false);
791 $qry = "select * from ".$this->_table;
793 if($where) {
794 $qry .= ' WHERE '.$where;
796 if ($lock) {
797 $qry .= $this->lockMode;
800 $row = $db->GetRow($qry,$bindarr);
802 if (isset($savem)) {
803 $db->SetFetchMode($savem);
805 $ADODB_FETCH_MODE = $save;
807 return $this->Set($row);
810 function LoadLocked($where=null, $bindarr=false)
812 $this->Load($where,$bindarr,true);
815 # useful for multiple record inserts
816 # see http://phplens.com/lens/lensforum/msgs.php?id=17795
817 function Reset()
819 $this->_where=null;
820 $this->_saved = false;
821 $this->_lasterr = false;
822 $this->_original = false;
823 $vars=get_object_vars($this);
824 foreach($vars as $k=>$v){
825 if(substr($k,0,1)!=='_'){
826 $this->{$k}=null;
829 $this->foreignName=strtolower(get_class($this));
830 return true;
833 // false on error
834 function Save()
836 if ($this->_saved) {
837 $ok = $this->Update();
839 else {
840 $ok = $this->Insert();
843 return $ok;
847 // false on error
848 function Insert()
850 $db = $this->DB();
851 if (!$db) {
852 return false;
854 $cnt = 0;
855 $table = $this->TableInfo();
857 $valarr = array();
858 $names = array();
859 $valstr = array();
861 foreach($table->flds as $name=>$fld) {
862 $val = $this->$name;
863 if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) {
864 $valarr[] = $val;
865 $names[] = $this->_QName($name,$db);
866 $valstr[] = $db->Param($cnt);
867 $cnt += 1;
871 if (empty($names)){
872 foreach($table->flds as $name=>$fld) {
873 $valarr[] = null;
874 $names[] = $name;
875 $valstr[] = $db->Param($cnt);
876 $cnt += 1;
879 $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
880 $ok = $db->Execute($sql,$valarr);
882 if ($ok) {
883 $this->_saved = true;
884 $autoinc = false;
885 foreach($table->keys as $k) {
886 if (is_null($this->$k)) {
887 $autoinc = true;
888 break;
891 if ($autoinc && sizeof($table->keys) == 1) {
892 $k = reset($table->keys);
893 $this->$k = $this->LastInsertID($db,$k);
897 $this->_original = $valarr;
898 return !empty($ok);
901 function Delete()
903 $db = $this->DB();
904 if (!$db) {
905 return false;
907 $table = $this->TableInfo();
909 $where = $this->GenWhere($db,$table);
910 $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
911 $ok = $db->Execute($sql);
913 return $ok ? true : false;
916 // returns an array of active record objects
917 function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
919 $db = $this->DB();
920 if (!$db || empty($this->_table)) {
921 return false;
923 $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra);
924 return $arr;
927 // returns 0 on error, 1 on update, 2 on insert
928 function Replace()
930 $db = $this->DB();
931 if (!$db) {
932 return false;
934 $table = $this->TableInfo();
936 $pkey = $table->keys;
938 foreach($table->flds as $name=>$fld) {
939 $val = $this->$name;
941 if (is_null($val)) {
942 if (isset($fld->not_null) && $fld->not_null) {
943 if (isset($fld->default_value) && strlen($fld->default_value)) {
944 continue;
946 else {
947 $this->Error("Cannot update null into $name","Replace");
948 return false;
952 if (is_null($val) && !empty($fld->auto_increment)) {
953 continue;
956 if (is_array($val)) {
957 continue;
960 $t = $db->MetaType($fld->type);
961 $arr[$name] = $this->doquote($db,$val,$t);
962 $valarr[] = $val;
965 if (!is_array($pkey)) {
966 $pkey = array($pkey);
969 switch (ADODB_ASSOC_CASE) {
970 case ADODB_ASSOC_CASE_LOWER:
971 foreach ($pkey as $k => $v) {
972 $pkey[$k] = strtolower($v);
974 break;
975 case ADODB_ASSOC_CASE_UPPER:
976 foreach ($pkey as $k => $v) {
977 $pkey[$k] = strtoupper($v);
979 break;
982 $ok = $db->Replace($this->_table,$arr,$pkey);
983 if ($ok) {
984 $this->_saved = true; // 1= update 2=insert
985 if ($ok == 2) {
986 $autoinc = false;
987 foreach($table->keys as $k) {
988 if (is_null($this->$k)) {
989 $autoinc = true;
990 break;
993 if ($autoinc && sizeof($table->keys) == 1) {
994 $k = reset($table->keys);
995 $this->$k = $this->LastInsertID($db,$k);
999 $this->_original = $valarr;
1001 return $ok;
1004 // returns 0 on error, 1 on update, -1 if no change in data (no update)
1005 function Update()
1007 $db = $this->DB();
1008 if (!$db) {
1009 return false;
1011 $table = $this->TableInfo();
1013 $where = $this->GenWhere($db, $table);
1015 if (!$where) {
1016 $this->error("Where missing for table $table", "Update");
1017 return false;
1019 $valarr = array();
1020 $neworig = array();
1021 $pairs = array();
1022 $i = -1;
1023 $cnt = 0;
1024 foreach($table->flds as $name=>$fld) {
1025 $i += 1;
1026 $val = $this->$name;
1027 $neworig[] = $val;
1029 if (isset($table->keys[$name]) || is_array($val)) {
1030 continue;
1033 if (is_null($val)) {
1034 if (isset($fld->not_null) && $fld->not_null) {
1035 if (isset($fld->default_value) && strlen($fld->default_value)) {
1036 continue;
1038 else {
1039 $this->Error("Cannot set field $name to NULL","Update");
1040 return false;
1045 if (isset($this->_original[$i]) && strcmp($val,$this->_original[$i]) == 0) {
1046 continue;
1049 if (is_null($this->_original[$i]) && is_null($val)) {
1050 continue;
1053 $valarr[] = $val;
1054 $pairs[] = $this->_QName($name,$db).'='.$db->Param($cnt);
1055 $cnt += 1;
1059 if (!$cnt) {
1060 return -1;
1063 $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
1064 $ok = $db->Execute($sql,$valarr);
1065 if ($ok) {
1066 $this->_original = $neworig;
1067 return 1;
1069 return 0;
1072 function GetAttributeNames()
1074 $table = $this->TableInfo();
1075 if (!$table) {
1076 return false;
1078 return array_keys($table->flds);
1083 function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr,
1084 $extra)
1086 global $_ADODB_ACTIVE_DBS;
1089 $save = $db->SetFetchMode(ADODB_FETCH_NUM);
1090 $qry = "select * from ".$table;
1092 if (!empty($whereOrderBy)) {
1093 $qry .= ' WHERE '.$whereOrderBy;
1095 if(isset($extra['limit'])) {
1096 $rows = false;
1097 if(isset($extra['offset'])) {
1098 $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset'],$bindarr);
1099 } else {
1100 $rs = $db->SelectLimit($qry, $extra['limit'],-1,$bindarr);
1102 if ($rs) {
1103 while (!$rs->EOF) {
1104 $rows[] = $rs->fields;
1105 $rs->MoveNext();
1108 } else
1109 $rows = $db->GetAll($qry,$bindarr);
1111 $db->SetFetchMode($save);
1113 $false = false;
1115 if ($rows === false) {
1116 return $false;
1120 if (!class_exists($class)) {
1121 $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
1122 return $false;
1124 $arr = array();
1125 // arrRef will be the structure that knows about our objects.
1126 // It is an associative array.
1127 // We will, however, return arr, preserving regular 0.. order so that
1128 // obj[0] can be used by app developpers.
1129 $arrRef = array();
1130 $bTos = array(); // Will store belongTo's indices if any
1131 foreach($rows as $row) {
1133 $obj = new $class($table,$primkeyArr,$db);
1134 if ($obj->ErrorNo()){
1135 $db->_errorMsg = $obj->ErrorMsg();
1136 return $false;
1138 $obj->Set($row);
1139 $arr[] = $obj;
1140 } // foreach($rows as $row)
1142 return $arr;