source: trunk/phpgwapi/inc/adodb/adodb-active-record.inc.php @ 34

Revision 34, 15.0 KB checked in by niltonneto, 17 years ago (diff)

Versão nova do ADODB (4.5 para 4.95)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1<?php
2/*
3
4@version V4.94 23 Jan 2007  (c) 2000-2007 John Lim (jlim#natsoft.com.my). All rights reserved.
5  Latest version is available at http://adodb.sourceforge.net
6 
7  Released under both BSD license and Lesser GPL library license.
8  Whenever there is any discrepancy between the two licenses,
9  the BSD license will take precedence.
10 
11  Active Record implementation. Superset of Zend Framework's.
12 
13  Version 0.07
14 
15  See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord
16        for info on Ruby on Rails Active Record implementation
17*/
18
19global $_ADODB_ACTIVE_DBS;
20global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
21global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
22
23// array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
24$_ADODB_ACTIVE_DBS = array();
25$ACTIVE_RECORD_SAFETY = true;
26
27class ADODB_Active_DB {
28        var $db; // ADOConnection
29        var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
30}
31
32class ADODB_Active_Table {
33        var $name; // table name
34        var $flds; // assoc array of adofieldobjs, indexed by fieldname
35        var $keys; // assoc array of primary keys, indexed by fieldname
36        var $_created; // only used when stored as a cached file
37}
38
39// returns index into $_ADODB_ACTIVE_DBS
40function ADODB_SetDatabaseAdapter(&$db)
41{
42        global $_ADODB_ACTIVE_DBS;
43       
44                foreach($_ADODB_ACTIVE_DBS as $k => $d) {
45                        if (PHP_VERSION >= 5) {
46                                if ($d->db === $db) return $k;
47                        } else {
48                                if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database)
49                                        return $k;
50                        }
51                }
52               
53                $obj = new ADODB_Active_DB();
54                $obj->db =& $db;
55                $obj->tables = array();
56               
57                $_ADODB_ACTIVE_DBS[] = $obj;
58               
59                return sizeof($_ADODB_ACTIVE_DBS)-1;
60}
61
62
63class ADODB_Active_Record {
64        var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
65        var $_table; // tablename, if set in class definition then use it as table name
66        var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
67        var $_where; // where clause set in Load()
68        var $_saved = false; // indicates whether data is already inserted.
69        var $_lasterr = false; // last error message
70        var $_original = false; // the original values loaded or inserted, refreshed on update
71       
72        // should be static
73        function SetDatabaseAdapter(&$db)
74        {
75                return ADODB_SetDatabaseAdapter($db);
76        }
77       
78        // php4 constructor
79        function ADODB_Active_Record($table = false, $pkeyarr=false, $db=false)
80        {
81                ADODB_Active_Record::__construct($table,$pkeyarr,$db);
82        }
83       
84        // php5 constructor
85        function __construct($table = false, $pkeyarr=false, $db=false)
86        {
87        global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS;
88       
89                if ($db == false && is_object($pkeyarr)) {
90                        $db = $pkeyarr;
91                        $pkeyarr = false;
92                }
93               
94                if (!$table) {
95                        if (!empty($this->_table)) $table = $this->_table;
96                        else $table = $this->_pluralize(get_class($this));
97                }
98                if ($db) {
99                        $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
100                } else
101                        $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
102               
103               
104                if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
105               
106                $this->_table = $table;
107                $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
108                $this->UpdateActiveTable($pkeyarr);
109        }
110       
111        function __wakeup()
112        {
113                $class = get_class($this);
114                new $class;
115        }
116       
117        function _pluralize($table)
118        {
119                $ut = strtoupper($table);
120                $len = strlen($table);
121                $lastc = $ut[$len-1];
122                $lastc2 = substr($ut,$len-2);
123                switch ($lastc) {
124                case 'S':
125                        return $table.'es';     
126                case 'Y':
127                        return substr($table,0,$len-1).'ies';
128                case 'X':       
129                        return $table.'es';
130                case 'H':
131                        if ($lastc2 == 'CH' || $lastc2 == 'SH')
132                                return $table.'es';
133                default:
134                        return $table.'s';
135                }
136        }
137       
138        //////////////////////////////////
139       
140        // update metadata
141        function UpdateActiveTable($pkeys=false,$forceUpdate=false)
142        {
143        global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
144       
145                $activedb =& $_ADODB_ACTIVE_DBS[$this->_dbat];
146
147                $table = $this->_table;
148                $tables = $activedb->tables;
149                $tableat = $this->_tableat;
150                if (!$forceUpdate && !empty($tables[$tableat])) {
151                        $tobj =& $tables[$tableat];
152                        foreach($tobj->flds as $name => $fld)
153                                $this->$name = null;
154                        return;
155                }
156               
157                $db =& $activedb->db;
158                $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
159                if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
160                        $fp = fopen($fname,'r');
161                        @flock($fp, LOCK_SH);
162                        $acttab = unserialize(fread($fp,100000));
163                        fclose($fp);
164                        if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) {
165                                // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
166                                // ideally, you should cache at least 32 secs
167                                $activedb->tables[$table] = $acttab;
168                               
169                                //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
170                                return;
171                        } else if ($db->debug) {
172                                ADOConnection::outp("Refreshing cached active record file: $fname");
173                        }
174                }
175                $activetab = new ADODB_Active_Table();
176                $activetab->name = $table;
177               
178               
179                $cols = $db->MetaColumns($table);
180                if (!$cols) {
181                        $this->Error("Invalid table name: $table",'UpdateActiveTable');
182                        return false;
183                }
184                $fld = reset($cols);
185                if (!$pkeys) {
186                        if (isset($fld->primary_key)) {
187                                $pkeys = array();
188                                foreach($cols as $name => $fld) {
189                                        if (!empty($fld->primary_key)) $pkeys[] = $name;
190                                }
191                        } else 
192                                $pkeys = $this->GetPrimaryKeys($db, $table);
193                }
194                if (empty($pkeys)) {
195                        $this->Error("No primary key found for table $table",'UpdateActiveTable');
196                        return false;
197                }
198               
199                $attr = array();
200                $keys = array();
201               
202                switch($ADODB_ASSOC_CASE) {
203                case 0:
204                        foreach($cols as $name => $fldobj) {
205                                $name = strtolower($name);
206                                $this->$name = null;
207                                $attr[$name] = $fldobj;
208                        }
209                        foreach($pkeys as $k => $name) {
210                                $keys[strtolower($name)] = strtolower($name);
211                        }
212                        break;
213                       
214                case 1:
215                        foreach($cols as $name => $fldobj) {
216                                $name = strtoupper($name);
217                                $this->$name = null;
218                                $attr[$name] = $fldobj;
219                        }
220                       
221                        foreach($pkeys as $k => $name) {
222                                $keys[strtoupper($name)] = strtoupper($name);
223                        }
224                        break;
225                default:
226                        foreach($cols as $name => $fldobj) {
227                                $name = ($fldobj->name);
228                                $this->$name = null;
229                                $attr[$name] = $fldobj;
230                        }
231                        foreach($pkeys as $k => $name) {
232                                $keys[$name] = $cols[$name]->name;
233                        }
234                        break;
235                }
236               
237                $activetab->keys = $keys;
238                $activetab->flds = $attr;
239
240                if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
241                        $activetab->_created = time();
242                        $s = serialize($activetab);
243                        if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php');
244                        adodb_write_file($fname,$s);
245                }
246                $activedb->tables[$table] = $activetab;
247        }
248       
249        function GetPrimaryKeys(&$db, $table)
250        {
251                return $db->MetaPrimaryKeys($table);
252        }
253       
254        // error handler for both PHP4+5.
255        function Error($err,$fn)
256        {
257        global $_ADODB_ACTIVE_DBS;
258       
259                $fn = get_class($this).'::'.$fn;
260                $this->_lasterr = $fn.': '.$err;
261               
262                if ($this->_dbat < 0) $db = false;
263                else {
264                        $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
265                        $db =& $activedb->db;
266                }
267               
268                if (function_exists('adodb_throw')) {   
269                        if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
270                        else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
271                } else
272                        if (!$db || $db->debug) ADOConnection::outp($this->_lasterr);
273               
274        }
275       
276        // return last error message
277        function ErrorMsg()
278        {
279                if (!function_exists('adodb_throw')) {
280                        if ($this->_dbat < 0) $db = false;
281                        else $db = $this->DB();
282               
283                        // last error could be database error too
284                        if ($db && $db->ErrorMsg()) return $db->ErrorMsg();
285                }
286                return $this->_lasterr;
287        }
288       
289        function ErrorNo()
290        {
291                if ($this->_dbat < 0) return -9999; // no database connection...
292                $db = $this->DB();
293               
294                return (int) $db->ErrorNo();
295        }
296
297
298        // retrieve ADOConnection from _ADODB_Active_DBs
299        function &DB()
300        {
301        global $_ADODB_ACTIVE_DBS;
302       
303                if ($this->_dbat < 0) {
304                        $false = false;
305                        $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
306                        return $false;
307                }
308                $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
309                $db =& $activedb->db;
310                return $db;
311        }
312       
313        // retrieve ADODB_Active_Table
314        function &TableInfo()
315        {
316        global $_ADODB_ACTIVE_DBS;
317       
318                $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
319                $table =& $activedb->tables[$this->_tableat];
320                return $table;
321        }
322       
323        // set a numeric array (using natural table field ordering) as object properties
324        function Set(&$row)
325        {
326        global $ACTIVE_RECORD_SAFETY;
327       
328                $db =& $this->DB();
329               
330                if (!$row) {
331                        $this->_saved = false;         
332                        return false;
333                }
334               
335                $this->_saved = true;
336               
337                $table =& $this->TableInfo();
338                if ($ACTIVE_RECORD_SAFETY && sizeof($table->flds) != sizeof($row)) {
339                        $this->Error("Table structure of $this->_table has changed","Load");
340                        return false;
341                }
342               
343                $cnt = 0;
344                foreach($table->flds as $name=>$fld) {
345                        $this->$name = $row[$cnt];
346                        $cnt += 1;
347                }
348                $this->_original = $row;
349                return true;
350        }
351       
352        // get last inserted id for INSERT
353        function LastInsertID(&$db,$fieldname)
354        {
355                if ($db->hasInsertID)
356                        $val = $db->Insert_ID($this->_table,$fieldname);
357                else
358                        $val = false;
359                       
360                if (is_null($val) || $val === false) {
361                        // this might not work reliably in multi-user environment
362                        return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
363                }
364                return $val;
365        }
366       
367        // quote data in where clause
368        function doquote(&$db, $val,$t)
369        {
370                switch($t) {
371                case 'D':
372                case 'T':
373                        if (empty($val)) return 'null';
374                       
375                case 'C':
376                case 'X':
377                        if (is_null($val)) return 'null';
378                       
379                        if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") {
380                                return $db->qstr($val);
381                                break;
382                        }
383                default:
384                        return $val;
385                        break;
386                }
387        }
388       
389        // generate where clause for an UPDATE/SELECT
390        function GenWhere(&$db, &$table)
391        {
392                $keys = $table->keys;
393                $parr = array();
394               
395                foreach($keys as $k) {
396                        $f = $table->flds[$k];
397                        if ($f) {
398                                $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
399                        }
400                }
401                return implode(' and ', $parr);
402        }
403       
404       
405        //------------------------------------------------------------ Public functions below
406       
407        function Load($where,$bindarr=false)
408        {
409                $db =& $this->DB(); if (!$db) return false;
410                $this->_where = $where;
411               
412                $save = $db->SetFetchMode(ADODB_FETCH_NUM);
413                $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr);
414                $db->SetFetchMode($save);
415               
416                return $this->Set($row);
417        }
418       
419        // false on error
420        function Save()
421        {
422                if ($this->_saved) $ok = $this->Update();
423                else $ok = $this->Insert();
424               
425                return $ok;
426        }
427       
428        // false on error
429        function Insert()
430        {
431                $db =& $this->DB(); if (!$db) return false;
432                $cnt = 0;
433                $table =& $this->TableInfo();
434               
435                $valarr = array();
436                $names = array();
437                $valstr = array();
438
439                foreach($table->flds as $name=>$fld) {
440                        $val = $this->$name;
441                        if(!is_null($val) || !array_key_exists($name, $table->keys)) {
442                                $valarr[] = $val;
443                                $names[] = $name;
444                                $valstr[] = $db->Param($cnt);
445                                $cnt += 1;
446                        }
447                }
448               
449                if (empty($names)){
450                        foreach($table->flds as $name=>$fld) {
451                                $valarr[] = null;
452                                $names[] = $name;
453                                $valstr[] = $db->Param($cnt);
454                                $cnt += 1;
455                        }
456                }
457                $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
458                $ok = $db->Execute($sql,$valarr);
459               
460                if ($ok) {
461                        $this->_saved = true;
462                        $autoinc = false;
463                        foreach($table->keys as $k) {
464                                if (is_null($this->$k)) {
465                                        $autoinc = true;
466                                        break;
467                                }
468                        }
469                        if ($autoinc && sizeof($table->keys) == 1) {
470                                $k = reset($table->keys);
471                                $this->$k = $this->LastInsertID($db,$k);
472                        }
473                }
474               
475                $this->_original = $valarr;
476                return !empty($ok);
477        }
478       
479        function Delete()
480        {
481                $db =& $this->DB(); if (!$db) return false;
482                $table =& $this->TableInfo();
483               
484                $where = $this->GenWhere($db,$table);
485                $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
486                $ok = $db->Execute($sql);
487               
488                return $ok ? true : false;
489        }
490       
491        // returns an array of active record objects
492        function &Find($whereOrderBy,$bindarr=false,$pkeysArr=false)
493        {
494                $db =& $this->DB(); if (!$db || empty($this->_table)) return false;
495                $arr =& $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr);
496                return $arr;
497        }
498       
499        // returns 0 on error, 1 on update, 2 on insert
500        function Replace()
501        {
502        global $ADODB_ASSOC_CASE;
503               
504                $db =& $this->DB(); if (!$db) return false;
505                $table =& $this->TableInfo();
506               
507                $pkey = $table->keys;
508               
509                foreach($table->flds as $name=>$fld) {
510                        $val = $this->$name;
511                        /*
512                        if (is_null($val)) {
513                                if (isset($fld->not_null) && $fld->not_null) {
514                                        if (isset($fld->default_value) && strlen($fld->default_value)) continue;
515                                        else {
516                                                $this->Error("Cannot update null into $name","Replace");
517                                                return false;
518                                        }
519                                }
520                        }*/
521                        if (is_null($val) && !empty($fld->auto_increment)) {
522                continue;
523            }
524                        $t = $db->MetaType($fld->type);
525                        $arr[$name] = $this->doquote($db,$val,$t);
526                        $valarr[] = $val;
527                }
528               
529                if (!is_array($pkey)) $pkey = array($pkey);
530               
531               
532                if ($ADODB_ASSOC_CASE == 0)
533                        foreach($pkey as $k => $v)
534                                $pkey[$k] = strtolower($v);
535                elseif ($ADODB_ASSOC_CASE == 0)
536                        foreach($pkey as $k => $v)
537                                $pkey[$k] = strtoupper($v);
538                               
539                $ok = $db->Replace($this->_table,$arr,$pkey);
540                if ($ok) {
541                        $this->_saved = true; // 1= update 2=insert
542                        if ($ok == 2) {
543                                $autoinc = false;
544                                foreach($table->keys as $k) {
545                                        if (is_null($this->$k)) {
546                                                $autoinc = true;
547                                                break;
548                                        }
549                                }
550                                if ($autoinc && sizeof($table->keys) == 1) {
551                                        $k = reset($table->keys);
552                                        $this->$k = $this->LastInsertID($db,$k);
553                                }
554                        }
555                       
556                        $this->_original =& $valarr;
557                }
558                return $ok;
559        }
560
561        // returns 0 on error, 1 on update, -1 if no change in data (no update)
562        function Update()
563        {
564                $db =& $this->DB(); if (!$db) return false;
565                $table =& $this->TableInfo();
566               
567                $where = $this->GenWhere($db, $table);
568               
569                if (!$where) {
570                        $this->error("Where missing for table $table", "Update");
571                        return false;
572                }
573                $valarr = array();
574                $neworig = array();
575                $pairs = array();
576                $i = -1;
577                $cnt = 0;
578                foreach($table->flds as $name=>$fld) {
579                        $i += 1;
580                        $val = $this->$name;
581                        $neworig[] = $val;
582                       
583                        if (isset($table->keys[$name])) {
584                                continue;
585                        }
586                       
587                        if (is_null($val)) {
588                                if (isset($fld->not_null) && $fld->not_null) {
589                                        if (isset($fld->default_value) && strlen($fld->default_value)) continue;
590                                        else {
591                                                $this->Error("Cannot set field $name to NULL","Update");
592                                                return false;
593                                        }
594                                }
595                        }
596                       
597                        if (isset($this->_original[$i]) && $val == $this->_original[$i]) {
598                                continue;
599                        }                       
600                        $valarr[] = $val;
601                        $pairs[] = $name.'='.$db->Param($cnt);
602                        $cnt += 1;
603                }
604               
605               
606                if (!$cnt) return -1;
607                $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
608                $ok = $db->Execute($sql,$valarr);
609                if ($ok) {
610                        $this->_original =& $neworig;
611                        return 1;
612                }
613                return 0;
614        }
615       
616        function GetAttributeNames()
617        {
618                $table =& $this->TableInfo();
619                if (!$table) return false;
620                return array_keys($table->flds);
621        }
622       
623};
624
625?>
Note: See TracBrowser for help on using the repository browser.