Zend Framework 透過 Zend_Db::factory() 建立適配器,支援 PDO、Mysqli 等驅動。本文示範:PDO MySQL 連線配置、註冊到 Registry 供全域存取、Model 封裝查詢、異常處理、字符集設定、連線池與持久連線考量。
$params = [
'host' => '127.0.0.1',
'username' => 'root',
'password' => '123456',
'dbname' => 'happycms',
'charset' => 'utf8',
'driver_options' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 使用原生預處理
],
];
$db = Zend_Db::factory('PDO_MYSQL', $params);
// 註冊到 Registry,全域可取
Zend_Registry::set('db', $db);
// 設為 Table 預設適配器
Zend_Db_Table::setDefaultAdapter($db);
class App_Article {
private $db;
public function __construct() {
$this->db = Zend_Registry::get('db');
}
public function listAll() {
try {
$sql = 'SELECT * FROM article ORDER BY addtime DESC';
return $this->db->fetchAll($sql); // 回傳陣列
} catch (Zend_Db_Exception $e) {
// 記錄日誌、拋出自定義異常
throw new App_Exception('查詢文章列表失敗: ' . $e->getMessage());
}
}
public function findById($id) {
$sql = 'SELECT * FROM article WHERE articleid = ?';
return $this->db->fetchRow($sql, [$id]); // 參數綁定
}
public function insert($data) {
return $this->db->insert('article', $data); // 回傳自增 ID
}
}
class ArticleController extends Zend_Controller_Action {
private $articleModel;
public function __construct() {
$this->articleModel = new App_Article();
}
public function listAllAction() {
try {
$articles = $this->articleModel->listAll();
$this->view->articles = $articles;
} catch (App_Exception $e) {
$this->_helper->flashMessenger($e->getMessage());
$this->_redirect('/');
}
}
}
| ID | 標題 | 作者 | 時間 |
|---|---|---|---|
| escape($art['articletitle']); ?> | escape($art['authorname']); ?> |
$select = $this->db->select()
->from('article', ['articleid', 'articletitle', 'authorname', 'addtime'])
->where('categoryid = ?', $catId)
->where('status = ?', 'published')
->order('addtime DESC')
->limitPage($page, $perPage);
$rows = $this->db->fetchAll($select);
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci (支援 Emoji)。SET NAMES utf8mb4 (PDO: MYSQL_ATTR_INIT_COMMAND)。mb_internal_encoding('UTF-8')。try {
$db->query($sql);
} catch (Zend_Db_Adapter_Exception $e) {
// 連線層面錯誤 (網路、認證)
error_log('DB Connection Error: ' . $e->getMessage());
} catch (Zend_Db_Statement_Exception $e) {
// SQL 執行錯誤 (語法、約束)
error_log('SQL Error: ' . $e->getMessage() . ' | Code: ' . $e->getCode());
}
Zend_Db_Table_Abstract::setDefaultMetadataCache($cache) 避免每請求 DESCRIBE TABLE。PDO::ATTR_PERSISTENT => true (PHP-FPM 下自動複用,慎用,可能導致連線洩漏)。$db->getProfiler()->setEnabled(true),開發環境分析慢查詢。| 錯誤訊息 | 原因 | 解決 |
|---|---|---|
| SQLSTATE[HY000] [2002] Connection refused | MySQL 服務未啟動/端口錯誤/防火牆 | service mysql start、檢查 3306、bind-address |
| SQLSTATE[HY000] [1045] Access denied | 用戶名/密碼錯誤、宿主不匹配 | 確認 GRANT、FLUSH PRIVILEGES、host 為 % 或具體 IP |
| SQLSTATE[HY000] [1049] Unknown database | 資料庫不存在 | CREATE DATABASE 或檢查拼寫 |
| PDOException: could not find driver | PHP 缺 pdo_mysql 擴展 | apt-get install php-mysql / 編譯 --with-pdo-mysql |
| MySQL server has gone away | 長連線超時 (wait_timeout) | 增加 wait_timeout、使用持久連線、重連邏輯 |
Zend Framework 資料庫層以 Zend_Db_Adapter 統一介面、Zend_Db_Select 物件化 SQL、Zend_Db_Table 表模型封裝,提供優雅的資料存取體驗。核心要點:**PDO 驅動 + 參數綁定 + Registry 全域適配器 + Table 預設適配器 + Metadata 快取 + 異常分層處理**。遷移至 Laminas\Db、Doctrine DBAL、Laravel Eloquent 時,適配器→Connection、Select→QueryBuilder、Table→Repository/Entity,架構思想一脈相承。