Connector.php
-
負責與數據庫通信,增刪改讀(crud)
首先, 建一個Connector類, 并且設置屬性
<?php class Connector { // 數據庫地址前綴,常見的有mysql,slqlsrv,odbc等等等 private $driver = 'mysql'; // 數據庫地址 private $host = 'localhost'; // 數據庫默認名稱, 設置為靜態是因為有切換數據庫的需求 private static $db = 'sakila'; // 數據庫用戶名 private $username = 'root'; // 數據庫密碼 private $password = ''; // 當前數據庫連接 protected $connection; // 數據庫連接箱,切換已存在的數據庫連接不需要重新通信,從這里取即可 protected static $container = []; // PDO默認屬性配置,具體請自行查看文檔 protected $options = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false, ]; }
-
buildConnectString – 就是生成DSN連接串, 非常直白
protected function buildConnectString() { return "$this->driver:host=$this->host;dbname=".self::$db; } // "mysql:host=localhost;dbname=sakila;"
-
connect – 屬性
public function connect() { try { // 連接數據庫,生成pdo實例, 將之賦予$connection,并存入$container之中 self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options); // 返回數據庫連接 return $this->connection; } catch (Exception $e) { // 若是失敗, 返回原因 // 還記得dd()嗎?這個輔助函數還會一直用上 dd($e->getMessage()); } }
-
屬性Database – 切換數據庫
public function setDatabase($db) { self::$db = $db; return $this; }
-
_construct – 生成實例后第一步要干嘛
function construct() { // 如果從未連接過該數據庫, 那就新建連接 if(empty(self::$container[self::$db])) $this->connect(); // 反之, 從$container中提取, 無需再次通信 $this->connection = self::$container[self::$db]; }
接下來兩個函數式配合著用的,單看可能會懵逼, 配合例子單步屬性
$a = new Connector(); $bindValues = [ 'PENELOPE', 'GUINESS' ]; dd($a->read('select * from actor where first_name = ? and last_name = ?', $bindValues));
返回值
array (size=1) 0 => object(stdClass)[4] public 'actor_id' => string '1' (length=1) public 'first_name' => string 'PENELOPE' (length=8) public 'last_name' => string 'GUINESS' (length=7) public 'last_update' => string '2006-02-15 04:34:33' (length=19)
-
read – 讀取數據
public function read($sql, $bindings) { // 將sql語句放入預處理函數 // $sql = select * from actor where first_name = ? and last_name = ? $statement = $this->connection->prepare($sql); // 將附帶參數帶入pdo實例 // $bindings = ['PENELOPE', 'GUINESS'] $this->bindValues($statement, $bindings); // 執行 $statement->execute(); // 返回所有合法數據, 以Object對象為數據類型 return $statement->fetchAll(PDO::FETCH_OBJ); }
-
bindValues – 將附帶參數帶入pdo實例
// 從例子中可以看出, 我用在預處理的變量為?, 這是因為pdo的局限性, 有興趣可以在評論區討論這個問題 public function bindValues($statement, $bindings) { // $bindings = ['PENELOPE', 'GUINESS'] // 依次循環每一個參數 foreach ($bindings as $key => $value) { // $key = 0/1 // $value = 'PENELOPE'/'GUINESS' $statement->bindValue( // 如果是字符串類型, 那就直接使用, 反之是數字, 將其+1 // 這里是數值, 因此返回1/2 is_string($key) ? $key : $key + 1, // 直接放入值 // 'PENELOPE'/'GUINESS' $value, // 這里直白不多說 // PDO::PARAM_STR/PDO::PARAM_STR is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR ); } }
所以懂了嗎_( :3」∠)
-
update – 改寫數據
// 與read不同的地方在于, read返回數據, update返回boolean(true/false) public function update($sql, $bindings) { $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); return $statement->execute(); }
-
// 與update一樣, 分開是因為方便日后維護制定 public function delete($sql, $bindings) { $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); return $statement->execute(); }
-
create – 增加數據
// 返回最新的自增ID, 如果有 public function create($sql, $bindings) { $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); $statement->execute(); return $this->lastInsertId(); }
-
lastInsertId – 返回屬性id, 如果有
// pdo自帶,只是稍微封裝 public function lastInsertId() { $id = $this->connection->lastInsertId(); return empty($id) ? null : $id; }
過于高級復雜的SQL語句可能無法封裝, 因此準備了可直接用RAW query通信數據庫的兩個函數
-
exec – 適用于增刪改
public function exec($sql) { return $this->connection->exec($sql); }
-
query – 適用于讀
public function query($sql) { $q = $this->connection->query($sql); return $q->fetchAll(PDO::FETCH_OBJ); }
將數據庫事務相關的函數封裝起來, 直白所以沒有注釋
public function beginTransaction() { $this->connection->beginTransaction(); return $this; } public function rollBack() { $this->connection->rollBack(); return $this; } public function commit() { $this->connection->commit(); return $this; } public function inTransaction() { return $this->connection->inTransaction(); }
完整代碼
<?php class Connector { // 數據庫地址前綴,常見的有mysql,slqlsrv,odbc等等等 private $driver = 'mysql'; // 數據庫地址 private $host = 'localhost'; // 數據庫默認名稱, 設置為靜態是因為有切換數據庫的需求 private static $db = 'sakila'; // 數據庫用戶名 private $username = 'root'; // 數據庫密碼 private $password = ''; // 當前數據庫連接 protected $connection; // 數據庫連接箱,切換已存在的數據庫連接不需要重新通信,從這里取即可 protected static $container = []; // PDO默認屬性配置,具體請自行查看文檔 protected $options = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false, ]; function construct() { // 如果從未連接過該數據庫, 那就新建連接 if(empty(self::$container[self::$db])) $this->connect(); // 反之, 從$container中提取, 無需再次通信 $this->connection = self::$container[self::$db]; } // 生成DSN連接串 protected function buildConnectString() { return "$this->driver:host=$this->host;dbname=".self::$db; } // 連接數據庫 public function connect() { try { // 連接數據庫,生成pdo實例, 將之賦予$connection,并存入$container之中 self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options); // 返回數據庫連接 return $this->connection; } catch (Exception $e) { // 若是失敗, 返回原因 dd($e->getMessage()); } } // 切換數據庫 public function setDatabase($db) { self::$db = $db; return $this; } // 讀取數據 public function read($sql, $bindings) { // 將sql語句放入預處理函數 $statement = $this->connection->prepare($sql); // 將附帶參數帶入pdo實例 $this->bindValues($statement, $bindings); // 執行 $statement->execute(); // 返回所有合法數據, 以Object對象為數據類型 return $statement->fetchAll(PDO::FETCH_OBJ); } // 將附帶參數帶入pdo實例 public function bindValues($statement, $bindings) { // 依次循環每一個參數 foreach ($bindings as $key => $value) { $statement->bindValue( // 如果是字符串類型, 那就直接使用, 反之是數字, 將其+1 is_string($key) ? $key : $key + 1, // 直接放入值 $value, // 這里直白不多說 is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR ); } } // 改寫數據 public function update($sql, $bindings) { // 與read不同的地方在于, read返回數據, update返回boolean(true/false) $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); return $statement->execute(); } // 刪除數據 public function delete($sql, $bindings) { $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); return $statement->execute(); } // 增加數據 public function create($sql, $bindings) { $statement = $this->connection->prepare($sql); $this->bindValues($statement, $bindings); $statement->execute(); return $this->lastInsertId(); } // 返回新增id, 如果有 public function lastInsertId() { $id = $this->connection->lastInsertId(); return empty($id) ? null : $id; } // 適用于增刪改 public function exec($sql) { return $this->connection->exec($sql); } // 適用于讀 public function query($sql) { $q = $this->connection->query($sql); return $q->fetchAll(PDO::FETCH_OBJ); } public function beginTransaction() { $this->connection->beginTransaction(); return $this; } public function rollBack() { $this->connection->rollBack(); return $this; } public function commit() { $this->connection->commit(); return $this; } public function inTransaction() { return $this->connection->inTransaction(); } }
本期疑問
1.) 因為php本身的特性, 默認情況下運行完所有代碼類會自行析構,pdo自動斷聯, 所以我沒有disconnect(),讓pdo斷開連接, 不知這樣是不是一種 bad practice?
2.) 增加和改寫數據的兩個函數并不支持多組數據一次性加入,只能單次增該, 原因是我之前寫了嫌太繁瑣所以刪了, 有心的童鞋可以提供方案
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END