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

CodeIgniter 3 Query Builder where() 進階用法全解析:條件構建、子查詢、運算子與安全防護

     編輯:PHP 相關知識

前言

CodeIgniter 的 Query Builder ($this->db->where()) 提供了流暢、安全的 SQL 條件構建介面。本文系統整理其 6 大核心變體、參數綁定機制、原生 SQL 注入防護、子查詢整合與實戰模式,助你寫出既安全又高效的查詢邏輯。

核心方法速查表

方法SQL 片段連接邏輯典型用途
where($key, $val, $escape=TRUE)WHERE key = 'val'AND等值比對
or_where($key, $val)OR key = 'val'OR替代條件
where_in($key, $array)WHERE key IN ('a','b')AND列舉匹配
or_where_in($key, $array)OR key IN ('a','b')OR列舉替代
where_not_in($key, $array)WHERE key NOT IN ('a','b')AND排除列舉
or_where_not_in($key, $array)OR key NOT IN ('a','b')OR排除替代

詳細用法與範例

1. 基礎等值比對 where()

// 簡單鍵值
$this->db->where('status', 1); 
// WHERE `status` = 1

// 陣列批次 (全部 AND)
$this->db->where(['status' => 1, 'type' => 'vip']);
// WHERE `status` = 1 AND `type` = 'vip'

// 自訂運算子 (需第三參數 FALSE 關閉自動跳脫)
$this->db->where('age >', 18, FALSE);
// WHERE `age` > 18

// 原生 SQL 片段 (第三參數 FALSE,自行負責安全)
$this->db->where('MATCH(title) AGAINST("keyword")', NULL, FALSE);
// WHERE MATCH(title) AGAINST("keyword")
安全提醒:預設 $escape=TRUE 會自動對值進行 $this->db->escape() (加引號、跳脫特殊字元)。傳入 FALSE 時,**必須確保值來自可信來源**,否則有 SQL 注入風險。

2. OR 連接 or_where()

$this->db->where('role', 'admin');
$this->db->or_where('role', 'supervisor');
// WHERE `role` = 'admin' OR `role` = 'supervisor'

// 結合運算子
$this->db->where('created_at >', '2024-01-01');
$this->db->or_where('status', 'pending');
// WHERE `created_at` > '2024-01-01' OR `status` = 'pending'

3. IN / NOT IN 系列

$ids = [10, 20, 30];

// WHERE `id` IN (10,20,30)
$this->db->where_in('id', $ids);

// OR WHERE `id` IN (10,20,30)
$this->db->or_where_in('id', $ids);

// WHERE `id` NOT IN (10,20,30)
$this->db->where_not_in('id', $ids);

// OR WHERE `id` NOT IN (10,20,30)
$this->db->or_where_not_in('id', $ids);

// 空陣列保護:CI 3.1+ 空陣列產生 `WHERE 0=1` (永假) 或 `WHERE 1=1` (永真),避免 SQL 語法錯誤
$this->db->where_in('id', []); // WHERE 0=1

4. 進階:子查詢整合

CI 不直接支援 where() 參數為 Query Builder 實例,需手動編譯子查詢 SQL:

// 子查詢:取得有訂單的用戶 ID
$subQuery = $this->db->select('user_id')->from('orders')->where('status', 'paid')->get_compiled_select();
// $subQuery = "SELECT `user_id` FROM `orders` WHERE `status` = 'paid'"

$this->db->where("id IN ($subQuery)", NULL, FALSE);
// WHERE `id` IN (SELECT `user_id` FROM `orders` WHERE `status` = 'paid')

// 或使用 EXISTS
$this->db->where("EXISTS (SELECT 1 FROM orders WHERE user_id = users.id AND status = 'paid')", NULL, FALSE);

5. 進階:動態條件建構模式

function build_user_query(array $filters) {
    $qb = $this->db->select('u.*, p.name as profile_name')
                   ->from('users u')
                   ->join('profiles p', 'p.user_id = u.id', 'left');

    // 關鍵字搜尋 (多欄位 OR)
    if (!empty($filters['keyword'])) {
        $kw = $filters['keyword'];
        $qb->group_start()
           ->like('u.username', $kw)
           ->or_like('u.email', $kw)
           ->or_like('p.name', $kw)
           ->group_end();
    }

    // 狀態過濾
    if (isset($filters['status'])) {
        $qb->where('u.status', $filters['status']);
    }

    // 角色多選 (IN)
    if (!empty($filters['roles'])) {
        $qb->where_in('u.role', $filters['roles']);
    }

    // 註冊時間區間
    if (!empty($filters['date_from'])) {
        $qb->where('u.created_at >=', $filters['date_from']);
    }
    if (!empty($filters['date_to'])) {
        $qb->where('u.created_at <=', $filters['date_to']);
    }

    // 排序與分頁
    $qb->order_by('u.created_at', 'DESC');
    if (!empty($filters['limit'])) {
        $qb->limit($filters['limit'], $filters['offset'] ?? 0);
    }

    return $qb->get()->result_array();
}

參數綁定與防注入機制深度解析

自動跳脫流程

// 內部簡化邏輯 (system/database/DB_query_builder.php)
public function where($key, $value = NULL, $escape = TRUE) {
    if (is_array($key)) {
        foreach ($key as $k => $v) $this->where($k, $v, $escape);
        return $this;
    }
    if ($escape === TRUE) {
        $value = $this->escape($value); // 單引號包裹 + 內部單引號跳脫
    }
    $this->ar_where[] = [
        'condition' => '=',
        'key'       => $this->protect_identifiers($key), // 反引號包裹欄位名
        'value'     => $value,
        'type'      => 'AND'
    ];
    return $this;
}

手動綁定參數 (Prepared Statement 風格)

CI 3 Query Builder 不直接支援具名參數綁定,但可透過 $this->db->query() 實現:

$sql = "SELECT * FROM users WHERE email = ? AND status = ?";
$results = $this->db->query($sql, [$email, 'active'])->result_array();
// 內部使用 PDO/MySQLi 預處理語句,完全免疫注入

常見錯誤與避坑指南

錯誤寫法問題正確寫法
where('id', $id, FALSE) 且 $id 來自 $_GETSQL 注入風險where('id', $id) (預設 escape) 或 query('... WHERE id = ?', [$id])
where("status = '$status'")字串拼接注入、語法錯誤where('status', $status)
where_in('id', $ids) 未檢查空陣列CI 3.0 產生 IN () 語法錯誤CI 3.1+ 自動處理;舊版先 if ($ids) where_in(...)
多個 where() 想要 OR 關係預設全 AND使用 or_where() 或 group_start()/group_end()
where('JSON_EXTRACT(data, "$.key")', 'val', FALSE)MySQL JSON 函數需關閉 escape確保 key 為常數,val 用 where('JSON_EXTRACT(data, "$.key") =', $val, FALSE)

效能優化建議

  1. 善用索引欄位放在 where 首位:where('indexed_col', $val)->where('non_indexed', $val2)
  2. 避免在 where 中對欄位使用函數:where('DATE(created_at)', $date) → 改用 where('created_at >=', $date . ' 00:00:00')->where('created_at <', $date . ' 23:59:59')
  3. 大量 IN 值 (超過 1000) 拆分或改用 JOIN/臨時表:MySQL IN 列表過大影響優化器。
  4. 啟用查詢快取:$this->db->cache_on(); (僅限 SELECT,需配置快取路徑)。

總結

掌握 where() 六大變體、$escape 參數意義、group_start()/group_end() 巢狀分組、子查詢編譯整合、動態條件建構模式,即可應對 95% 的查詢條件場景。核心原則:**永遠預設啟用 escape,只有在確定安全且必要時才關閉;複雜邏輯用 Query Builder 組合,極簡場景用 query() 參數綁定。**

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