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

PHP 實現鏈式調用(流式接口)模式:SQL 查詢構建器實戰示例

     編輯:PHP 相關知識

PHP 實現鏈式調用(流式接口)模式:SQL 查詢構建器實戰示例

鏈式調用通過方法返回 $this 實現連續調用,極大提升代碼可讀性與編寫體驗,是現代 PHP 框架(Laravel Query Builder、ThinkPHP Model、Doctrine QB)的核心設計模式。本文通過構建一個 SQL 查詢構建器,詳細演示鏈式調用的實現原理與擴展技巧。

一、核心原理

class Builder {
    public function method1() { /* ... */ return $this; }
    public function method2() { /* ... */ return $this; }
}

// 使用
$builder->method1()->method2()->method3();

關鍵點:每個鏈式方法必須 return $this,最終由終結方法(如 get()、toSql())返回實際結果。

二、SQL 查詢構建器完整實現

<?php
class SqlBuilder {
    private $sql = [
        'select' => '*',
        'from'   => '',
        'where'  => '',
        'order'  => '',
        'limit'  => '',
        'params' => [] // 參數綁定,防注入
    ];
    
    // SELECT 字段
    public function select(string $fields = '*'): self {
        $this->sql['select'] = $fields;
        return $this;
    }
    
    // FROM 表
    public function from(string $table): self {
        $this->sql['from'] = "FROM {$table}";
        return $this;
    }
    
    // WHERE 條件(支持參數綁定)
    public function where(string $column, $operator = null, $value = null): self {
        // 重載:where('id', 1) / where('id', '>', 1) / where('status', '=', 1, 'AND')
        if (func_num_args() === 2) {
            $value = $operator;
            $operator = '=';
        }
        $logic = func_num_args() === 4 ? func_get_arg(3) : 'AND';
        $placeholder = ':' . preg_replace('/[^a-z0-9_]/i', '', $column);
        $condition = "{$column} {$operator} {$placeholder}";
        
        if ($this->sql['where']) {
            $this->sql['where'] .= " {$logic} {$condition}";
        } else {
            $this->sql['where'] = "WHERE {$condition}";
        }
        $this->sql['params'][$placeholder] = $value;
        return $this;
    }
    
    // OR WHERE 簡寫
    public function orWhere(string $column, $operator = null, $value = null): self {
        return $this->where($column, $operator, $value, 'OR');
    }
    
    // ORDER BY
    public function orderBy(string $column, string $direction = 'ASC'): self {
        $this->sql['order'] = "ORDER BY {$column} " . strtoupper($direction);
        return $this;
    }
    
    // LIMIT / OFFSET
    public function limit(int $count, int $offset = 0): self {
        $this->sql['limit'] = $offset > 0 
            ? "LIMIT {$offset}, {$count}" 
            : "LIMIT {$count}";
        return $this;
    }
    
    // 獲取最終 SQL(調試用)
    public function toSql(): string {
        $parts = array_filter($this->sql, function($k) { return $k !== 'params'; }, ARRAY_FILTER_USE_KEY);
        return implode(' ', $parts);
    }
    
    // 獲取綁定參數
    public function getBindings(): array {
        return $this->sql['params'];
    }
    
    // 執行查詢(需注入 PDO)
    public function get(PDO $pdo): array {
        $stmt = $pdo->prepare($this->toSql());
        $stmt->execute($this->getBindings());
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    // 重置構建器(復用實例)
    public function reset(): self {
        $this->sql = [
            'select' => '*', 'from' => '', 'where' => '',
            'order' => '', 'limit' => '', 'params' => []
        ];
        return $this;
    }
}

// 使用示例
$builder = new SqlBuilder();
$users = $builder->select('id, name, email')
                 ->from('users')
                 ->where('status', 1)
                 ->where('role', '!=', 'banned')
                 ->orWhere('is_admin', 1)
                 ->orderBy('created_at', 'DESC')
                 ->limit(10)
                 ->get($pdo);

echo $builder->toSql();
// SELECT id, name, email FROM users WHERE status = :status AND role != :role OR is_admin = :is_admin ORDER BY created_at DESC LIMIT 10
?>

三、ThinkPHP / Laravel 內部實現參考

  • ThinkPHP Model:Model::where()->field()->order()->limit()->select(),終結方法 select/find/get 觸發查詢執行
  • Laravel Query Builder:DB::table()->where()->orderBy()->limit()->get(),底層 Illuminate\Database\Query\Builder 實現 __call 動態代理
  • Eloquent ORM:模型繼承 Model,查詢構建器方法代理到 newQuery() 返回的 Builder 實例

四、靜態類鏈式調用(進階)

靜態方法無 $this,需返回 新實例 或使用 靜態屬性存儲狀態(非線程安全):

class StaticBuilder {
    private static $instance;
    private $data = [];
    
    public static function create(): self {
        self::$instance = new self();
        return self::$instance;
    }
    
    public function set(string $key, $value): self {
        $this->data[$key] = $value;
        return $this;
    }
    
    public function getData(): array {
        $result = $this->data;
        self::$instance = null; // 用後重置
        return $result;
    }
}

// 使用
$result = StaticBuilder::create()->set('a', 1)->set('b', 2)->getData();

注意:靜態鏈式調用在併發環境下有狀態污染風險,生產環境建議使用實例模式或無狀態函數式風格。

五、擴展技巧與最佳實踐

1. 終結方法區分

public function scopeActive(self $query): self {
    return $query->where('status', 1);
}

// 使用
User::query()->active()->get(); // ThinkPHP/Laravel 支持

3. 參數驗證與異常處理

public function limit(int $count, int $offset = 0): self {
    if ($count <= 0) throw new \InvalidArgumentException('Limit must be positive');
    if ($offset < 0) throw new \InvalidArgumentException('Offset cannot be negative');
    // ...
}

4. 復用與重置

構建器實例可復用,但需提供 reset() 或 newQuery() 確保狀態隔離。

六、總結

鏈式調用本質是 方法返回對象自身($this),配合 終結方法觸發執行 的流式接口模式。核心優勢:

  • 代碼閱讀如自然語言,降低認知負荷
  • 參數順序無關,靈活組合
  • 易於擴展新子句(JOIN、GROUP BY、HAVING 等)
  • 天然支持參數綁定,防止 SQL 注入

掌握此模式可自行構建查詢構建器、HTTP 客戶端、配置構建器、報表生成器等流式 API。

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