2013-12-18 29 views
0

我已經創建了一個PDO上的數據庫抽象層,以避免圍繞我的腳本創建多個查詢,這很難維護。在一個請求中包含多個查詢的PDO數據庫抽象層

我的DBAL不是很寬;它負責簡單的任務,例如INSERTING,UPDATING和SELECTING(有或沒有加入)。它不包括更多先進的東西,如從多個表中選擇等。

與我的DBAL一起提出的問題是,當一個HTTP請求中存在更多相同類型時,它是混淆查詢。例如,腳本中有三個選擇語句,第一個是可用的,另外兩個不是。我試圖創建一個flush方法來清除查詢中以前填充的屬性,但它不起作用,我不知道該如何解決。我還沒有準備好擺脫我的課程,並重新開始編寫查詢 - 用這種方式編寫它們非常容易。

無論如何,這是我做一些查詢我的同班同學:

$insert_update_select = array(
    'COLUMNS' => array(
     'column_name1' => 'data_to_update_or_insert1', 
     'column_name2' => 'data_to_update_or_insert2' 
    ), 
    'WHERE' => array('x > y', 'y < x'), 
    'ORDER' => array('ASC' => 'column_name1'), 
    'LIMIT' => array(0, 5), 
); 

// This query works with updating, inserting and selecting 
$db = new db(); 

$db->insert('table_name', $insert_update_select); 
$db->update('table_name', $insert_update_select); 
$db->select('table_name', $insert_update_select); 

不要問我如何加入表;我其實忘了我自己的語法是如何工作的,哈哈。 (一定要嘗試記住)

不管怎麼說,這是我的課:

<?php 
class db 
{ 
    private $db_type = 'mysql'; 
    private $db_host = 'localhost'; 
    private $db_user = 'root'; 
    private $db_pass = ''; 
    private $db_name = 'imgzer'; 

    private $db; 
    private $db_connection  = ''; 
    private $insert_data  = ''; 
    private $update_data  = ''; 
    private $select_data  = ''; 
    private $condition_data  = ''; 
    private $order_data   = ''; 
    private $limit_data   = ''; 
    private $join_data   = array(); 
    private $query; 
    private $table; 

    private $return_data; 
    private $affected_rows; 
    private $return_id; 

    // Database tables 
    const USERS_TABLE = 'imgzer_users'; 
    const CONFIG_TABLE = 'imgzer_config'; 

    public function __construct() 
    { 
     $this->db_connection = "$this->db_type:host=$this->db_host;dbname=$this->db_name"; 

     $this->db = new PDO($this->db_connection, $this->db_user, $this->db_pass); 
     $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

     unset($this->db_pass); 
    } 

    public function open() 
    { 
     if ($this->db) 
     { 
      return true;  
     } 

     return false; 
    } 

    public function close() 
    { 
     if ($this->db->close()) 
     { 
      return true;  
     } 

     return false; 
    } 

    private function build_array($type, $data, $join_data = array()) 
    { 
     if (empty($data)) 
     { 
      return; 
     } 

     $type = strtoupper($type);    

     $this->update_data = ''; 
     $this->select_data = ''; 

     $data_index = 0; 
     $data_length = sizeof($data); 
     $last_row = $data_length - 1; 

     switch ($type) 
     { 
      case 'INSERT': 

       if (!is_array($data)) 
       { 
        return;  
       } 

       $this->insert_data = '('; 

       foreach ($data as $column => $value) 
       { 
        $this->insert_data .= $column . (($data_index != $last_row) ? ', ' : '');     
        $data_index++; 
       } 

       $data_index = 0; 

       $this->insert_data .= ') '; 
       $this->insert_data .= 'VALUES ('; 

       foreach ($data as $column => $value) 
       { 
        $this->insert_data .= '?' . (($data_index != $last_row) ? ', ' : ''); 
        $data_index++; 
       } 

       $this->insert_data .= ') '; 

      break; 

      case 'UPDATE': 

       $this->update_data = ''; 

       foreach ($data as $column => $value) 
       { 
        $this->update_data .= $column . ' = ?' . (($data_index != $last_row) ? ', ' : ''); 
        $data_index++; 
       } 

      break; 

      case 'SELECT': 

       if (empty($join_data)) 
       { 
        return; 
       } 

       if (is_array($join_data)) 
       { 
        $from_table = array_keys($join_data['FROM']); 
        $join_table = array_keys($join_data['TABLES']); 

        $this->select_data = implode(', ', array_flip($data)) . ' FROM ' ; 
        $this->select_data .= $from_table[0] . ' ' . $join_data['FROM'][$from_table[0]] . ' '; 

        for ($i = 0; $i < sizeof($this->join_data); $i++) 
        { 
         $this->select_data .= $this->get_join_type($join_data['JOIN']). ' '; 
         $this->select_data .= $join_table[$i] . ' ' . $join_data['TABLES'][$join_table[$i]]; 
         $this->select_data .= $this->join_data[$i]; 
        } 

        $this->select_data = rtrim($this->select_data, ' '); 
       } 
       else 
       { 
        if (!isset($data[0])) 
        { 
         $data = array_flip($data); 
        } 

        $this->select_data = implode(', ', $data) . ' FROM ' . $this->table . ' '; 
       } 

      break; 
     } 
    } 

    private function set_join($on) 
    { 
     if (empty($on)) 
     { 
      return; 
     } 

     if (is_array($on)) 
     { 
      for ($i = 0; $i < sizeof($on); $i++) 
      { 
       $on[$i] = ' ON (' . implode(' AND ', $on[$i]) . ') '; 
      } 
     } 

     $this->join_data = $on; 
    } 

    private function set_order($order) 
    { 
     if (empty($order)) 
     { 
      return; 
     } 

     $this->order_data = ' ORDER BY '; 

     if (is_array($order)) 
     { 
      $data_index = 0; 
      $data_size = sizeof($order) - 1; 

      foreach ($order as $order_type => $column) 
      { 
       if ($order_type != 'ASC' && $order_type != 'DESC') 
       { 
        throw new Exception('Order type in SQL has to be either ASC or DESC'); 
        return; 
       } 

       $this->order_data .= $column . ' ' . $order_type . (($data_index != $data_size) ? ', ' : ''); 
       $data_index++; 
      } 

      return; 
     } 

     $this->order_data .= $order; 
    } 

    private function set_limit($limit) 
    { 
     if (empty($limit)) 
     { 
      return; 
     } 

     if (sizeof($limit) > 2) 
     { 
      return; 
     } 

     if (sizeof($limit) == 1) 
     { 
      $limit = array(0, $limit[0]); 
     } 

     if (is_array($limit)) 
     { 
      $limit = implode(', ', $limit); 
     } 

     $this->limit_data = " LIMIT {$limit}"; 
    } 

    private function set_where($condition) 
    { 
     if (empty($condition)) 
     { 
      return; 
     } 

     if (is_array($condition)) 
     { 
      $condition = implode(' AND ', $condition); 
     } 

     $this->condition_data = " WHERE $condition"; 
    } 

    public function in_set($where_ary) 
    { 
     $where_str = implode(', ', $where_ary); 
     $where_str = substr($where_str, 0, -2); 

     $where_str = 'IN (' . $where_str . ')'; 

     return $where_str; 
    } 

    /* 
    * Example usage: 
    * $insert_ary = array('col_1' => 'col_data_1', 'col_2' => 'col_data_2'); 
    * $condition_ary = array('col_1 > 5', 'col_2 <> 10'); 
    * $order_ary = array('ASC' => 'col_1', 'DESC' => 'col_2'); 
    * $limit = array($start = 0, $limit = 5); 
    * $instance->insert('my_table', $insert_ary, $condition_ary, $order_ary, $limit); 
    */ 
    public function insert($table, $data, $return_id = false) 
    { 
     $data = $this->data_abstract($data); 

     // Prepare the arrays 
     $this->build_array('INSERT', $data['COLUMNS']); 
     $this->set_where($data['WHERE']); 
     $this->set_order($data['ORDER']); 
     $this->set_limit($data['LIMIT']); 

     $sql = 'INSERT INTO ' . $table . ' '; 
     $sql .= $this->insert_data; 
     $sql .= $this->condition_data; 
     $sql .= $this->order_data; 
     $sql .= $this->limit_data; 

     $this->query = $this->db->prepare($sql); 

     $param_index = 1; 

     foreach ($data['COLUMNS'] as $column => &$value) 
     {   
      $this->query->bindParam($param_index, $value); 
      $param_index++; 
     } 

     $this->query->execute(); 

     if ($return_id) 
     { 
      $this->return_id = $this->query->last_insert_id(); 
     } 
     else 
     { 
      $this->affected_rows = $this->query->rowCount(); 
     } 
    } 

    public function update($table, $data, $return_id = false) 
    { 
     $data = $this->data_abstract($data); 

     // Prepare the arrays 
     $this->build_array('UPDATE', $data['COLUMNS']); 
     $this->set_where($data['WHERE']); 
     $this->set_order($data['ORDER']); 
     $this->set_limit($data['LIMIT']); 

     $sql = 'UPDATE ' . $table . ' SET '; 
     $sql .= $this->update_data; 
     $sql .= $this->condition_data; 
     $sql .= $this->order_data; 
     $sql .= $this->limit_data; 

     $this->query = $this->db->prepare($sql); 

     $param_index = 1; 

     foreach ($data['COLUMNS'] as $column => &$value) 
     {   
      $this->query->bindParam($param_index, $value); 
      $param_index++; 
     } 

     $this->query->execute(); 

     if ($return_data) 
     { 
      $this->return_id = $this->query->last_insert_id(); 
     } 
     else 
     { 
      $this->affected_rows = $this->query->rowCount(); 
     } 
    } 

    /* 
    * Joining example: 
    * $join_data = array(
    * 'TABLES' => array('table_2' => 't2', 'table_3' => 't3'), 
    * 'JOIN'  => 'LEFT', 
    * 'ON'  => array(
    *     array('colx > 15', 'coly < 20'), 
    *     array('fieldx > 15', 'fieldy < 20') 
    *    ), 
    *); 
    */ 
    public function select($table, $data, $join = false, $fetch_type = 'assoc') 
    {  
     $data = $this->data_abstract($data); 

     if ($join) 
     { 
      if (!is_array($table)) 
      { 
       throw new Exception('Table has to be associated with a short index'); 
       return; 
      } 

      $this->set_join($join['ON']); 
      $table = array_merge(array('FROM' => $table), $join); 
     } 

     // Globalize table name if not joins are used 
     $this->table = $table; 

     // Prepare the arrays 
     $this->build_array('SELECT', $data['COLUMNS'], $table); 
     $this->set_where($data['WHERE']); 
     $this->set_order($data['ORDER']); 
     $this->set_limit($data['LIMIT']); 

     $sql = 'SELECT '; 
     $sql .= $this->select_data; 
     $sql .= $this->condition_data; 
     $sql .= $this->order_data; 
     $sql .= $this->limit_data; 

     $this->query = $this->db->prepare($sql);      
     $result = $this->query->execute(); 

     $fetch_type  = ($fetch_type == 'assoc') ? PDO::FETCH_ASSOC : PDO::FETCH_NUM; 
     $fetched_data = $this->query->fetchAll($fetch_type); 
     $data_result = $fetched_data; 

     if (sizeof($fetched_data) == 1) 
     { 
      $data_result = $fetched_data[0]; 
     } 

     $this->return_data = $data_result; 

     // Clear the result 
     //$this->query->closeCursor(); 
    } 

    public function fetch() 
    { 
     return $this->return_data; 
    } 

    public function affected_rows() 
    { 
     return $this->affected_rows; 
    } 

    private function data_abstract($data) 
    { 
     $abstract_ary = array('COLUMNS' => '', 'WHERE' => '', 'ORDER' => '', 'LIMIT' => 0); 
     return array_merge($abstract_ary, $data); 
    } 

    private function get_join_type($type) 
    { 
     switch ($type) 
     { 
      default: 
      case 'LEFT': 
       return 'LEFT JOIN'; 
      break; 
      case 'RIGHT': 
       return 'RIGHT JOIN'; 
      break; 
      case 'INNER': 
       return 'INNER JOIN'; 
      break; 
      case 'NORMAL': 
      case 'JOIN': 
       return 'JOIN'; 
      break; 
     } 
    } 

    private function flush() 
    { 
     unset($this->query, $this->insert_data, $this->update_data, $this->select_data); 
    } 
} 

$db = new db(); 
?> 

有什麼不妥的地方(可能是很多)和實際上,我怎麼讓它有效地工作?

+2

那裏有太多的代碼。你能選擇有問題的部分嗎? –

+0

我無法確定哪個部分有問題?我猜''select'方法將是首先的地方,因爲它不支持多個*返回*對於一個HTTP請求 – Aborted

回答

0

不要使它成爲有狀態。

即使沒有看代碼,我會告訴你什麼是問題:擺脫$this->stmt變量。
由於某些原因,所有的DBAL編寫者都對這樣一個變量有強烈的傾向......將狀態引入到他們的類中,從而使其無法使用。

所有方法調用都必須是原子,每個方法都執行所有必需的操作並返回所有請求的數據。在類變量中不保存任何內容。就如此容易。在這種極少數情況下,當PDOStatement對象必須進一步使用 - 返回這個非常對象,不要將其保存在裏面。否則,只需返回請求的數據。

我也建議擺脫你的整個DBAL,因爲它寫的是好意,但我可以肯定地告訴你,實現變得不那麼有用,但它實際上會讓你的代碼在許多方面變得更糟 - 可讀性,靈活性,可維護性。爲了追求虛擬可用性,您只需從SQL中節省一兩句話,但卻使整個應用程序代碼不可靠。

雖然你不會聽我的。在維護應用程序方面的一些經驗是需要看到我的觀點。

+0

請建議一種替代方法來簡化查詢寫入過程而不使用ORM或類似的東西。那裏有一個基於PDO的好DBAL嗎? – Aborted

+2

由於PDO已經是一個,所以在PDO上創建一個* DBAL *是沒有什麼意義的。你之後叫做* query builder *。您可以在該術語中找到大量示例。儘管你實際上也不需要這個。我不明白所有新手程序員強烈希望擺脫寶貴的SQL。 –

+0

接受你的答案,因爲它包括非常有用(和誠實)的建議。我已經擺脫了我的課,並開始再次使用查詢。我其實不是一個新手程序員,我只是沒有足夠的時間成爲一名專業人員。 – Aborted