摘要:雖然現(xiàn)在這樣的情況已經(jīng)很少,但是對于查詢構造器而言,還是要提供一個方便的方法來對表前綴進行設置,特別是當你沒有權限修改表名的時候。所以我們將表前綴作為一個配置參數(shù)傳入查詢構造器,在查詢構造器的底層進行自動前綴添加。
關聯(lián)查詢是關系型數(shù)據(jù)庫典型的查詢語句,根據(jù)兩個或多個表中的列之間的關系,從這些表中查詢數(shù)據(jù)。在 SQL 標準中使用 JOIN 和 ON 關鍵字來實現(xiàn)關聯(lián)查詢。
Join 子句join 子句的構造并不難,注意事項就是關聯(lián)查詢的注意事項:
寫對語法和關聯(lián)的條件
使用 table.field 模式防止字段重名
基類中新建 join() 方法:
// $table 要關聯(lián)的表 // $one 作為關聯(lián)條件的一個表的字段 // $two 作為關聯(lián)條件的另一個表的字段 // $type 關聯(lián)模式 inner、left、right public function join($table, $one, $two, $type = "INNER") { // 判斷模式是否合法 if( ! in_array($type, ["INNER", "LEFT", "RIGHT"])) { throw new InvalidArgumentException("Error join mode"); } // 構建 join 子句字符串 $this->_join_str .= " ".$type." JOIN ".self::_wrapRow($table). " ON ".self::_wrapRow($one)." = ".self::_wrapRow($two); return $this; }
leftJoin() 和 rightJoin() 方法:
public function leftJoin($table, $one, $two) { return $this->join($table, $one, $two, "LEFT"); } public function rightJoin($table, $one, $two) { return $this->join($table, $one, $two, "RIGHT"); }
注:Sqlite 是不支持 right join 的,所以 rightJoin() 方法在 Sqlite 驅動類中無效。
構建 SELECT student.name, class.name FROM student INNER JOIN class ON student.class_id = class.id;:
$results = $driver->table("student") ->select("student.name", "class.name") ->join("class", "student.class_id", "class.id") ->get();表前綴 為什么要有表前綴
以前很多數(shù)據(jù)表放在一個數(shù)據(jù)庫中的時候,需要表前綴來區(qū)分功能。雖然現(xiàn)在這樣的情況已經(jīng)很少,但是對于查詢構造器而言,還是要提供一個方便的方法來對表前綴進行設置,特別是當你沒有權限修改表名的時候。
自動添加表前綴的方法對于有表前綴的表,我們并不想每次都寫一個前綴,這樣會導致前綴更改后,應用層要跟著修改。所以我們將表前綴作為一個配置參數(shù)傳入查詢構造器,在查詢構造器的底層進行自動前綴添加。
表前綴的配置,假設表前綴為 "test_" :
// 以 mysql 為例 $config = [ "host" => "localhost", "port" => "3306", "user" => "username", "password" => "password", "dbname" => "dbname", "charset" => "utf8", "prefix" => "test_", "timezone" => "+8:00", "collection" => "utf8_general_ci", "strict" => false, // "unix_socket" => "/var/run/mysqld/mysqld.sock", ]; $db = new Mysql($config);
進行自動添加前綴的方法:
protected function _wrapTable($table) { // 構造函數(shù)傳入的配置中有前綴參數(shù)嗎? $prefix = array_key_exists("prefix", $this->_config) ? $this->_config["prefix"] : ""; // 拼接前綴 return $prefix.$table; }
修改 table() 方法:
public function table($table) { // 自動添加前綴 $this->_table = self::_wrapRow($this->_wrapTable($table)); return $this; }
join 子句中也涉及到表,所以修改 join() 方法:
public function join($table, $one, $two, $type = "INNER") { if( ! in_array($type, ["INNER", "LEFT", "RIGHT"])) { throw new InvalidArgumentException("Error join mode"); } // 添加表前綴 $table = $this->_wrapTable($table); $this->_join_str .= " ".$type." JOIN ".self::_wrapRow($table). " ON ".self::_wrapRow($one)." = ".self::_wrapRow($two); return $this; }table.field 模式的表前綴添加
增加了表前綴后,我們會發(fā)現(xiàn)一個問題:
使用 table()、join() 方法傳入的表可以自動的添加前綴,但是 table.field 格式中的表沒法自動添加前綴,如上面的 join("class", "student.class_id", "class.id"),我們總不能每次都寫成 join("class", "test_student.class_id", "test_class.id") 這種 (這樣的話和全部手工添加前綴沒什么兩樣),必須找到一個自動添加前綴的辦法。
觀察 table.field 模式,它出現(xiàn)的位置不定,可能在列、任何一個子句中出現(xiàn),所以在固定的位置去添加前綴是不大可能的。那么我們反過來想一下,如果在 SQL 已經(jīng)構造完成但還未執(zhí)行時,這時已經(jīng)知道有哪些地方使用了這種格式,去一一替換即可。那么如何知道有哪些地方使用了這種格式?
使用正則
我們用正則表達式找到 table.field 的 table 部分,給 table 加上表前綴即可 (這里不考慮跨庫查詢時三個點的情況)。
基類新增 _wrapPrepareSql() 方法:
// 替換 table.field 為 prefixtable.field protected function _wrapPrepareSql() { $quote = static::$_quote_symbol; $prefix_pattern = "/".$quote."([a-zA-Z0-9_]+)".$quote."(.)".$quote."([a-zA-Z0-9_]+)".$quote."/"; $prefix_replace = self::_quote($this->_wrapTable("$1"))."$2".self::_quote("$3"); $this->_prepare_sql = preg_replace($prefix_pattern, $prefix_replace, $this->_prepare_sql); }
修改 _execute() 方法:
protected function _execute() { try { // table.field 模式添加表前綴 $this->_wrapPrepareSql(); $this->_pdoSt = $this->_pdo->prepare($this->_prepare_sql); $this->_bindParams(); $this->_pdoSt->execute(); $this->_reset(); } catch (PDOException $e) { if($this->_isTimeout($e)) { $this->_closeConnection(); $this->_connect(); try { // table.field 模式添加表前綴 $this->_wrapPrepareSql(); $this->_pdoSt = $this->_pdo->prepare($this->_prepare_sql); $this->_bindParams(); $this->_pdoSt->execute(); $this->_reset(); } catch (PDOException $e) { throw $e; } } else { throw $e; } } }
最后我們進行一個完整的測試:
require_once dirname(dirname(__FILE__)) . "/vendor/autoload.php"; use DriversMysql; $config = [ "host" => "localhost", "port" => "3306", "user" => "username", "password" => "password", "dbname" => "database", "prefix" => "test_", "charset" => "utf8", "timezone" => "+8:00", "collection" => "utf8_general_ci", "strict" => false, ]; $driver = new Mysql($config); $results = $driver->table("student") ->select("student.name", "class.name") ->join("class", "student.class_id", "class.id") ->get(); var_dump($results);
試試看吧!
復雜語句的構造到目前位置,查詢相關的 SQL 構造方法基本開發(fā)完畢,我們進行一些復雜的 SQL 構造吧。
注:這里只是以我的測試環(huán)境舉例,大家可以按照自己的思路去建表
構造語句 SELECT * FROM t_user WHERE username = "Jackie aa" OR ( NOT EXISTS ( SELECT * FROM t_user WHERE username = "Jackie aa" ) AND (username = "Jackie Conroy" OR username = "Jammie Haag")) AND g_id IN ( SELECT id FROM t_user_group) ORDER BY id DESC LIMIT 1 OFFSET 0 :
$results = $driver->table("user") ->where("username", "Jackie aa") ->orWhereBrackets(function($query) { $query->whereNotExists(function($query) { $query->table("user")->where("username", "Jackie aa"); })->WhereBrackets(function($query) { $query->where("username", "Jackie Conroy") ->orWhere("username", "Jammie Haag"); }); }) ->whereInSub("g_id", function($query) { $query->table("user_group")->select("id"); }) ->orderBy("id", "DESC") ->limit(0, 1) ->get();
構造語句 SELECT t_user.username, t_user_group.groupname FROM t_user LEFT JOIN t_user_group ON t_user.g_id = t_user_group.id WHERE username = "Jackie aa" OR ( NOT EXISTS ( SELECT * FROM t_user WHERE username = "Jackie aa" ) AND username = "Jackie Conroy" ):
$results = $driver->table("user") ->select("user.username", "user_group.groupname") ->leftJoin("user_group", "user.g_id", "user_group.id") ->where("user.username", "Jackie aa") ->orWhereBrackets(function($query) { $query->whereNotExists(function($query) { $query->table("user")->where("username", "Jackie aa"); })->where("user.username", "Jackie Conroy"); }) ->get();
更多例子參考 WorkerF 單元測試 - PDODQL
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30825.html
摘要:而在項目開發(fā)中,我們想要的是一個更好用的可維護的工具,此時,對代碼的封裝模塊化就顯得尤為重要,于是出現(xiàn)了兩種方案查詢構造器,對象關系映射。典型環(huán)境下按照一般的查詢構造器處理就行。 文章目錄 寫一個特殊的查詢構造器 - (前言) 寫一個特殊的查詢構造器 - (一、程序結構,基礎封裝) 寫一個特殊的查詢構造器 - (二、第一條語句) 寫一個特殊的查詢構造器 - (三、條件查詢) 寫一個特殊...
摘要:注在常駐內(nèi)存單例模式下,這種多次用一個類進行查詢的情形很常見。斷線重連對于典型環(huán)境而言,一次的查詢已經(jīng)隨著的請求而結束,的垃圾回收功能會回收一次請求周期內(nèi)的數(shù)據(jù)。但在常駐內(nèi)存的環(huán)境下,尤其是單例模式下,數(shù)據(jù)庫驅動類可能一直在內(nèi)存中不被銷毀。 構造、執(zhí)行第一條語句 上一篇完成了代碼結構的搭建和 PDO 的基礎封裝,這一篇我們來講如何構造一個最基本的 SQL 語句,并執(zhí)行得到結果。 que...
摘要:返回與此鎖相關聯(lián)的給定條件等待的線程數(shù)的估計。查詢是否有線程正在等待獲取此鎖。為公平鎖,為非公平鎖線程運行了獲得鎖定運行結果公平鎖的運行結果是有序的。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchronized關鍵字(2) Java多線程學習(三)volatile關鍵字 ...
摘要:下面我們來測試一下,訪問我們經(jīng)過修改后的編寫的接口這里我將返回值統(tǒng)一為,以便數(shù)據(jù)存入,實際類型應是接口的返回類型。如果沒有返回值的話,那就可以一個對象直接通過構造方法賦值即可。 為什么要統(tǒng)一返回值 在我們做后端應用的時候,前后端分離的情況下,我們經(jīng)常會定義一個數(shù)據(jù)格式,通常會包含code,message,data這三個必不可少的信息來方便我們的交流,下面我們直接來看代碼 ReturnV...
閱讀 2928·2021-11-04 16:06
閱讀 767·2021-09-30 09:56
閱讀 1832·2021-09-22 10:02
閱讀 2612·2019-08-29 13:43
閱讀 2198·2019-08-29 13:42
閱讀 2288·2019-08-29 12:21
閱讀 1038·2019-08-29 11:29
閱讀 1375·2019-08-26 13:51