如何寫一個屬于自己的數(shù)據(jù)庫封裝(2)
發(fā)表時間:2023-09-10 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]Connector.php負(fù)責(zé)與數(shù)據(jù)庫通信,增刪改讀(CRUD)首先, 建一個Connector類, 并且設(shè)置屬性<?phpclass Connector {// 數(shù)據(jù)庫地址前綴,常見的有mysql,slqlsrv,odbc等等等private $driver = mysql;// 數(shù)據(jù)庫地...
Connector.php
首先, 建一個Connector類, 并且設(shè)置屬性
<?php
class Connector {
// 數(shù)據(jù)庫地址前綴,常見的有mysql,slqlsrv,odbc等等等
private $driver = 'mysql';
// 數(shù)據(jù)庫地址
private $host = 'localhost';
// 數(shù)據(jù)庫默認(rèn)名稱, 設(shè)置為靜態(tài)是因?yàn)橛星袚Q數(shù)據(jù)庫的需求
private static $db = 'sakila';
// 數(shù)據(jù)庫用戶名
private $username = 'root';
// 數(shù)據(jù)庫密碼
private $password = '';
// 當(dāng)前數(shù)據(jù)庫連接
protected $connection;
// 數(shù)據(jù)庫連接箱,切換已存在的數(shù)據(jù)庫連接不需要重新通信,從這里取即可
protected static $container = [];
// PDO默認(rèn)屬性配置,具體請自行查看文檔
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,
];
}
以上代碼配合注釋應(yīng)該可以理解了所以不多解釋了,直接進(jìn)入函數(shù)
buildConnectString - 就是生成DSN連接串, 非常直白
protected function buildConnectString() {
return "$this->driver:host=$this->host;dbname=".self::$db;
}
// "mysql:host=localhost;dbname=sakila;"
connect - 連接數(shù)據(jù)庫
public function connect() {
try {
// 連接數(shù)據(jù)庫,生成pdo實(shí)例, 將之賦予$connection,并存入$container之中
self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options);
// 返回數(shù)據(jù)庫連接
return $this->connection;
} catch (Exception $e) {
// 若是失敗, 返回原因
// 還記得dd()嗎?這個輔助函數(shù)還會一直用上
dd($e->getMessage());
}
}
setDatabase - 切換數(shù)據(jù)庫
public function setDatabase($db) {
self::$db = $db;
return $this;
}
_construct - 生成實(shí)例后第一步要干嘛
function construct() {
// 如果從未連接過該數(shù)據(jù)庫, 那就新建連接
if(empty(self::$container[self::$db])) $this->connect();
// 反之, 從$container中提取, 無需再次通信
$this->connection = self::$container[self::$db];
}
接下來兩個函數(shù)式配合著用的,單看可能會懵逼, 配合例子單步調(diào)試
$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 - 讀取數(shù)據(jù)
public function read($sql, $bindings) {
// 將sql語句放入預(yù)處理函數(shù)
// $sql = select * from actor where first_name = ? and last_name = ?
$statement = $this->connection->prepare($sql);
// 將附帶參數(shù)帶入pdo實(shí)例
// $bindings = ['PENELOPE', 'GUINESS']
$this->bindValues($statement, $bindings);
// 執(zhí)行
$statement->execute();
// 返回所有合法數(shù)據(jù), 以O(shè)bject對象為數(shù)據(jù)類型
return $statement->fetchAll(PDO::FETCH_OBJ);
}
bindValues - 將附帶參數(shù)帶入pdo實(shí)例
// 從例子中可以看出, 我用在預(yù)處理的變量為?, 這是因?yàn)閜do的局限性, 有興趣可以在評論區(qū)討論這個問題
public function bindValues($statement, $bindings) {
// $bindings = ['PENELOPE', 'GUINESS']
// 依次循環(huán)每一個參數(shù)
foreach ($bindings as $key => $value) {
// $key = 0/1
// $value = 'PENELOPE'/'GUINESS'
$statement->bindValue(
// 如果是字符串類型, 那就直接使用, 反之是數(shù)字, 將其+1
// 這里是數(shù)值, 因此返回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 - 改寫數(shù)據(jù)
// 與read不同的地方在于, read返回數(shù)據(jù), update返回boolean(true/false)
public function update($sql, $bindings) {
$statement = $this->connection->prepare($sql);
$this->bindValues($statement, $bindings);
return $statement->execute();
}
delete - 刪除數(shù)據(jù)
// 與update一樣, 分開是因?yàn)榉奖闳蘸缶S護(hù)制定
public function delete($sql, $bindings) {
$statement = $this->connection->prepare($sql);
$this->bindValues($statement, $bindings);
return $statement->execute();
}
create - 增加數(shù)據(jù)
// 返回最新的自增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;
}
過于高級復(fù)雜的SQL語句可能無法封裝, 因此準(zhǔn)備了可直接用RAW query通信數(shù)據(jù)庫的兩個函數(shù)
將數(shù)據(jù)庫事務(wù)相關(guān)的函數(shù)封裝起來, 直白所以沒有注釋
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 {
// 數(shù)據(jù)庫地址前綴,常見的有mysql,slqlsrv,odbc等等等
private $driver = 'mysql';
// 數(shù)據(jù)庫地址
private $host = 'localhost';
// 數(shù)據(jù)庫默認(rèn)名稱, 設(shè)置為靜態(tài)是因?yàn)橛星袚Q數(shù)據(jù)庫的需求
private static $db = 'sakila';
// 數(shù)據(jù)庫用戶名
private $username = 'root';
// 數(shù)據(jù)庫密碼
private $password = '';
// 當(dāng)前數(shù)據(jù)庫連接
protected $connection;
// 數(shù)據(jù)庫連接箱,切換已存在的數(shù)據(jù)庫連接不需要重新通信,從這里取即可
protected static $container = [];
// PDO默認(rèn)屬性配置,具體請自行查看文檔
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() {
// 如果從未連接過該數(shù)據(jù)庫, 那就新建連接
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;
}
// 連接數(shù)據(jù)庫
public function connect() {
try {
// 連接數(shù)據(jù)庫,生成pdo實(shí)例, 將之賦予$connection,并存入$container之中
self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options);
// 返回數(shù)據(jù)庫連接
return $this->connection;
} catch (Exception $e) {
// 若是失敗, 返回原因
dd($e->getMessage());
}
}
// 切換數(shù)據(jù)庫
public function setDatabase($db) {
self::$db = $db;
return $this;
}
// 讀取數(shù)據(jù)
public function read($sql, $bindings) {
// 將sql語句放入預(yù)處理函數(shù)
$statement = $this->connection->prepare($sql);
// 將附帶參數(shù)帶入pdo實(shí)例
$this->bindValues($statement, $bindings);
// 執(zhí)行
$statement->execute();
// 返回所有合法數(shù)據(jù), 以O(shè)bject對象為數(shù)據(jù)類型
return $statement->fetchAll(PDO::FETCH_OBJ);
}
// 將附帶參數(shù)帶入pdo實(shí)例
public function bindValues($statement, $bindings) {
// 依次循環(huán)每一個參數(shù)
foreach ($bindings as $key => $value) {
$statement->bindValue(
// 如果是字符串類型, 那就直接使用, 反之是數(shù)字, 將其+1
is_string($key) ? $key : $key + 1,
// 直接放入值
$value,
// 這里直白不多說
is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
);
}
}
// 改寫數(shù)據(jù)
public function update($sql, $bindings) {
// 與read不同的地方在于, read返回數(shù)據(jù), update返回boolean(true/false)
$statement = $this->connection->prepare($sql);
$this->bindValues($statement, $bindings);
return $statement->execute();
}
// 刪除數(shù)據(jù)
public function delete($sql, $bindings) {
$statement = $this->connection->prepare($sql);
$this->bindValues($statement, $bindings);
return $statement->execute();
}
// 增加數(shù)據(jù)
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.) 因?yàn)閜hp本身的特性, 默認(rèn)情況下運(yùn)行完所有代碼類會自行析構(gòu),pdo自動斷聯(lián), 所以我沒有disconnect(),讓pdo斷開連接, 不知這樣是不是一種 bad practice?
2.) 增加和改寫數(shù)據(jù)的兩個函數(shù)并不支持多組數(shù)據(jù)一次性加入,只能單次增該, 原因是我之前寫了嫌太繁瑣所以刪了, 有心的童鞋可以提供方案
以上就是如何寫一個屬于自己的數(shù)據(jù)庫封裝(2)的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
學(xué)習(xí)教程快速掌握從入門到精通的SQL知識。