# ch04 — WHERE / プレースホルダ / prepare + execute

Source: https://php-school.pages.dev/topics/db/ch04-select-where/
Site: AIと学ぶPHPの学校 (https://php-school.pages.dev)
Lesson: データベース連携
Level: L4

Reference (official): https://www.php.net/manual/ja/book.pdo.php

## 本文

# ch04 — WHERE / プレースホルダ / prepare + execute

## 概要

`SELECT * FROM users` は全件取れるが、実用では **「条件に合う行だけ」** が欲しい。これを担うのが **`WHERE` 句** で、`SELECT * FROM users WHERE age >= 25` のように「列に対する条件」を書く。比較演算子 (`=` `>=` `<` `!=`)、論理結合 (`AND` `OR`)、あいまい検索 (`LIKE 'A%'`) を組み合わせて絞り込む。

ここで最重要なのが、**値を SQL に直接書かず、必ず `?` プレースホルダを使う** こと。`"WHERE name = '$name'"` のように文字列連結すると、入力に `'` や `;` が混ざった瞬間に SQL が壊れる (= SQL インジェクションの入り口)。PHP では「SQL の **形** を先に登録 → 値は後から **別ルート** で渡す」という 2 段階で書く。これが `prepare` と `execute` の役割。

リズムは `$stmt = $pdo->prepare("SELECT * FROM users WHERE age >= ?")` で型紙を作り、`$stmt->execute([25])` で値を埋め込み、`$stmt->fetchAll()` で結果を受ける。`?` は左から順に配列要素と対応。複数条件でも値の配列を渡すだけ。このリズムが INSERT / UPDATE / DELETE すべての基本形になる。

## 学習目標

- `WHERE` 句で行を絞り込める
- 値を埋め込むときは **必ず `?` プレースホルダ** を使う (SQL インジェクション対策の入口)
- `$pdo->prepare($sql)` → `$stmt->execute([...])` → `$stmt->fetchAll()` の流れを書ける
- 複数条件 (`AND`) と `LIKE` の基本を扱える

## 所要時間

スライド 5 分 + ドリル 3 問 = **20 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | `age = ?` で特定年齢の名前を出力 |
| 02 | `age >= ?` で 25 以上の人を出力 |
| 03 | `name LIKE ? AND age < ?` で 2 条件検索 |

## スライド

---
marp: true
theme: dojo
paginate: true
size: 16:9
---

<!--
LLM_CONTEXT:
  Lesson 11 / Chapter 4
  目的: WHERE で行を絞る / プレースホルダ (?) で安全に値を渡す / prepare → execute の流れ
  扱わない: INSERT (ch05) / UPDATE/DELETE (ch06) / 取得モード詳細 (ch07) / トランザクション (ch08) / SQL インジェクションの体系的な話 (ch09)
  読み上げ時間目安: 4 分半〜5 分
-->

# WHERE / プレースホルダ / prepare + execute

Lesson 11 / Chapter 4

---

## `WHERE` で行を絞り込む

```sql
SELECT name FROM users WHERE age >= 25 ORDER BY id;
```

- `WHERE 条件` を付けると、条件に合う **行だけ** が返る
- `=` / `<` / `<=` / `>` / `>=` / `<>` (≠) が使える
- 文字列の **部分一致** は `LIKE`: `name LIKE '太%'` (「太」で始まる)
- `AND` / `OR` で複数条件をつなげられる

---

## ⚠ やってはいけない: 文字列連結

```php
// ❌ 絶対にこう書かない
$age = $_GET['age'];
$sql = "SELECT name FROM users WHERE age = $age";
$pdo->query($sql);
```

ユーザー入力をそのまま SQL に連結すると、外から SQL を **書き換えられる** (SQL インジェクション)。

例: `$age` に `0 OR 1=1` を入れられると `WHERE age = 0 OR 1=1` になり、全件返ってしまう。

→ **値を SQL に混ぜるときは必ずプレースホルダ `?` を使う**

---

## ✅ 正しい書き方: プレースホルダ `?`

```php
$sql = 'SELECT name FROM users WHERE age = ? ORDER BY id';
$stmt = $pdo->prepare($sql);
$stmt->execute([25]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($rows as $row) {
    echo $row['name'] . "\n";
}
```

- SQL の中に値を書かず `?` (プレースホルダ) を置く
- `prepare()` で SQL を準備、`execute([...])` で値を渡す
- 値は PDO 側で安全に処理される → SQL インジェクションを防げる

---

## prepare → execute → fetch の流れ

```php
// 1. SQL に ? を置いて prepare
$stmt = $pdo->prepare('SELECT name FROM users WHERE age = ?');

// 2. execute に値を配列で渡す (? の数と配列の要素数を一致させる)
$stmt->execute([25]);

// 3. 結果を取り出す
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
```

- `?` が複数あるときは配列の **順番** がそのまま対応する
- `execute([25, '太%'])` なら 1 個目の `?` が 25、2 個目が `'太%'`

---

## 複数条件 / LIKE

```php
$sql = 'SELECT name FROM users
        WHERE name LIKE ? AND age < ?
        ORDER BY id';
$stmt = $pdo->prepare($sql);
$stmt->execute(['太%', 30]);
```

- `LIKE '太%'` … 「太」で始まる名前 (`%` は 0 文字以上の任意文字列)
- `AND` でつなげれば 2 条件以上
- ワイルドカード文字 (`%` / `_`) はプレースホルダ側ではなく、**渡す値の側** に入れる

---

## このチャプターでできるようになること

✅ `WHERE` 句で行を絞れる
✅ 値を埋め込むときは `?` プレースホルダを使うとわかっている
✅ `prepare()` → `execute([...])` → `fetchAll()` の流れを書ける
✅ 複数条件 (`AND`) と `LIKE` を組み合わせられる

→ ドリルへ
