work on unifying old DB abstraction layer with new woc
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Wed, 4 Feb 2009 18:33:09 +0000 (18:33 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Wed, 4 Feb 2009 18:33:09 +0000 (18:33 +0000)
git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@264 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

wob/order.wolf
woc/phpout.cpp
woc/processor.cpp
woc/processor.h
www/inc/db/db.php
www/inc/db/db_mysql.php
www/inc/wbase/schema.php
www/inc/wbase/table.php

index a3449f0..b6a9194 100644 (file)
@@ -48,7 +48,7 @@
                <Foreign method="getTickets" via="ticket:orderid=orderid"/>
                <Foreign method="getVouchers" via="voucher:orderid=orderid"/>
        </Table>
-       <Table name="ticket" backup="yes">
+       <Table name="ticket" backup="yes" base="BarcodeTable">
                <!--a 8-32 char code (code39: case-insensitive letters+digits) for the ticket-->
                <Column name="ticketid" type="string:32" primarykey="yes"/>
                <Column name="eventid" type="int32" foreignkey="event:eventid"/>
@@ -58,7 +58,7 @@
                <Column name="status" type="int32" notnull="yes"/>
                <Column name="orderid" type="int32" foreignkey="order:orderid" notnull="yes"/>
        </Table>
-       <Table name="voucher" backup="yes">
+       <Table name="voucher" backup="yes" base="BarcodeTable">
                <!--a 8-32 char code (code39: case-insensitive letters+digits) for the voucher-->
                <Column name="voucherid" type="string:32" primarykey="yes"/>
                <!--price of the voucher (0 if cancelled)-->
index f48915f..c0677c6 100644 (file)
@@ -81,11 +81,12 @@ void WocPHPServerOut::newTable(const WocTable&tbl)
                return;
        }
        tf.write(PHPSTART);
-       QString code="class WT"+tbl.name()+" extends WobTable\n{\n";
+       QString code="class WT"+tbl.name()+" extends "+tbl.baseClass()+"\n{\n";
        //initializer
        code+="protected function __construct(array $data,$isfromdb){parent::__construct($data,$isfromdb,\""+tbl.name()+"\");}\n\n";
        
        //static get instance
+       QStringList cols=tbl.columns();
        QStringList pcols=tbl.primaryColumns();
        code+="public static function getFromDB(";
        for(int i=0;i<pcols.size();i++){
@@ -103,7 +104,28 @@ void WocPHPServerOut::newTable(const WocTable&tbl)
        code+="public static function selectFromDB($where){\n\tglobal "+dbi+";\n\t$res="+dbi+"->select(\""+tbl.name()+"\",\"*\",$where);\n\tif($res===false || count($res)<1)return array();\n\t";
        code+="$r=array();\n\tforeach($res as $row)\n\t\t$r[]=new WT"+tbl.name()+"($row,true);\n\treturn $r;\n}\n\n";
        
-       //TODO:automatic resolution of internal foreign keys
+       //go through columns, generate specific code
+       for(int i=0;i<cols.size();i++){
+               //automatic resolution of internal foreign keys
+               if(tbl.columnIsForeign(cols[i])){
+                       code+="public function get"+cols[i]+"(){\n\tglobal "+dbi+";\n\treturn WT";
+                       QStringList foreign=tbl.columnForeign(cols[i]).split(":");
+                       code+=foreign[0]+"::selectFromDB(\""+foreign[1]+"=\"."+dbi+"->escapeColumn(\""+foreign[0]+"\",\""+foreign[1]+"\",$this->"+cols[i]+"));\n}\n\n";
+               }
+               //implement enum check for set method of enum columns
+               if(tbl.columnType(cols[i]).startsWith("enum")){
+                       code+="private function verifyValue"+cols[i]+"($v){if(false";
+                       QList<QPair<QString,int> >ens=tbl.columnEnums(cols[i]);
+                       QList<int>envs;
+                       for(int j=0;j<ens.size();j++){
+                               int v=ens[j].second;
+                               if(envs.contains(v))continue;
+                               envs.append(v);
+                               code+="||$v=="+QString::number(v);
+                       }
+                       code+=")return true;else return false;}\n\n";
+               }
+       }
        
        //reverse resolution of configured foreign keys
        QStringList fs=tbl.foreigns();
@@ -121,7 +143,7 @@ void WocPHPServerOut::newTable(const WocTable&tbl)
                        qDebug("Warning: Foreign clause %s of table %s has illegal syntax. Should be foreigntable:column=localcolumn.",fs[i].toAscii().data(),tbl.name().toAscii().data());
                        continue;
                }
-               code+="public function "+fs[i]+"(){\n\treturn WT"+foreign[0]+"::selectFromDB(\""+foreign[1]+"=\"."+dbi+"->escapeColumn(\"";
+               code+="public function "+fs[i]+"(){\n\tglobal "+dbi+";\n\treturn WT"+foreign[0]+"::selectFromDB(\""+foreign[1]+"=\"."+dbi+"->escapeColumn(\"";
                code+=foreign[0]+"\",\""+foreign[1]+"\",$this->"+local+"));\n}\n\n";
        }
        
@@ -139,7 +161,6 @@ void WocPHPServerOut::newTable(const WocTable&tbl)
        
        //extend schema file
        //column definitions
-       QStringList cols=tbl.columns();
        code="\t$this->scheme[\""+tbl.name()+"\"]=array(";
        for(int i=0;i<cols.size();i++){
                if(i)code+=",";
index abde096..2b2de15 100644 (file)
@@ -265,6 +265,7 @@ WocTable::WocTable(const QDomElement&tbl)
                return;
        }
        m_backup=str2bool(tbl.attribute("backup","0"));
+       m_base=tbl.attribute("base","WocTable");
        qDebug("Info: parsing table %s",m_name.toAscii().data());
        QDomNodeList nl=tbl.elementsByTagName("Column");
        //Columns
@@ -449,6 +450,13 @@ bool WocTable::hasColumn(QString c)const
        return false;
 }
 
+QList<QPair<QString,int> > WocTable::columnEnums(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].enumvals;
+       return QList<QPair<QString,int> >();
+}
 
 QList<QPair<QString,int> > WocTable::getEnums()const
 {
index c522b22..4d5aff8 100644 (file)
@@ -40,6 +40,7 @@ class WocTable
                
                QString name()const{return m_name;}
                bool inBackup()const{return m_backup;}
+               QString baseClass()const{return m_base;}
                
                bool hasColumn(QString)const;
                QStringList columns()const;
@@ -53,6 +54,7 @@ class WocTable
                QString columnForeign(QString)const;
                bool columnIsIndexed(QString)const;
                bool columnIsUnique(QString)const;
+               QList<QPair<QString,int> > columnEnums(QString)const;
                
                QList<QPair<QString,int> > getEnums()const;
                
@@ -63,7 +65,7 @@ class WocTable
                QList<QMap<QString,QString> > presets()const{return m_presets;}
        private:
                bool m_valid,m_backup;
-               QString m_name;
+               QString m_name,m_base;
                struct s_col {
                        QString name,type,foreign,defaultval;
                        bool isnull,isprime,isindex,isunique;
index 9db7399..1b0c300 100644 (file)
@@ -43,7 +43,7 @@ abstract class DbEngine
        }
        
        /**returns whether the table exists; must be implemented by driver*/
-       public abstract function haveTable($tablename);
+       public abstract function hasTable($tablename);
        
        /**begins a transaction; must be implemented by driver; use sqlBeginTransaction to create the SQL statement!*/
        public abstract function beginTransaction();
@@ -60,7 +60,7 @@ abstract class DbEngine
        /**unlocks the database - only used by the backup functions; if necessary should also commit a transaction; default just commits*/
        protected function unlockDB(){$this->commitTransaction();}
        
-       /**gets some data from the database; $table is the name of the table, $cols is the list of columns to return or "*" for all, $where is the where clause of the SQL-statement, $orderby may contain additional ORDER BY or GROUP BY clauses; returns array of rows, which are in *_fetch_array format; returns false on error; use sqlSelect to create the SQL statement!*/
+       /**gets some data from the database; $table is the name of the table, $cols is the list of columns to return or "*" for all, $where is the where clause of the SQL-statement, $orderby may contain additional ORDER BY or GROUP BY clauses; returns array of rows, which are in *_fetch_array format; returns false on error; use sqlSelect to create the SQL statement!; make sure that NULL values are returned as PHP value null (most DB drivers already do this)*/
        public abstract function select($table,$cols,$where,$orderby="");
        
        /**insert values into a table; returns false on failure, the new primary key if a sequence was set, true otherwise; use sqlInsert to create the SQL statement!*/
@@ -272,35 +272,36 @@ abstract class DbEngine
                return "ROLLBACK TRANSACTION";
        }
        
-       /**escapes integers; the default implementation just makes sure it is an int*/
+       /**escapes integers; the default implementation just makes sure it is an int (false and null are translated to NULL)*/
        public function escapeInt($i)
        {
-               if($i === false)return "NULL";
+               if($i === false || $i === null)return "NULL";
                return round($i + 0);
        }
        
-       /**escapes strings; the default uses addslashes and encloses the value in ''; it is recommended to overwrite this with the proper escaping procedure for the target DB*/
+       /**escapes strings; the default uses addslashes and encloses the value in ''; it is recommended to overwrite this with the proper escaping procedure for the target DB (false and null are translated to NULL)*/
        public function escapeString($s)
        {
-               if($s === false) return "NULL";
+               if($s === false || $s === null) return "NULL";
                return "'".addslashes($s)."'";
        }
        
        /**escapes blobs; the default uses addslashes and encloses the value in ''; it is recommended to overwrite this with the proper escaping procedure for the target DB*/
        public function escapeBlob($s)
        {
-               if($s === false) return "NULL";
+               if($s === false || $s===null) return "NULL";
                return "'".addslashes($s)."'";
        }
        
-       /**escapes a boolean value; the default translates 0, '0', 'f', 'false', 'no' to FALSE and numerics !=0, 't', 'true', 'yes' to TRUE; any other value (incl. the PHP constant false) is translated to NULL (exception: the constant true is treated as 'true')*/
+       /**escapes a boolean value; the default translates 0, '0', 'f', 'false', 'n', 'no', false to FALSE and numerics !=0, 't', 'true', 'y', 'yes', true to TRUE; any other value (incl. the PHP constant null) is translated to NULL*/
        public function escapeBool($s)
        {
+               if($s===null)return "NULL";
                if(is_numeric($s)){
                        if(($s+0)!=0)return "TRUE";
                        else return "FALSE";
                }
-               if($s===false)return "NULL";
+               if($s===false)return "FALSE";
                if($s===true)return "TRUE";
                switch(strtolower($s)){
                        case "t":case "true":case "yes":case "y":
@@ -345,7 +346,7 @@ abstract class DbEngine
        /**tries to find out whether the connected DB version is usable*/
        public function canUseDb()
        {
-               if(!$this->haveTable("config"))
+               if(!$this->hasTable("config"))
                        return false;
                return $this->getConfig("MagicSmokeVersion")==$this->needVersion();
        }
@@ -397,10 +398,10 @@ abstract class DbEngine
        /**returns the error string of the last operation*/
        public abstract function lastError();
        
-       /**returns whether the result value is NULL; the default interprets both null and false as NULL; overwrite if the DB can return false for a boolean*/
+       /**returns whether the result value is NULL; the default interprets only the special value null as NULL*/
        public function isNull($val)
        {
-               if($val===false || $val===null)return true;
+               if($val===null)return true;
                else return false;
        }
        
@@ -454,13 +455,13 @@ abstract class DbEngine
        private function unescapeBackup($fmt,$val)
        {
                switch($fmt){
-                       case "NULL":return false;
+                       case "NULL":return null;
                        case "int":return $val+0;
                        case "str":return base64_decode($val);
                        case "bool":return $val+0;
                        default:
                                print("Warning: encountered unknown data encoding \"".htmlspecialchars($fmt)."\". Using NULL instead.<br>\n");
-                               return false;
+                               return null;
                }
        }
        
@@ -519,7 +520,7 @@ abstract class DbEngine
                                case "table":
                                        $data=array();
                                        $table=$cmd[1];
-                                       if(!$this->haveTable($table)){
+                                       if(!$this->hasTable($table)){
                                                print("Switching to non-existing table ".htmlspecialchars($table)." - ignoring data that follows.<br>\n");
                                                $table="";
                                        }else
index fac5f50..cda3b4b 100644 (file)
@@ -61,10 +61,10 @@ class MysqlEngine extends DbEngine
                        die("Cannot make this database transaction safe, aborting");
        }
        
-       public function haveTable($tnm)
+       public function hasTable($tnm)
        {
                global $dbScheme;
-               if(!$dbScheme->haveTable($tnm))return false;
+               if(!$dbScheme->hasTable($tnm))return false;
                $res=mysqli_query($this->dbhdl,"select * from ".$this->tableName($tnm)." where 1=2");
                if($res===false)return false;
                mysqli_free_result($res);
@@ -235,14 +235,16 @@ class MysqlEngine extends DbEngine
        /**escapes strings; it uses mysqli_escape_string and encloses the value in ''*/
        public function escapeString($s)
        {
-               if($s === false) return "NULL";
+               if($s === false||$s===null) return "NULL";
                return "'".mysqli_real_escape_string($this->dbhdl,$s)."'";
        }
 
        /**escapes blobs; it uses mysqli_escape_string and encloses the value in '' - blobs are binary strings in MySQL*/
        public function escapeBlob($s)
        {
-               if($s === false) return "NULL";
+               if($s === false||$s===null) return "NULL";
                return "'".mysqli_real_escape_string($this->dbhdl,$s)."'";
        }
-};
\ No newline at end of file
+};
+
+?>
\ No newline at end of file
index 7e74ee3..fb368ba 100644 (file)
@@ -20,7 +20,7 @@ class WobSchemaBase {
        }
        
        /**returns whether a table exists in the schema*/
-       public function haveTable($t)
+       public function hasTable($t)
        {
                return in_array($t,array_keys($this->scheme));
        }
@@ -51,6 +51,12 @@ class WobSchemaBase {
                return $r;
        }
        
+       /**return whether the table has this column*/
+       public function tableHasColumn($tab,$col)
+       {
+               return isset($this->scheme[$tab][$col]);
+       }
+       
        /**return default lines of the table for the initialization; returns empty array if there are none*/
        public function tableDefaults($tab)
        {
index afa6ef5..79070d2 100644 (file)
 //
 //
 
+class ValueOutOfRange extends Exception
+{
+       public function __construct($tab,$col,$val)
+       {
+               $this->message="Value of table '$tab' column '$col' is out of range. Value '$val' not allowed.";
+       }
+};
+
 class WobTable
 {
        private $data;
@@ -26,38 +34,79 @@ class WobTable
        
        public function __set($name,$value)
        {
+               global $db,$dbScheme;
                //verify name against scheme
-               //escape value
+               if(!$dbScheme->tableHasColumn($this->table,$name))return;
+               //verify enum vals
+               $vm="verifyValue".$name;
+               if(method_exists($this,$vm))
+                       if(!$t->$vm($value))
+                               throw ValueOutOfRange($this->table,$name,$value);
                //DB update (if from DB)
+               $succ=true;
+               if($this->isfromdb){
+                       $succ=$db->update($this->table,array($name => $value),$this->where());
+               }
                //if successful: store
+               if($succ)$this->data[$name]=$value;
+       }
+       
+       public function where()
+       {
+               global $dbScheme,$db;
+               $r="";
+               $pk=$dbScheme->primaryKeyColumns($this->table);
+               foreach($pk as $c){
+                       if($r!="")$r.=" AND ";
+                       $r.=$c."=".$db->escapeColumn($this->table,$c,$this->data[$c]);
+               }
+               return $r;
        }
        
        public function __get($name)
        {
                //verify name
-               //return value or false
+               //return value or null
+               if(isset($this->data[$name]))return $this->data[$name];
+               else return null;
        }
        
        public function __isset($name)
        {
+               global $db;
                //verify name and return true on existence AND notnull
+               if(isset($this->data[$name]))return !$db->isNull($this->data[$name]);
+               else return false;
        }
        
        public function __unset($name)
        {
+               global $dbScheme;
                //reset to null
+               if($dbScheme->tableHasColumn($name))$this->data[$name]=null;
        }
        
-       /**insert the object under a new primary key value into the DB (implicitly calls newKey)*/
+       /**insert the object under a new primary key value into the DB (implicitly calls newKey); returns true on success*/
        public function insert()
        {
+               //remove PK from object (so it is set to default)
+               global $dbScheme;
+               $this->isfromdb=false;
+               $pk=$dbScheme->primaryKeyColumns($this->table);
+               foreach($pk as $c)unset($this->data[$c]);
+               //optionally create new key
+               $this->newKey();
+               //now insert
+               $r=$db->insert($this->table,$this->data);
+               if($r===false)return false;
+               //TODO: make this a bit more safe:
+               if($r!==true)$this->data[$pk[0]]=$r;
+               return true;
        }
        
        /**generate a new primary key value for insert and marks the object as not yet in the DB; the default sets the primary key to NULL; call the original first if you overwrite it*/
        public function newKey()
        {
-               $this->isfromdb=false;
-               //get primary key columns and set them to false
        }
 };