第15回: クエリビルダの作り方 — SQLの直書きをやめて「安全に組み立てる」

章: 第2章: データベース応用

条件が増えるたびにSQL文字列をつなぎ合わせていませんか?

$sql = "SELECT * FROM users WHERE active = 1" に条件を追加するたびに $sql .= " AND name LIKE '%{$name}%'" と書いていると、SQLインジェクションのリスクが忍び込みます。

複数の条件を組み合わせるクエリを安全に組み立て、プレースホルダーでバインドすることを仕組みで保証するのがクエリビルダです。

クエリビルダの実装


<?php
class QueryBuilder {
    private string $table = '';
    private array $conditions = [];
    private array $bindings = [];
    public function from(string $table): self {
        $this->table = $table; return $this;
    }
    public function where(string $col, mixed $val): self {
        $this->conditions[] = "{$col} = ?";
        $this->bindings[] = $val; return $this;
    }
    public function toSql(): string {
        $sql = "SELECT * FROM {$this->table}";
        if ($this->conditions) {
            $sql .= ' WHERE ' . implode(' AND ', $this->conditions);
        }
        return $sql;
    }
}
$qb = (new QueryBuilder())->from('users')->where('active', 1);
echo $qb->toSql();

直書きSQL vs クエリビルダ

観点 直書きSQL クエリビルダ
SQLインジェクション 文字列結合で危険 プレースホルダーで安全
条件の組み合わせ 文字列操作で複雑化 メソッドチェーンで可読
動的クエリ if文が乱立 where()を条件付きで呼ぶだけ
テスト SQL文字列を比較 ビルダの状態をテスト

チェックポイント: 値のバインドは必ずプレースホルダー(? や名前付き :param)を使いましょう。文字列結合でのSQL組み立てはSQLインジェクションにつながります。

実行まで含めた完全な実装


<?php
class QueryBuilder {
    private string $table = '';
    private array $conditions = [];
    private array $bindings = [];

    public function from(string $table): self {
        $this->table = $table;
        return $this;
    }

    public function where(string $col, mixed $val): self {
        $this->conditions[] = "{$col} = ?";
        $this->bindings[] = $val;
        return $this;
    }

    public function execute(PDO $pdo): array {
        $sql = "SELECT * FROM {$this->table}";
        if ($this->conditions) {
            $sql .= ' WHERE ' . implode(' AND ', $this->conditions);
        }
        $stmt = $pdo->prepare($sql);
        $stmt->execute($this->bindings);
        return $stmt->fetchAll();
    }
}

$results = (new QueryBuilder())
    ->from('users')
    ->where('active', 1)
    ->where('role', 'admin')
    ->execute($pdo);

チェックポイント: クエリビルダを自作することで、LaravelやDoctrineのクエリビルダが内部でやっていることの理解が深まります。

まとめ & 次のステップ

  • クエリビルダはSQLを直書きせず、プレースホルダーで安全に組み立てる仕組み
  • メソッドチェーンで条件を積み上げることで動的クエリも可読性を保てる
  • SQLインジェクション対策はクエリビルダの仕組みで自然に実現される

次回はORM入門(Eloquent)を学びます。クエリを書かずにオブジェクト操作でDBアクセスができる、Laravelの中心的機能です。

Related Articles