# ch10 — DSN 比較 / 複数 DB 接続

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

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

## 本文

# ch10 — DSN 比較 / 複数 DB 接続

## 概要

PDO の真価は、**「同じ PHP コードで複数の DB を扱える」** ところにある。SELECT / INSERT / prepare / execute はすべて DB 種類に依存しない API なので、**接続時の DSN を切り替えるだけ** で SQLite / MySQL / PostgreSQL を行き来できる。開発は SQLite で気軽に動かし、本番では MySQL や PostgreSQL に切り替える運用が当たり前にできる。

DSN は DB ごとに記法が違う: `sqlite:/path/to.db` / `mysql:host=...;dbname=...;charset=utf8mb4` / `pgsql:host=...;dbname=...`。実務では DSN・ユーザ名・パスワードを **環境変数** で外出しし、コードに DB 種類を埋め込まない設計にする。

ただし「DSN だけで動く」のは **標準 SQL の範囲のみ**。`AUTO_INCREMENT` / `SERIAL` / `RETURNING` / `JSONB` のような **DB 固有の文法** は移植性が無く、複数 DB を真面目にサポートするなら driver 分岐や Doctrine / Eloquent のような **ORM** に吸収させる。この章で「切り替わる範囲」を体感し、ch11 / ch12 で「切り替わらない部分」を見る。

## 学習目標

- SQLite / MySQL / PostgreSQL の DSN 文字列の違いを読み分けられる
- PDO は **DSN を切り替えるだけ** で同じコードが 3 DB で動くと理解する
- 環境変数で接続先を切り替える発想を身につける

## 所要時間

スライド 4 分 + ドリル 1 問 = **10 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | 3 種類の DSN サンプルを出力し、SQLite で実接続して件数を表示 |

## 採点用 DB について

採点ランナーは `tests/setup.sql` を一時 SQLite に流し込み、そのファイルパスを環境変数 `DOJO_DB_PATH` で渡してきます。SQLite だけが実接続対象です。

```php
$pdo = new PDO('sqlite:' . getenv('DOJO_DB_PATH'));
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```

MySQL / PostgreSQL は **DSN サンプル文字列を出力するだけ** で OK。実際の接続テストは Docker 環境を用意したときに行う。

## Docker で MySQL / PostgreSQL を試したい場合

実際の MySQL / PostgreSQL コンテナに接続して動作確認したい場合は、リポジトリ直下の `docker/README.md` を参照してください (docker compose で MySQL/PostgreSQL を起動する手順)。

## スライド

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

<!--
LLM_CONTEXT:
  Lesson 11 / Chapter 10
  目的: PDO は DSN を切り替えるだけで複数 DB に対応できることを理解する
  扱わない: SQL インジェクション対策 (ch09) / トランザクション (ch08) / fetch モード (ch07)
  読み上げ時間目安: 3 分半〜4 分
-->

# DSN 比較 / 複数 DB 接続

Lesson 11 / Chapter 10

---

## PDO は「DSN だけ」で接続先を切り替えられる

```php
<?php
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```

- DSN (Data Source Name) = どの DB のどこに繋ぐかを表す文字列
- DSN 以外の SQL コード (SELECT / INSERT / prepare / fetch) は **3 DB で同じ**
- → 「アプリは PHP のまま、DB だけ差し替える」が成立する

---

## SQLite の DSN

```php
<?php
$dsn = "sqlite:/path/to/file.sqlite";
$pdo = new PDO($dsn);
```

- ファイルパスを直接書くだけ
- ユーザ / パスワード不要 (ローカルファイル)
- 学習や軽い実験に向いている

---

## MySQL の DSN

```php
<?php
$dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
$pdo = new PDO($dsn, "root", "secret");
```

- `host` / `dbname` / `charset` を `;` で並べる
- ユーザ・パスワードは PDO の 2,3 番目の引数
- `charset=utf8mb4` を必ず付ける (絵文字含む UTF-8 のため)

---

## PostgreSQL の DSN

```php
<?php
$dsn = "pgsql:host=localhost;dbname=mydb";
$pdo = new PDO($dsn, "postgres", "secret");
```

- 書式は MySQL と似ているが先頭が `pgsql:`
- charset は SQL レイヤで決まる (DSN で指定しない)
- ポート指定が必要なら `;port=5432` を追加

---

## DSN 一覧 (横並び比較)

| DB | DSN 例 |
|---|---|
| SQLite | `sqlite:/path/to/file.sqlite` |
| MySQL | `mysql:host=localhost;dbname=mydb;charset=utf8mb4` |
| PostgreSQL | `pgsql:host=localhost;dbname=mydb` |

- 共通: `ドライバ名:` で始まる
- 共通: `;` 区切りで key=value
- 違い: ファイルパス vs ホスト+DB 名

---

## 環境変数で切り替える

```php
<?php
$driver = getenv('DOJO_DB_DRIVER') ?: 'sqlite';

$dsn = match ($driver) {
    'sqlite' => "sqlite:" . getenv('DOJO_DB_PATH'),
    'mysql'  => "mysql:host=localhost;dbname=mydb;charset=utf8mb4",
    'pgsql'  => "pgsql:host=localhost;dbname=mydb",
};

$pdo = new PDO($dsn);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```

→ コードはこのままで、`DOJO_DB_DRIVER=mysql` にすれば MySQL に繋がる。

---

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

✅ SQLite / MySQL / PostgreSQL の DSN 書式を読み分けられる
✅ PDO は DSN を差し替えるだけで複数 DB に対応できると説明できる
✅ 環境変数で接続先を切り替える発想が持てる
✅ `mysql:` には `charset=utf8mb4` を付ける習慣

→ ドリルへ (実接続は SQLite で行い、MySQL/PostgreSQL は DSN サンプル出力で確認)
