CodeIgniter Query Builder 支援流暢的鏈式呼叫:
$this->db->select('id, name')
->from('users')
->where('status', 1)
->order_by('created_at', 'DESC')
->limit(10)
->get();
每個方法呼叫後似乎都回傳「某個物件」讓下一個方法接續呼叫。本文深入剖析其底層實現原理。
**所有 Query Builder 公開方法最終都回傳 $this (當前 DB_query_builder 實例)。**
class CI_DB_query_builder {
// 內部狀態屬性
protected $qb_select = [];
protected $qb_from = [];
protected $qb_where = [];
protected $qb_orderby = [];
protected $qb_limit = FALSE;
protected $qb_offset = FALSE;
// ... 其他 30+ 狀態屬性
// 公開方法:修改內部狀態 → 回傳 $this
public function select($select = '*', $escape = TRUE) {
if (is_string($select)) $select = explode(',', $select);
foreach ($select as $val) {
$val = trim($val);
if ($val !== '') $this->qb_select[] = $val;
}
return $this; // <=== 關鍵:回傳自身實例
}
public function from($from) {
$this->qb_from = (array) $from;
return $this;
}
public function where($key, $value = NULL, $escape = TRUE) {
// ... 處理邏輯,將條件存入 $this->qb_where
return $this;
}
public function order_by($orderby, $direction = '') {
$this->qb_orderby[] = [$orderby, $direction];
return $this;
}
public function limit($limit, $offset = 0) {
$this->qb_limit = (int) $limit;
$this->qb_offset = (int) $offset;
return $this;
}
// 終端方法:執行查詢,回傳結果物件 (不再回傳 $this)
public function get($table = '', $limit = NULL, $offset = NULL) {
if ($table !== '') $this->from($table);
if ($limit !== NULL) $this->limit($limit, $offset);
$sql = $this->get_compiled_select($table);
$result = $this->query($sql);
$this->reset_query(); // 重置內部狀態,支援複用
return $result; // 回傳 CI_DB_result 實例
}
public function get_compiled_select($table = '', $reset = TRUE) {
// 根據內部狀態組裝 SQL 字串
// SELECT $select FROM $from WHERE $where ORDER BY $orderby LIMIT $limit OFFSET $offset
if ($reset) $this->reset_query();
return $sql;
}
protected function reset_query() {
$this->qb_select = [];
$this->qb_from = [];
$this->qb_where = [];
// ... 重置所有 qb_* 屬性
}
}
$this (或新建不可變物件) 實現連續呼叫。// 一般呼叫 (需重複 $this->db)
$this->db->select('id, name');
$this->db->from('users');
$this->db->where('status', 1);
$query = $this->db->get();
// 鏈式呼叫 (單一表達式)
$query = $this->db->select('id, name')
->from('users')
->where('status', 1)
->get();
// 兩者等價,鏈式僅為語法糖
| 類型 | 方法 | 回傳值 | 說明 |
|---|---|---|---|
| 中介方法 (Intermediary) | select, from, where, join, like, order_by, limit, group_by, having, or_where, where_in... | $this | 修改內部狀態,支援繼續鏈式 |
| 終端方法 (Terminal) | get, insert, update, delete, replace, count_all_results, get_compiled_select | 結果物件 / 執行結果 | 執行實際 SQL,通常會 reset_query() |
| 狀態檢查 | count_all, last_query | 資訊 | 不修改狀態 |
在 application/core/MY_DB_query_builder.php 擴展:
<?php
class MY_DB_query_builder extends CI_DB_query_builder {
// 軟刪除範圍
public function not_deleted() {
return $this->where($this->protect_identifiers('deleted_at'), NULL, FALSE);
}
// 發布狀態範圍
public function published() {
return $this->where('status', 'published');
}
// 搜尋範圍 (多欄位 LIKE)
public function search($keyword, array $columns) {
if (empty($keyword)) return $this;
return $this->group_start()
->like($columns[0], $keyword)
->or_like($columns[1], $keyword)
// ... 更多欄位
->group_end();
}
// 分頁快捷方法
public function paginate($page = 1, $per_page = 20) {
$page = max(1, (int)$page);
$offset = ($page - 1) * $per_page;
return $this->limit($per_page, $offset);
}
}
// 需在 database.php 設定 $db['default']['dbdriver'] = 'mysqli'; 並確保 MY_DB_query_builder 載入
使用:
$articles = $this->db->select('id, title, slug')
->from('articles')
->published()
->not_deleted()
->search($kw, ['title', 'content'])
->order_by('published_at', 'DESC')
->paginate($page, 15)
->get()
->result_array();
get/insert/update/delete) 才會查詢資料庫。reset_query(),但手動呼叫 get_compiled_select(FALSE) 後需自行重置。$this->db 獲取共享實例,確保連線複用與快取一致性。get_compiled_select() 印出 SQL 除錯。get_compiled_select() 確認 SQL,必要時改用原生 $this->db->query($sql, $params) 避免 Builder 組裝開銷。CodeIgniter Query Builder 鏈式操作的核心極其簡單:**每個中介方法修改內部狀態陣列後回傳 $this,終端方法根據累積狀態組裝 SQL 執行並重置狀態。** 這是典型的 **Fluent Interface (流暢介面)** 設計模式應用。理解此原理,不僅能正確使用現有 API,更能根據業務需求擴展領域專用的鏈式方法 (Scope),大幅提升查詢建構的表達力與復用性。