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

PHP 匿名函數 (Closure) 實戰:構建優雅的數據庫訪問層 (BaseDao 模式)

     編輯:PHP 相關知識

動機

傳統 DAO 充斥樣板代碼:連接、預處理、綁定、執行、映射、關閉。利用 PHP 5.3+ 匿名函數 (Closure) 封裝「執行上下文」,實現連接管理、參數綁定、結果映射的關注點分離,極大簡化業務代碼。

核心實現:BaseDao

<?php
declare(strict_types=1);

class BaseDao {
    private PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }

    /** 通用查詢執行器
     * @param string $sql 含佔位符 SQL
     * @param array $params 參數陣列
     * @param callable|null $mapper 結果映射 Closure (PDOStatement $stmt): mixed
     * @return mixed
     */
    protected function query(string $sql, array $params = [], ?callable $mapper = null) {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $mapper ? $mapper($stmt) : $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /** 單行查詢 */
    protected function queryOne(string $sql, array $params = [], ?callable $mapper = null) {
        return $this->query($sql, $params, $mapper ?? fn($stmt) => $stmt->fetch(PDO::FETCH_ASSOC));
    }

    /** 寫操作 返回影響行數/最後插入 ID */
    protected function execute(string $sql, array $params = []): int {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->rowCount();
    }

    protected function lastInsertId(): string {
        return $this->pdo->lastInsertId();
    }
}

業務層繼承示例

<?php
class User {
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
        public string $createdAt
    ) {}
}

class UserDao extends BaseDao {
    public function findAll(): array {
        return $this->query(
            'SELECT id, name, email, created_at FROM users ORDER BY id DESC',
            [],
            fn($stmt) => $stmt->fetchAll(PDO::FETCH_FUNC, function($id, $name, $email, $createdAt) {
                return new User($id, $name, $email, $createdAt);
            })
        );
    }

    public function findById(int $id): ?User {
        return $this->queryOne(
            'SELECT id, name, email, created_at FROM users WHERE id = ?',
            [$id],
            fn($stmt) => $stmt->fetch(PDO::FETCH_FUNC, function($id, $name, $email, $createdAt) {
                return new User($id, $name, $email, $createdAt);
            }) ?? null
        );
    }

    public function insert(User $user): int {
        $this->execute(
            'INSERT INTO users (name, email, created_at) VALUES (?, ?, NOW())',
            [$user->name, $user->email]
        );
        return (int)$this->lastInsertId();
    }

    public function update(User $user): bool {
        return $this->execute(
            'UPDATE users SET name = ?, email = ? WHERE id = ?',
            [$user->name, $user->email, $user->id]
        ) > 0;
    }

    public function delete(int $id): bool {
        return $this->execute('DELETE FROM users WHERE id = ?', [$id]) > 0;
    }
}

使用姿勢

<?php
$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'user', 'pass');
$userDao = new UserDao($pdo);

// 查詢列表
$users = $userDao->findAll();
foreach ($users as $u) { echo $u->name . PHP_EOL; }

// 單條查詢
$user = $userDao->findById(1);
if ($user) $user->name = 'Updated';

// 寫操作
$newId = $userDao->insert(new User(0, 'New', 'new@test.com', ''));
$userDao->update($user);
$userDao->delete($newId);

進階擴展點

  • 事務封裝:transactional(callable $work): mixed 自動提交/回滾。
  • 分頁器:paginate(string $sql, array $params, int $page, int $perPage, callable $mapper) 返回 [items, total, page, perPage]。
  • 緩存裝飾器:在 query() 內集成 Redis/Memcached,Key 由 SQL + 參數哈希生成。
  • 類型安全映射:PHP 8.1+ PDO::FETCH_CLASS 直接映射到 readonly class。

為什麼不用 ORM?

輕量項目、微服務、性能敏感、團隊偏好 SQL 可控時,BaseDao + Closure 提供零依賴、零學習成本、完全可控的替代方案,代碼量約 ORM 1/10,效能損耗可忽略。

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