摘要:同樣的,添加屬性,修改函數(shù)構(gòu)造語句的方法基類中添加方法檢測有沒有設(shè)置子句構(gòu)建語句參數(shù)綁定返回影響的行數(shù)更新數(shù)據(jù)示例相比,語句更為簡單,只需子句即可。
查詢語句 (DQL) 的構(gòu)造功能開發(fā)完畢,我們再給查詢構(gòu)造器增加一些對 DML (Data Manipulation Language) 語句的支持,如簡單的 insert、update、delete 操作。
insert我們先回顧下 PDO 原生的 insert 操作怎么進行:
// 預(yù)編譯 $pdoSt = $pdo->prepare("INSERT INTO test_table ("username", "age") VALUES (:username, :age);"); // 綁定參數(shù) $pdoSt->bindValue(":username", "Jack", PDO::PARAM_STR) $pdoSt->bindValue(":age", 18, PDO::PARAM_INT) // 執(zhí)行 $pdoSt->execute(); // 獲取執(zhí)行數(shù)據(jù) $count = $pdoSt->rowCount(); // 返回被影響的行數(shù) $lastId = $pdo->lastInsertId(); // 獲取最后插入行的 ID
數(shù)據(jù)插入
和查詢語句的執(zhí)行過程并沒有太大差別,只是語法不同?;叵氲诙?,我們新建了 _buildQuery() 方法去構(gòu)造最終的 SQL,對于 insert,我們在基類新建 _buildInsert() 方法:
protected function _buildInsert() { $this->_prepare_sql = "INSERT INTO ".$this->_table.$this->_insert_str; }
基類添加 _insert_str 屬性:
protected $_insert_str = "";
修改 _reset() 函數(shù),將 _insert_str 屬性的初始化過程添加進去:
protected function _reset() { $this->_table = ""; $this->_prepare_sql = ""; $this->_cols_str = " * "; $this->_where_str = ""; $this->_orderby_str = ""; $this->_groupby_str = ""; $this->_having_str = ""; $this->_join_str = ""; $this->_limit_str = ""; $this->_insert_str = ""; // 重置 insert 語句 $this->_bind_params = []; }
基類中添加 insert() 方法:
public function insert(array $data) { // 構(gòu)建字符串 $field_str = ""; $value_str = ""; foreach ($data as $key => $value) { $field_str .= " ".self::_wrapRow($key).","; $plh = self::_getPlh(); // 生產(chǎn)占位符 $this->_bind_params[$plh] = $value; //保存綁定數(shù)據(jù) $value_str .= " ".$plh.","; } // 清除右側(cè)多余的逗號 $field_str = rtrim($field_str, ","); $value_str = rtrim($value_str, ","); $this->_insert_str = " (".$field_str.") VALUES (".$value_str.") "; // 構(gòu)造 insert 語句 $this->_buildInsert(); // 執(zhí)行 $this->_execute(); // 獲取影響的行數(shù) return $this->_pdoSt->rowCount(); }
對上述代碼,我們申明了 insert() 方法的參數(shù)是一個鍵值數(shù)組,用來傳入要插入的字段、值映射。默認返回被影響的行數(shù) (比較通用)。
測試
試著插入一條數(shù)據(jù)吧:
$insert_data = [ "username" => "jack", "age" => 18, ]; $results = $driver->table("test_table")->insert($insert_data);
獲取最后插入行的 ID
當一個表中有自增 id 且為主鍵時,這個 id 可以被看作區(qū)分數(shù)據(jù)的唯一標識。而在插入一條數(shù)據(jù)后獲取這條新增數(shù)據(jù)的 id 也是常見的業(yè)務(wù)需求。
PDO 提供了一個簡單的獲取最后插入行的 ID 的方法 PDO::lastInsertId() 供我們使用。
基類添加 insertGetLastId() 方法:
public function insertGetLastId(array $data) { $this->insert($data); return $this->_pdo->lastInsertId(); }
測試:
$insert_data = [ "username" => "jack", "age" => 18, ]; $lastId = $driver->table("test_table")->insertGetLastId($insert_data);
個體差異
然而,上述的 insertGetLastId() 方法在 PostgreSql 中并不奏效。PostgreSql 中,使用 PDO::lastInsertId() 獲取結(jié)果需要傳入正確的自增序列名 (PostgreSQL 中創(chuàng)建表時,如果使用 serial 類型,默認生成的自增序列名為:表名 + + 字段名 + + seq)?!?】
但是這個方式并不好用,因為訪問 insertGetLastId() 方法時必須手動傳入這個序列名稱,這樣 insertGetLastId() 方法對底層的依賴嚴重,比如當?shù)讓域?qū)動從 postgresql 換到 mysql 時,需要更改上層應(yīng)用。而我們希望無論是 mysql 還是 postgresql,上層應(yīng)用調(diào)用 insertGetLastId() 方法時是無差別的,即底層對上層透明。
為了解決這個問題,就需要用到 postgresql 的 returning 語法了。postgresql 中 insert、update 和 delete 操作都有一個可選的 returning 子句,可以指定最后執(zhí)行的字段進行返回,返回的數(shù)據(jù)可以像 select 一樣取結(jié)果。【2】
對于我們返回最后插入行的 ID 的需求,只需 returning id 就好。
當然,基類的 insertGetLastId() 方法對于 postgresql 而言已經(jīng)無效了,我們在 Pgsql 驅(qū)動類中重寫 insertGetLastId() 方法:
public function insertGetLastId(array $data) { // 構(gòu)建語句字符串、綁定數(shù)據(jù) $field_str = ""; $value_str = ""; foreach ($data as $key => $value) { $field_str .= " ".self::_wrapRow($key).","; $plh = self::_getPlh(); $this->_bind_params[$plh] = $value; $value_str .= " ".$plh.","; } $field_str = rtrim($field_str, ","); $value_str = rtrim($value_str, ","); // 使用 returning 子句返回 id $this->_insert_str = " (".$field_str.") VALUES (".$value_str.") RETURNING id "; // execute $this->_buildInsert(); $this->_execute(); // 使用 returning 子句后,可以像使用 SELECT 一樣獲取一個 returning 指定字段的結(jié)果集。 $result = $this->_pdoSt->fetch(PDO::FETCH_ASSOC); // 返回 id return $result["id"]; }
OK,我們再來測試看看,是不是就好用了呢?
update做完 insert,update 就很簡單了,不同的是為了防止全局更新的失誤發(fā)生,update 構(gòu)造時強行要求使用 where 子句。
同樣的,添加 _update_str 屬性,修改 _reset() 函數(shù):
protected $_update_str = ""; ... protected function _reset() { $this->_table = ""; $this->_prepare_sql = ""; $this->_cols_str = " * "; $this->_where_str = ""; $this->_orderby_str = ""; $this->_groupby_str = ""; $this->_having_str = ""; $this->_join_str = ""; $this->_limit_str = ""; $this->_insert_str = ""; $this->_update_str = ""; $this->_bind_params = []; }
構(gòu)造 update 語句的方法:
protected function _buildUpdate() { $this->_prepare_sql = "UPDATE ".$this->_table.$this->_update_str.$this->_where_str; }
基類中添加 update() 方法:
public function update(array $data) { // 檢測有沒有設(shè)置 where 子句 if(empty($this->_where_str)) { throw new InvalidArgumentException("Need where condition"); } // 構(gòu)建語句、參數(shù)綁定 $this->_update_str = " SET "; foreach ($data as $key => $value) { $plh = self::_getPlh(); $this->_bind_params[$plh] = $value; $this->_update_str .= " ".self::_wrapRow($key)." = ".$plh.","; } $this->_update_str = rtrim($this->_update_str, ","); $this->_buildUpdate(); $this->_execute(); // 返回影響的行數(shù) return $this->_pdoSt->rowCount(); }
更新數(shù)據(jù)示例:
$update_data = [ "username" => "lucy", "age" => 22, ]; $results = $driver->table("test_table") ->where("username", "jack") ->update($update_data);delete
相比 insert、update,delete 語句更為簡單,只需 where 子句即可。和 update 一樣,需要防止誤操作刪除所有數(shù)據(jù)。
構(gòu)造 delete 語句的方法:
protected function _buildDelete() { $this->_prepare_sql = "DELETE FROM ".$this->_table.$this->_where_str; }
基類中添加 delete() 方法:
public function delete() { // 檢測有沒有設(shè)置 where 子句 if(empty($this->_where_str)) { throw new InvalidArgumentException("Need where condition"); } $this->_buildDelete(); $this->_execute(); return $this->_pdoSt->rowCount(); }
刪除數(shù)據(jù)示例:
$results = $driver->table("test_table") ->where("age", 18) ->delete();事務(wù)
既然有了 DML 操作,那么就少不了事務(wù)。對于事務(wù),我們可以直接使用 PDO 提供的 PDO::beginTransaction()、PDO::commit()、PDO::rollBack()、PDO::inTransaction() 方法來實現(xiàn)。
基類添加 beginTrans() 方法:
// 開始事務(wù) public function beginTrans() { try { return $this->_pdo->beginTransaction(); } catch (PDOException $e) { // 斷線重連 if ($this->_isTimeout($e)) { $this->_closeConnection(); $this->_connect(); try { return $this->_pdo->beginTransaction(); } catch (PDOException $e) { throw $e; } } else { throw $e; } } }
注:因為 PDO::beginTransaction() 也是和 PDO::prepare() 一樣會連接數(shù)據(jù)庫的方法,所以需要做斷線重連的操作。
commitTrans() 方法:
// 提交事務(wù) public function commitTrans() { return $this->_pdo->commit(); }
rollBackTrans() 方法:
// 回滾事務(wù) public function rollBackTrans() { if ($this->_pdo->inTransaction()) { // 如果已經(jīng)開始了事務(wù),則運行回滾操作 return $this->_pdo->rollBack(); } }
事務(wù)使用示例:
// 注冊事務(wù) $driver->beginTrans(); $results = $driver->table("test_table") ->where("age", 18) ->delete(); $driver->commitTrans(); // 確認刪除 // 回滾事務(wù) $driver->beginTrans(); $results = $driver->table("test_table") ->where("age", 18) ->delete(); $driver->rollBackTrans(); // 撤銷刪除參考
【1】PHP Manual - PDO::lastInsertId
【2】PostgreSQL Documentation - Returning Data From Modified Rows
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/30824.html
摘要:而在項目開發(fā)中,我們想要的是一個更好用的可維護的工具,此時,對代碼的封裝模塊化就顯得尤為重要,于是出現(xiàn)了兩種方案查詢構(gòu)造器,對象關(guān)系映射。典型環(huán)境下按照一般的查詢構(gòu)造器處理就行。 文章目錄 寫一個特殊的查詢構(gòu)造器 - (前言) 寫一個特殊的查詢構(gòu)造器 - (一、程序結(jié)構(gòu),基礎(chǔ)封裝) 寫一個特殊的查詢構(gòu)造器 - (二、第一條語句) 寫一個特殊的查詢構(gòu)造器 - (三、條件查詢) 寫一個特殊...
閱讀 2668·2021-11-24 10:44
閱讀 1928·2021-11-22 13:53
閱讀 1952·2021-09-30 09:47
閱讀 3713·2021-09-22 16:00
閱讀 2444·2021-09-08 09:36
閱讀 2323·2019-08-30 15:53
閱讀 2798·2019-08-30 15:48
閱讀 996·2019-08-30 15:44