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

CodeIgniter 3 Query Builder 鏈式操作底層原理:$this 回傳模式、方法鏈與 Fluent Interface 設計模式解析

     編輯:PHP 相關知識

現象觀察

CodeIgniter Query Builder 支援流暢的鏈式呼叫:

$this->db->select('id, name')
         ->from('users')
         ->where('status', 1)
         ->order_by('created_at', 'DESC')
         ->limit(10)
         ->get();

每個方法呼叫後似乎都回傳「某個物件」讓下一個方法接續呼叫。本文深入剖析其底層實現原理。

核心原理:回傳 $this (Fluent Interface)

**所有 Query Builder 公開方法最終都回傳 $this (當前 DB_query_builder 實例)。**

簡化版原始碼結構 (system/database/DB_query_builder.php)

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_* 屬性
    }
}

設計模式:Fluent Interface (流暢介面)

  • 定義:方法鏈風格 API,透過回傳 $this (或新建不可變物件) 實現連續呼叫。
  • 優點:程式碼可讀性高、接近自然語言、減少中間變數、支援 IDE 自動完成。
  • 缺點:除錯中斷點難設定、錯誤堆疊追蹤較長、不可變物件模式記憶體開銷大 (CI 採用可變 $this)。

對比:一般呼叫 vs 鏈式呼叫

// 一般呼叫 (需重複 $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();

常見誤區與避坑

  1. 以為每次鏈式都執行 SQL:只有終端方法 (get/insert/update/delete) 才會查詢資料庫。
  2. 重複使用同一實例未重置:CI 3 終端方法會自動 reset_query(),但手動呼叫 get_compiled_select(FALSE) 後需自行重置。
  3. 在模型中直接 new CI_DB_query_builder:應透過 $this->db 獲取共享實例,確保連線複用與快取一致性。
  4. 鏈式過長導致除錯困難:適當拆分為多行,或用 get_compiled_select() 印出 SQL 除錯。

效能影響分析

  • 記憶體:單一實例累積狀態,極微量 (KB 級),無額外物件分配。
  • CPU:方法呼叫開銷可忽略,主要成本在 SQL 組裝與資料庫往返。
  • 優化建議:複雜查詢優先用 get_compiled_select() 確認 SQL,必要時改用原生 $this->db->query($sql, $params) 避免 Builder 組裝開銷。

總結

CodeIgniter Query Builder 鏈式操作的核心極其簡單:**每個中介方法修改內部狀態陣列後回傳 $this,終端方法根據累積狀態組裝 SQL 執行並重置狀態。** 這是典型的 **Fluent Interface (流暢介面)** 設計模式應用。理解此原理,不僅能正確使用現有 API,更能根據業務需求擴展領域專用的鏈式方法 (Scope),大幅提升查詢建構的表達力與復用性。

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