在掌握 PDO 核心 API 後,將連線管理與常用 SQL 操作封裝為可重用類別,能大幅提升開發效率並統一錯誤處理。本文提供兩個基礎類別:Db 負責連線單例管理,Model 封裝增刪改查核心方法,適合中小型專案直接使用或作為進階 ORM 基礎。
<?php
// Db.class.php
class Db {
private static $pdo = null;
/**
* 取得 PDO 連線實例(單例模式)
* @return PDO
*/
public static function getDB(): PDO {
if (self::$pdo instanceof PDO) {
return self::$pdo;
}
try {
self::$pdo = new PDO(DB_DSN, DB_USER, DB_PWD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ORACLE_NULLS => true,
]);
self::$pdo->exec("SET NAMES utf8mb4");
} catch (PDOException $e) {
// 生產環境建議記錄日誌而非直接 exit
error_log('Database connection failed: ' . $e->getMessage());
throw new RuntimeException('資料庫連線失敗,請稍後再試');
}
return self::$pdo;
}
// 禁止複製與反序列化
private function __clone() {}
public function __wakeup() {}
}
config.php)<?php
define('DB_DSN', 'mysql:host=127.0.0.1;dbname=myapp;charset=utf8mb4');
define('DB_USER', 'app_user');
define('DB_PWD', 'secure_password');
<?php
// Model.class.php
abstract class Model {
protected $table;
protected $primaryKey = 'id';
protected $fillable = []; // 允許批次賦值欄位
/**
* 取得 PDO 實例
*/
protected function db(): PDO {
return Db::getDB();
}
/**
* 執行增刪改 SQL,回傳影響行數
* @param string $sql
* @param array $params
* @return int
*/
protected function execute(string $sql, array $params = []): int {
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
return $stmt->rowCount();
}
/**
* 查詢單筆資料
* @param string $sql
* @param array $params
* @return array|false
*/
protected function fetchOne(string $sql, array $params = []) {
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
return $stmt->fetch();
}
/**
* 查詢多筆資料
* @param string $sql
* @param array $params
* @return array
*/
protected function fetchAll(string $sql, array $params = []): array {
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
// === 便利方法 ===
/** 依主鍵查詢單筆 */
public function find($id) {
return $this->fetchOne(
"SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?",
[$id]
);
}
/** 查詢全部 */
public function all(): array {
return $this->fetchAll("SELECT * FROM {$this->table}");
}
/** 新增資料,回傳新增 ID */
public function create(array $data) {
// 過濾允許欄位
$data = array_intersect_key($data, array_flip($this->fillable));
$fields = implode(', ', array_keys($data));
$placeholders = ':' . implode(', :', array_keys($data));
$sql = "INSERT INTO {$this->table} ($fields) VALUES ($placeholders)";
$this->execute($sql, $data);
return $this->db()->lastInsertId();
}
/** 更新資料 */
public function update($id, array $data): int {
$data = array_intersect_key($data, array_flip($this->fillable));
$set = implode(', ', array_map(fn($k) => "$k = :$k", array_keys($data)));
$data[$this->primaryKey] = $id;
$sql = "UPDATE {$this->table} SET $set WHERE {$this->primaryKey} = :{$this->primaryKey}";
return $this->execute($sql, $data);
}
/** 刪除資料 */
public function delete($id): int {
return $this->execute(
"DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?",
[$id]
);
}
}
<?php
// UserModel.php
require_once 'Model.class.php';
class UserModel extends Model {
protected $table = 'users';
protected $fillable = ['name', 'email', 'password', 'status', 'created_at'];
}
// 使用範例
$userModel = new UserModel();
// 新增
$newId = $userModel->create([
'name' => '張三',
'email' => 'zhang@example.com',
'password' => password_hash('123456', PASSWORD_DEFAULT),
'status' => 1,
]);
// 查詢
$user = $userModel->find($newId);
$allUsers = $userModel->all();
// 更新
$userModel->update($newId, ['status' => 0]);
// 刪除
$userModel->delete($newId);
where()、orderBy()、limit() 鏈式呼叫hasOne()、hasMany()、belongsTo() 方法creating、updated、deleting 鉤子deleted_at 欄位 + 全域範圍respect/validation 或自訂規則原文提供的極簡版本:
// Db.class.php 簡易版
class Db {
static public function getDB() {
try {
$pdo = new PDO(DB_DSN, DB_USER, DB_PWD);
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, true);
$pdo->query('SET NAMES utf8');
} catch (PDOException $e) {
exit('資料庫連接錯誤:' . $e->getMessage());
}
return $pdo;
}
}
// Model.class.php 簡易版
class Model {
public function aud($sql) { // 增刪改
$pdo = Db::getDB();
return $pdo->exec($sql);
}
public function getAll($sql) { // 查詢
$pdo = Db::getDB();
return $pdo->query($sql); // 回傳 PDOStatement
}
}
簡易版直接拼接 SQL 字串,存在 SQL 注入風險,僅適合受信內部工具或學習參考。生產環境請使用預處理版本。