Web制作の勉強をしていて、ずっと避けていたものがあります。
データベースです。
触ったら何か取り返しのつかないことが起きるんじゃないか、という気持ちがありました。
AIを使いながら学習していたのですが、出てくる言葉が英単語ばかりでした。
PDOって何なのか。
DSNって何なのか。
prepare、execute?
そもそも、なんでファイルを分けるのか。
意味がわからないまま、挫折しそうになっていました。
それでも実際にデータベースを使ったLPを作ってみたところ、自分が書いているコードが何をしているのか、ようやく少しわかってきました。
今回は「PHPでデータベースを扱う3ファイル」について、初心者目線でまとめていきます。
私自身まだ勉強中なので、拙い部分があるかと思います。同じところでつまずいている方の参考になればうれしいです。
そもそもデータベースって何なのか
私は最初、データベース=難しいもの、というイメージだけを持っていました。
でも実際に触ってみると、情報を整理して保存しておく箱のようなもので、それ自体は特別難しいものではなさそうだと理解しました。
たとえば、メーカー名のデータベースを作りたい場合。
maker テーブル
id | maker_name
-----------------
1 | メーカーA
2 | メーカーB
3 | メーカーC
こういう表を保存しておきます。
そして「id=2 のメーカー名を教えて」と聞かれたら、「メーカーBです」と返してくれる。
そういう仕組みです。
MySQL と phpMyAdmin は別のもの
ここで「そもそも、そのデータは誰が入れているのか」という疑問が出てきます。
私は見よう見まねで、phpMyAdminという画面から数値を入力しました。
データベースといえばよく聞くのがMySQLです。では、私が触ったphpMyAdminは何だったのか。
調べたところ、この2つはまったく別のものでした。
MySQL=データが保存されている倉庫
MySQLは、実際にデータを保存している場所です。
先ほどのmakerテーブルのようなデータが、この中に入っています。
phpMyAdmin=倉庫の管理画面
phpMyAdminは、その倉庫の中を見たり編集したりするための画面です。
データを追加する、修正する、削除する、SQLを書いて検索する。こういった操作ができます。
関係としては、こういう形になります。
phpMyAdmin(管理画面)
↓
MySQL(データが保存されている場所)
私が勘違いしていたこと
私は最初、phpMyAdmin=データベースだと思っていました。
データベース系ではphpMyAdminしか触っていなかったので、簡単じゃないかとすら思っていました…
実際には、それぞれの役割はこう分かれています。
phpMyAdmin → 操作するための画面
MySQL → データを保存している本体
PHP → データを取りに行く人
PHPは、倉庫にデータを取りに行く係だったわけです。
データが画面に表示されるまでの流れ
たとえば、phpMyAdminから新しく「メーカーD」を登録したとします。
id | maker_name
-----------------
1 | メーカーA
2 | メーカーB
3 | メーカーC
4 | メーカーD
この状態で、次のURLにアクセスしたときの流れです。
例:https://sample.com/?maker=3
① ユーザーが ?maker=3 でアクセス
↓
② data.php(データ取得ファイル)が呼ばれる
↓
③ db_connect.php(接続ファイル)がデータベースに接続
↓
④ maker テーブルから id=3 を探す
↓
⑤ $config['maker_name'] に値が入る
↓
⑥ index.php で表示される
この流れで、データベースに入れておいた情報がサイトに表示されます。
URLの「?maker=3」の部分については、後ろのほうで説明します。
使うファイルは4つ
私が今回作ったのは、データベース用のファイル3つと、実際に表示するファイル1つの合計4つです。
ファイル名は私が勝手に付けたものなので、それぞれの環境に合わせて変えて問題ないと思います。
① db_connect_secrecy.php(機密ファイル)
ホスト名、DB名、ユーザー名、パスワードを書いておくファイルです。
ここが一番大事なファイルで、GitHubに上げないこと、AIに読ませないことが必要になります。
② db_connect.php(接続ファイル)
①を読み込んで、PDOでデータベースへの接続を確立するファイルです。
③ data.php(データ取得ファイル)
②を読み込んで、URLからIDを受け取り、データベースからメーカー名を取ってくるファイルです。
④ index.php(ページ本体)
③を読み込んで、HTMLの中にメーカー名を表示するファイルです。
なんでファイルを分けるのか
最初は「なんで1つのファイルにまとめて書いちゃダメなんだろう」と思っていました。
分けてみてわかったのは、機密情報を守れることと、使い回しができることでした。
①の機密ファイルを分けると、パスワードをGitに上げずに済みますし、AIに読ませないための設定もしやすくなります。
②の接続ファイルを分けると、DB接続の処理が1箇所にまとまります。他のページでもそのまま読み込むだけで使えます。
③のデータ取得ファイルを分けると、やりたい処理だけに集中できます。
今後、別のテーブルからデータを取りたくなったときも、①と②はそのまま使い回して③だけ新しく作ればいい。
これはかなり便利だと思いました。
実際に書いたコード(4ファイル分)
まずは私が使ったコードを、そのまま載せておきます。
① db_connect_secrecy.php(機密ファイル)
<?php
$db_host = 'localhost'; // Dockerの場合は db
$db_name = 'sample_database'; // データベース名
$db_user = 'sample_user'; // ユーザー名
$db_pass = 'sample_password'; // パスワード
$db_charset = 'utf8mb4'; // 文字コード
?>
② db_connect.php(接続ファイル)
<?php
include(__DIR__ . '/db_connect_secrecy.php');
$dsn = "mysql:host={$db_host};dbname={$db_name};charset={$db_charset}";
try {
$pdo = new PDO($dsn, $db_user, $db_pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
} catch (PDOException $e) {
die('DB接続エラー');
}
?>
③ data.php(データ取得ファイル)
<?php
include(__DIR__ . '/db_connect.php');
// ?maker=2 のようにURLパラメータからメーカーIDを取得
$maker_id = isset($_GET['maker']) ? (int)$_GET['maker'] : 1;
$stmt = $pdo->prepare('SELECT maker_name FROM maker WHERE id = :id LIMIT 1');
$stmt->execute([':id' => $maker_id]);
$maker = $stmt->fetch();
if (!$maker) {
$stmt = $pdo->prepare('SELECT maker_name FROM maker WHERE id = 1 LIMIT 1');
$stmt->execute();
$maker = $stmt->fetch();
}
$config['maker_name'] = $maker ? $maker['maker_name'] : 'メーカー1';
?>
④ index.php と、実際に表示するファイル
<?php
$config = include(__DIR__ . '/config.php');
include(__DIR__ . '/data.php');
?>
<div class="hero">
<p class="hero-maker"><?php echo $config['maker_name']; ?></p>
</div>
コードの中身を1つずつ見ていきます
①機密ファイル:パスワードを直接書いて大丈夫なのか
ここで最初に疑問が出ました。パスワードをファイルに直接書いて大丈夫なのか、ということです。
調べた範囲では、パスワードを処理のコードの中に直接書かないことが大事なようです。
接続情報だけを別ファイルに切り出しておいて、そのファイル自体を守る、という考え方だと理解しました。
GitHubを使っている場合は.gitignoreで管理対象から外す。AIを使っている場合は読ませない設定をする。
コードを書くことと同じくらい、情報を守ることも大事だと感じました。
この設定については別の記事にまとめているので、そちらも置いておきます。


Claude Codeを使う場合には下記記事もご参照ください!

②接続ファイル:DSNとPDO
include(__DIR__ . ‘/db_connect_secrecy.php’);
機密ファイルを読み込む部分です。これで $db_host などの変数が使えるようになります。
$dsn = “mysql:host={$db_host};dbname={$db_name};charset={$db_charset}”;
DSNは、接続先の住所のようなものです。
どのサーバーの、どのデータベースに、どの文字コードで接続するか。それを1行にまとめた文字列だと理解しました。
$pdo = new PDO($dsn, $db_user, $db_pass);
PDOは、PHPからデータベースに接続するための道具です。
私は翻訳係のようなものだとイメージしています。
PHP
↓
PDO(翻訳係)
↓
MySQL
②接続ファイル:PDOのオプション3つ
new PDO の後ろに付いている配列の部分です。
ERRMODE_EXCEPTION:エラーが起きたらすぐにエラーを出します。黙って無視されないようにするものです。
FETCH_ASSOC:データを取ってきたとき、カラム名をキーにした配列で返してくれます。
EMULATE_PREPARES => false:SQLインジェクション対策を強くするための設定です。
②接続ファイル:try / catch とは
try と catch は、とりあえずやってみて、失敗したら対処するという書き方です。
JavaScriptのif文と何が違うのか、最初はわかりませんでした。
私は、こう整理しました。
if文は、事前に確認する。ドアが開いているか確認してから入る。
try/catchは、とりあえずやってみる。ドアを開けてみて、開かなかったら対処する。
データベースは、実際に接続してみないとサーバーが落ちているのか、パスワードが違うのかがわかりません。
だからこそ、とりあえず開けてみる書き方が向いているようです。
PHPの「$」は何なのか
$pdo のように、頭に $ が付いていることが多くて気になっていました。
PHPでは変数(値を入れる箱)を作るとき、必ず先頭に $ を付けるルールがあります。
JavaScriptの let のようなものだと考えると、少しわかりやすくなりました。
③データ取得ファイル:URLから値を受け取る
$maker_id = isset($_GET[‘maker’]) ? (int)$_GET[‘maker’] : 1;
URLからメーカーIDを取得している部分です。
$_GET は、PHPが最初から用意している特別な変数です。
URLの「?」以降の値を、自動的に取ってきてくれます。
だから、https://sample.com/?maker=3 でアクセスすると、3という値が受け取れる、というわけでした。
③データ取得ファイル:prepare / execute / fetch
ここが一番わからなかったところです。
データベースへの問い合わせは、3ステップに分かれていました。
① prepare(...) 命令書を作る(まだ実行しない)
② execute(...) :id に実際の値を入れて実行する
③ fetch() 結果を1件取り出す
いきなり取ってくるのではなく、命令書を先に作って、値をあとから流し込むという順番になっています。
SQLの中身も分解すると、そんなに複雑ではありませんでした。
SELECT maker_name → maker_name というカラム(列)を取ってきて
FROM maker → maker というテーブル(表)から
WHERE id = :id → id が指定した値のものだけ
LIMIT 1 → 1件だけ
③データ取得ファイル:「:id」はSQLインジェクション対策
この「:id」が何なのか、ずっとわかりませんでした。
調べたところ、SQLインジェクション対策のための書き方でした。
SQLインジェクションは、悪意のあるユーザーがURLに攻撃用のコードを仕込む攻撃のことです。
たとえば、こう書いてしまったとします。
$sql = "SELECT * FROM maker WHERE id = " . $_GET['maker'];
URLの値をそのままSQLにつなげているので、想定していない命令が実行される可能性があります。
テーブルを消される、といったことも起こり得るようです。
そこで、こう書きます。
WHERE id = :id
こう書いておくと、「これはデータですよ」とPDOが安全に扱ってくれます。
命令ではなく、ただの値として処理してくれる、ということだと理解しました。
③データ取得ファイル:最後のデフォルト値
$config[‘maker_name’] = $maker ? $maker[‘maker_name’] : ‘メーカー1’;
データベースが完全に空だったときにだけ使われる、最後の保険です。
値は自由に変えて大丈夫です。
私がつまずいたポイント
- phpMyAdmin=データベースだと思い込んでいた(実際は管理画面)
- ファイルを分ける意味がわからなかった(機密の分離と、使い回しのため)
- 「:id」を書かずにURLの値を直接つなげると、攻撃を受ける可能性がある
- 機密ファイルは.gitignoreとAIの設定、両方で守る必要がある
まとめ
データベースについては、まだまだ学習が必要だと思っています。
それでも、避けていたころに比べれば、自分が書いているコードが何をしているのかは見えるようになりました。
データを保存しているのがMySQLで、それを見る画面がphpMyAdmin。取りに行くのがPHPで、その通訳がPDO。
この関係がわかっただけでも、コードを読むのがずいぶん楽になりました。
私と同じところで止まっている方に、少しでも届いたらうれしいです。
ここに書ききれなかった部分もあるので、気になることがあればコメントで教えていただけると助かります。
ここまで読んでくださり、ありがとうございました。


