首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PDO 常用資料庫操作類庫封裝:Db 與 Model 基礎類實作

     編輯:PHP 相關知識

在掌握 PDO 核心 API 後,將連線管理與常用 SQL 操作封裝為可重用類別,能大幅提升開發效率並統一錯誤處理。本文提供兩個基礎類別:Db 負責連線單例管理,Model 封裝增刪改查核心方法,適合中小型專案直接使用或作為進階 ORM 基礎。

一、Db 類別:連線管理單例

<?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');

二、Model 類別:基礎 CRUD 封裝

<?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 或自訂規則
  • 快取層:查詢結果自動快取 Redis

五、原始簡易版本(對照參考)

原文提供的極簡版本:

// 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 注入風險,僅適合受信內部工具或學習參考。生產環境請使用預處理版本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved