/** * The generic DB access Class, Entry of all DB Access * Only PG is supported -- 201210 * * @author Anthony * 2010-2012 reserved */ class DB { // Query types const SELECT = 1; const INSERT = 2; const UPDATE = 3; const DELETE = 4; /** * True Value */ const T = 't'; /** * False Value */ const F = 'f'; /** * Null Value */ const N = 'N/A'; //NULL Value /** * Specilize the value; * 'f' as False, 't' as TRUE, 'N/A' as NULL value * * @param String $s, Orignal Value * * @return String, specilized value */ public static function specializeValue($s){ if($s === self::N){ return NULL; } if($s === self::T){ return True; } if($s === self::F){ return False; } return $s; } /** * Batch insert into table * @param String $table_name Table Name * @param Array $cols columns of table * @param Array $values, values array of data * @param String/Array $return_cols Return column[s], 'id' is return by default,Array for multi Column * @param String $db Instance name of DB Connection * * @return Resultset return result set of return_cols */ public static function insert_batch($table_name,$cols,$values,$return_cols='id',$db='default'){ $_sql = 'insert into '.self::quote_table($table_name,$db).'('.self::quote_column($cols,$db).') values '; $_vsql = array(); foreach ($values as $value){ $_vsql[] = '('.self::quote($value).')'; } $_sql .= implode(',',$_vsql); $_sql .= ' returning '.self::quote_column($return_cols); return self::query(self::SELECT,$_sql)->execute($db)->as_array(); } /** * Insert into table from Array Data, and return column[s], ID is return by default * * @param String $table_name Table Name * @param Array $data Array Data Of key value pairs. * @param String/Array $return_cols Return column[s], 'id' is return by default,Array for multi Column * @param String $db Instance name of DB Connection * * @return Boolean/Resultset True if success without return column, False if failed, value of column[s] if return_cols presented. */ public static function insert_table($table_name,$data,$return_cols='id',$db='default'){ if (!is_array($data)){ return false; } if (is_null($return_cols)){ $_sql = 'insert into '.self::quote_table($table_name,$db).'('.self::quote_column(array_keys($data),$db).') values ('. self::quote(array_values($data),$db).')'; return self::query(self::INSERT,$_sql)->execute($db); } //Specialize value $data = array_map('self::specializeValue',$data); if (is_string($return_cols)){ $_sql = 'insert into '.self::quote_table($table_name,$db).'('.self::quote_column(array_keys($data),$db).') values ('. self::quote(array_values($data),$db).')'." returning ".$return_cols; $id = self::query(self::SELECT,$_sql)->execute($db)->get($return_cols); return $id; }else{ if (is_array($return_cols)){ $ids = implode(',',$return_cols); $_sql = 'insert into '.self::quote_table($table_name,$db).'('.self::quote_column(array_keys($data),$db).') values ('. self::quote(array_values($data),$db).')'." returning ".$ids; $r_ids = self::query(self::SELECT,$_sql)->execute($db)->current(); return $r_ids; } } return false; } /** * Update Table data, and compare with reference data * * @param String $table_name Table Name * @param Integer $id ID of data * @param Array $data Array Data Of key value pairs. * @param Array $refdata Reference data * @param String $id_name Column name of ID * @param String $db Instance name of DB Connection * * @return Integer Affected Rows,False if failed! */ public static function update_data($table_name,$id,$data,$refdata,$id_name='id',$db='default'){ if (!is_array($data)){ throw new exception('Data should be col=>val pairs array'); } foreach($data as $k => $v){ if(is_array($refdata)){ if(isset($refdata[$k])){ if($v == $refdata[$k]){ unset($data[$k]); } } }elseif(is_object($refdata)){ if(isset($refdata->$k)){ if($v == $refdata->$k){ unset($data[$k]); } } }else{ throw new exception('refdata type error'); } } //Specialize value $data = array_map('self::specializeValue',$data); if(count($data)>0){ return self::update_table($table_name,$id,$data,'id',$db); }else{ return 0; } } /** * Update table with data without checking the referenced Data * * @param String $table_name Table Name * @param Integer $id ID of data * @param Array $data Array Data Of key value pairs. * @param String $id_name Column name of ID * @param String $db Instance name of DB Connection * * @return Integer Affected Rows,False if failed! */ public static function update_table($table_name,$id,$data,$id_name='id',$db='default'){ if (!is_array($data)){ return false; } $_sql = 'update '.self::quote_table($table_name,$db).' set '.self::quote_assoicate($data,'=',',',$db).' where '. self::quote_column($id_name,$db).'='.self::quote($id,$db); return self::query(self::UPDATE,$_sql)->execute($db); } /** * quote key value pair of col => values * * @param Array $data, col=>value pairs * @param String $concat, default '=' * @param String Delimiter, default ',' * @param String Database instance * * @return String */ public static function quote_assoicate($data,$concat='=',$delimiter=',',$db='default'){ $_sql = ''; $_sqlArray = array(); foreach ($data as $k => $v){ $_sqlArray[] = self::quote_column($k,$db).$concat.self::quote($v,$db); } $_sql = implode($delimiter,$_sqlArray); return $_sql; } /** * Quote cols * * @param String $value, The column[s] name * @param String $db, Database Instance Name */ public static function quote_column($value,$db='default'){ if(!is_array($value)){ return self::quote_identifier($value,$db); }else{ //quote_column array and implode $_qs = array(); foreach ($value as $ele){ $_qs[] = self::quote_column($ele,$db); } $_quote_column_String = implode(',',$_qs); return $_quote_column_String; } } /** * Quote the values to escape * * @param Scalar/Array $value * * @return quote string or array */ public static function quote($value,$db='default'){ if(!is_array($value)){ return Database::instance($db)->quote($value); }else{ //Quote array and implode $_qs = array(); foreach ($value as $ele){ $_qs[] = self::quote($ele,$db); } $_quoteString = implode(',',$_qs); return $_quoteString; } } /** * Escape string of DB * * @param string $s table name * @param String $db Database instance name * * @return String */ public static function escape($s,$db='default'){ return Database::instance($db)->escape($s); } /** * Quote Table name * * @param string $s table name * @param String $db Database instance name * * @return String */ public static function quote_table($s,$db='default'){ return Database::instance($db)->quote_table($s); } /** * Quote a database identifier, such as a column name. * * $column = DB::quote_identifier($column,'default'); * * You can also use SQL methods within identifiers. * * // The value of "column" will be quoted * $column = DB::quote_identifier('COUNT("column")'); * * Objects passed to this function will be converted to strings. * [Database_Query] objects will be compiled and converted to a sub-query. * All other objects will be converted using the '__toString' method. * * @param mixed $value any identifier * @param String $db, Database instance * @return string */ public static function quote_identifier($value,$db='default'){ return Database::instance($db)->quote_identifier($value); } /** * Get Connection for Database instance * * @param String $db Database Instance name * * @return Connection of Databse */ public static function getConnection($db = 'default'){ return Database::instance($db)->getConnection(); } /** * Get Children of current record * * @param String $table Table name * @param Bollean $returnSql * @param Integer $pid Parent Id of table record * @param String $idname ID column name * @param String $pidname Parent ID column name * @param String $db Database Instance name * * @return Records of Children */ public static function getChildren($table,$returnSql = false ,$pid= '0',$idname='id',$pidname='pid' ,$db='default'){ $_sql = 'select * from '.self::quote_table($table,$db).' where '.$pidname.'='.self::quote($pid,$db). " and $idname <>".self::quote($pid,$db); if($returnSql){ return $_sql; } $_res = self::query(self::SELECT,$_sql,true)->execute($db)->as_array(); if($_res){ return $_res; }else{ return false; } } /** * Tree query for connect by,traverse all the child records of Data * * @param String $tableName Tablename * @param Boolean $returnSql Return SQL String if TURE * @param String $startWith Begin valueof traverse * @param String $idCol ID Column name * @param String $pidCol Parent ID Column name * @param String $orderCol Order Column * @param Integer $maxDepth Depth of traverse, * @param Integer $level Start Level * @param String $delimiter Delimiter of branch * @param String $db Database configuration instance * * @return Record/String Return Record array or String of SQL */ public static function getTree($tableName,$returnSql=false,$startWith='0',$idCol='id',$pidCol='pid', $orderCol='id', $maxDepth=0,$level = 0,$delimiter = ';',$db='default'){ $_funcParas = array(); $_funcParas[] = self::quote($tableName,$db); //Table|View $_funcParas[] = self::quote($idCol,$db); //ID column $_funcParas[] = self::quote($pidCol,$db); //Parent ID Column $_funcParas[] = self::quote($orderCol,$db); //Default Order by ASC $_funcParas[] = self::quote($startWith,$db); //Begin ID $_funcParas[] = self::quote($maxDepth,$db); //Depth of traverse $_funcParas[] = self::quote($delimiter,$db); //Delimitor of Branch,default ';' $_sql = 'select * from connectby(' .implode(',',$_funcParas).')' .' as t(id int, pid int, level int, branch text, pos int)'; if($level > 0){ $_sql .= ' where level >='.self::quote($level,$db); } if($returnSql) return $_sql; $_res = self::query(self::SELECT,$_sql,true)->execute($db)->as_array(); if($_res){ return $_res; }else{ return false; } } /** * Start transaction * * @param String $db Instance name of DB * * @return Result set */ public static function begin($db='default'){ return DB::query(self::UPDATE, "BEGIN")->execute($db); } /** * Define Savepoint * * @param String $savepoint * * @param String $db */ public static function savepoint($savepoint, $db='default'){ return DB::query(self::UPDATE, "SAVEPOINT ".$savepoint)->execute($db); } /** * Rollback to Savepoint * * @param String $savepoint * * @param String $db Database Instance name */ public static function rollpoint($savepoint, $db='default'){ return DB::query(self::UPDATE, "ROLLBACK TO ".$savepoint)->execute($db); } /** * Commit an transaction * @param String DB connection */ public static function commit($db='default'){ return DB::query(self::UPDATE, "COMMIT")->execute($db); } public static function rollback($db='default'){ return DB::query(self::UPDATE, "ROLLBACK")->execute($db); } /** * Create a new [Database_Query] of the given type. * * // Create a new SELECT query * $query = DB::query(self::SELECT, 'SELECT * FROM users'); * * // Create a new DELETE query * $query = DB::query(self::DELETE, 'DELETE FROM users WHERE id = 5'); * * Specifying the type changes the returned result. When using * self::SELECT, a [Database_Query_Result] will be returned. * self::INSERT queries will return the insert id and number of rows. * For all other queries, the number of affected rows is returned. * * @param integer type: self::SELECT, self::UPDATE, etc * @param string SQL statement * @param Boolean $as_object Return Result set as Object if true, default FALSE * @param Array $params Query parameters of SQL, default array() * @param String $stmt_name The query is Prepared Statement if TRUE, * Execute Prepared Statement when $param is Not NULL * Prepare Statement when $param is NULL * * @return Database_Query */ public static function query($type, $sql = NULL ,$as_object = false,$params = array(),$stmt_name = NULL) { return new Database_Query($type, $sql,$as_object,$params,$stmt_name); } /** * Gettting paginated page from Orignal SQL * * @param String $sql SQL query * @param UTL Object &$page UTL object of tempalte * @param String $orderBy Order by column, default 'updated desc' * @param String $dataPro Data Property Name, default 'data' * @param String $pagePro Pagnation Frament property Name, default 'pagination' * @param Array $config Pagination Configuration Array overider * @param String $db Database Instance Name, default 'default' * @param Boolean $as_object Populate Data as Object if TRUE, default TRUE * @param String $_paginClass Class Name of pagination * @return True if success */ public static function getPage($_sql,&$page,$orderBy ='updated desc', $dataPro='data',$pagePro = 'pagination', $config = NULL,$db = 'default',$as_object= true,$_paginClass='Pagination'){ $_csql = 'select count(1) as c from ('.$_sql.') st'; $_c = DB::query(self::SELECT,$_csql)->execute($db)->get('c'); if($config){ $config['total_items'] = $_c; $_pagination = new $_paginClass($config); }else{ $config = array(); $config['total_items'] = $_c; $_pagination = new $_paginClass($config); } $_sql .= ' order by '.$orderBy; if($_pagination->offset){ $_sql .= ' offset '.$_pagination->offset; } $_sql .= ' limit '.$_pagination->items_per_page; $_data = DB::query(self::SELECT,$_sql,$as_object)->execute($db)->as_array(); if(!$_data){ $page->{$dataPro} = false; $page->{$pagePro} = false; return false; } $page->{$dataPro} = $_data; $page->{$pagePro} = $_pagination; return true; } /** * Get All roles of subordinate * * @param Integer $role_id Integer User Role ID * @param Boolean $quote Quote the SQL if ture, reutrn orignal SQL if false * @param String $role_table Table of role hierarchy * @param Integer $level Start Level of tree traverse * @param String $db Database Instance name * @return SQL String */ public static function getRoleTreeSql($role_id,$quote = false,$role_table,$level=0,$db='default'){ $_sql = 'select id from ('.self::getTree($role_table,true,$role_id,'id','pid','id', 0, //Maxdepth $level, //Level ';',$db).') utree'; if(!$quote) return $_sql; else return '('.$_sql.')'; } /** * Getting SQL String to query Objects of subordinate and owned objects * Child User Role Tree[CURT] * * @param integer $role_id Role ID of user * @param integer $user_id User ID * @param String $role_table Table of Role * @param Boolean $quote Quote the SQL if ture, reutrn orignal SQL if false * @param String $roleCol Role ID column name * @param String $ownerCol Owner ID column name * @param String $db Database instance name * @return SQL String */ public static function getCURTreeSql($role_id,$user_id,$role_table,$quote = true, $roleCol='role_id',$ownerCol = 'owner_id' ,$db='default'){ $_sql = ' '.$roleCol.' in '.self::getRoleTreeSql($role_id,true,$role_table, 1, //Level start with 1 $db). ' or '.$ownerCol.'='.self::quote($user_id,$db); if(!$quote) return $_sql; else return '('.$_sql.')'; } /** * Array from tree query to tree * * @param Array $eles , the record set from self::getTree * @param String $elename, element name of node * @param String $cldname, Child node name * @param String $delimiter, The delimiter of branch * * @return Object , Tree object of data */ public static function array2tree($eles,$elename,$cldname,$delimiter=';'){ if($elename == $cldname){ throw new Exception('Ele name equals cldname!'); } $rtree = array(); foreach ($eles as $ele){ $_branch = $ele->branch; //Log::debug('branch='.$_branch); //The depth in the array $_depths = explode($delimiter,$_branch); if(count($_depths == 1)){ $_root = $_depths[0]; } $_cur = &$rtree; foreach ($_depths as $depth){ //Create NODE if(!isset($_cur[$cldname])){ $_cur[$cldname] = array(); } if(!isset($_cur[$cldname][$depth])){ $_cur[$cldname][$depth] = array(); $_cur = &$_cur[$cldname][$depth]; }else{ $_cur = &$_cur[$cldname][$depth]; } } $_cur[$elename] = $ele; } return $rtree[$cldname][$_root]; } }