【MySQL入門】お問い合わせ内容をデータベースに保存する
章: 2章
技術タグ: DB保存 MySQL PDO PRGパターン プリペアドステートメント
今回やること
前回は、PRGパターンを使ってフォームの二重送信を防ぎやすい構成を作りました。
フォーム送信後に receive.php で直接画面表示せず、thanks.php へリダイレクトする形です。
今回は、フォーム処理をさらに実務に近づけます。
お問い合わせ内容をMySQLに保存します。
フォームに入力する ↓ POSTでPHPへ送信する ↓ PHPでバリデーションする ↓ 問題なければMySQLへ保存する ↓ thanks.phpへリダイレクトする
これまで画面に表示するだけだったお問い合わせ内容を、あとから確認できるデータとして残します。
今回のゴール
今回のゴールは、以下です。
- LaragonのMySQLを使う
- お問い合わせ保存用のデータベースを作る
- お問い合わせ保存用のテーブルを作る
- PHPからPDOでMySQLへ接続する
- フォーム送信内容をDBへ保存する
- プリペアドステートメントで安全にINSERTする
- 保存後にPRGパターンでthanks.phpへ移動する
今回から、フォームはただの画面ではなく、データを扱うWebシステムに近づきます。
今回必要なもの
今回はMySQLを使うため、Laragon環境で進めます。
- VS Code
- Google Chrome
- Laragon
- phpMyAdmin または LaragonのDB操作画面
- Git Bash
- Chrome DevTools
MySQLは、データを保存するためのデータベースです。
お問い合わせフォームでは、名前・メールアドレス・お問い合わせ内容・送信日時などを保存できます。
今回のファイル構成
今回は、DB接続用の db.php を追加します。
contact-form-app/ ├─ index.php ├─ receive.php ├─ thanks.php ├─ db.php └─ style.css
| ファイル | 役割 |
|---|---|
| index.php | フォーム表示・エラー表示 |
| receive.php | POST受信・バリデーション・DB保存・リダイレクト |
| thanks.php | 送信完了画面 |
| db.php | MySQL接続処理 |
| style.css | 見た目調整 |
DB接続処理を receive.php に直接書くこともできます。
しかし、接続処理は今後も使い回す可能性があるため、db.php に分けます。
DB保存で何が変わるのか
これまでのフォームは、送られてきた値を画面で確認するだけでした。
しかし、それだけではあとから確認できません。
DBに保存すると、送信されたお問い合わせをあとから一覧表示したり、管理画面で確認したりできます。
メールだけ ↓ 受信できるが、管理しづらい DB保存 ↓ 一覧表示・検索・対応状況管理に発展できる
実務では、お問い合わせ内容をメール送信するだけでなく、DBに保存して管理することも多いです。
データベースを作る
まず、お問い合わせ保存用のデータベースを作ります。
phpMyAdminなどで、以下のSQLを実行します。
CREATE DATABASE contact_form_app
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;contact_form_app という名前のデータベースを作ります。
utf8mb4 は、日本語や絵文字なども扱いやすい文字コードです。
テーブルを作る
次に、お問い合わせ内容を保存するテーブルを作ります。
作成した contact_form_app データベースを選択し、以下のSQLを実行します。
CREATE TABLE inquiries (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);今回作る inquiries テーブルには、以下のカラムを用意します。
| カラム | 役割 |
|---|---|
| id | 1件ごとの識別番号 |
| name | お名前 |
| メールアドレス | |
| message | お問い合わせ内容 |
| created_at | 送信日時 |
まずはMVPなので、最小限の構成にしています。
あとで対応状況やメモを追加するなら、status や admin_memo のようなカラムを増やせます。
db.phpを作る
次に、PHPからMySQLへ接続するためのファイルを作ります。
db.php に以下を書きます。
<?php
/**
* db.php
*
* 役割:
* - MySQLへ接続するためのPDOインスタンスを作る
* - DB接続処理を1か所にまとめる
*
* 注意:
* - 今回は学習用MVPとして、このファイルに接続情報を書いています。
* - 本番では環境変数や設定ファイルの扱いを検討します。
*/
$dsn = 'mysql:host=localhost;dbname=contact_form_app;charset=utf8mb4';
$db_user = 'root';
$db_pass = '';
try {
$pdo = new PDO($dsn, $db_user, $db_pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
exit('データベース接続に失敗しました。');
}ここでは、PDOを使ってMySQLに接続しています。
PDOは、PHPからデータベースを扱うための仕組みです。
今回は、接続情報を1か所にまとめるために db.php に分けています。
PDOとは何か
PDOは、PHP Data Objects の略です。
PHPからデータベースへ接続し、SQLを実行するための仕組みです。
ざっくり言うと、PHPとMySQLの間をつなぐ通訳のような存在です。
PHP ↓ PDO MySQL
PDOを使うと、SQL実行やプリペアドステートメントを扱いやすくなります。
receive.phpをDB保存対応にする
次に、receive.php を修正します。
バリデーションを通過したあと、MySQLへINSERTします。
【ここに Highlighting Code Block:receive.php(PHP)】
<?php
/**
* receive.php
*
* 役割:
* - フォームからPOST送信された値を受け取る
* - サーバー側で入力チェックする
* - エラーがあればフォーム画面へ戻す
* - 問題なければDBへ保存する
* - 保存後、thanks.phpへリダイレクトする
*/
require_once __DIR__ . '/db.php';
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
$errors = [];
if ($name === '') {
$errors[] = 'お名前を入力してください。';
}
if ($email === '') {
$errors[] = 'メールアドレスを入力してください。';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'メールアドレスの形式で入力してください。';
}
if ($message === '') {
$errors[] = 'お問い合わせ内容を入力してください。';
}
if (!empty($errors)) {
$query = http_build_query([
'errors' => $errors,
'name' => $name,
'email' => $email,
'message' => $message,
]);
header('Location: index.php?' . $query);
exit;
}
$sql = 'INSERT INTO inquiries (name, email, message) VALUES (:name, :email, :message)';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':name' => $name,
':email' => $email,
':message' => $message,
]);
header('Location: thanks.php');
exit;今回の重要ポイントは、次の流れです。
require_onceでdb.phpを読み込む ↓ POST値を受け取る ↓ PHP側でバリデーションする ↓ エラーがあればindex.phpへ戻す ↓ 問題なければINSERTする ↓ thanks.phpへリダイレクトする
PRGパターンは今回も維持します。
DB保存が成功したあと、thanks.php へリダイレクトします。
INSERT文を見る
DBへ保存している中心部分はここです。
$sql = 'INSERT INTO inquiries (name, email, message) VALUES (:name, :email, :message)';
INSERT INTO は、テーブルに新しいレコードを追加するSQLです。
今回は、inquiries テーブルに name、email、message を保存します。
:name や :email は、あとから値を差し込むための目印です。
プリペアドステートメントとは何か
今回のDB保存では、プリペアドステートメントを使っています。
$stmt = $pdo->prepare($sql);
$stmt->execute([
':name' => $name,
':email' => $email,
':message' => $message,
]);
プリペアドステートメントは、SQL文と値を分けて扱う仕組みです。
これにより、ユーザーが入力した値を安全にSQLへ渡しやすくなります。
フォームから送られてきた値をSQL文字列に直接連結するのは危険です。
危険な考え方 ↓ SQL文字列にユーザー入力を直接くっつける 安全に近い考え方 ↓ SQL文を用意する 値はexecuteで渡す
SQLインジェクション対策の入口として、ここはかなり重要です。
実際にフォームから送信してみる
Laragonで以下のURLを開きます。
http://contact-form-app.test/
フォームに入力して送信します。
うまくいけば、thanks.php に移動します。
その後、phpMyAdminで inquiries テーブルを確認します。
入力した内容が保存されていれば成功です。
保存されたデータをSQLで確認する
phpMyAdminやMySQLクライアントで、以下のSQLを実行すると保存内容を確認できます。
SELECT id, name, email, message, created_at
FROM inquiries
ORDER BY id DESC;新しいお問い合わせが上に表示されるように、ORDER BY id DESC を使っています。
ここで、自分が送信した名前・メールアドレス・お問い合わせ内容が入っていればOKです。
curlからも保存できる
フォーム画面を使わず、curlから直接POSTしても保存できます。
curl -i -X POST http://contact-form-app.test/receive.php \
-d "name=Leon" \
-d "email=leon@example.com" \
-d "message=DB保存テストです" これで保存できるなら、ブラウザ画面を通らなくてもサーバー側の処理が動いていることが分かります。
ここでも、PHP側バリデーションとプリペアドステートメントが重要です。
今回のコードでまだ足りないもの
今回でDB保存はできました。
ただし、まだ実務完成形ではありません。
- DB保存失敗時のエラー処理が簡易的
- 管理画面や一覧画面がまだない
- CSRF対策用のトークンがまだない
- 二重送信の完全防止まではできていない
- メール送信とDB保存の両方を行う設計はまだ未対応
- 接続情報の管理は学習用MVPのまま
一気に全部やると重くなるので、今回はDB保存の基本に集中します。
小さく動かしてから、必要な防御や管理機能を足していきます。
LG流:保存は責任の始まり
DBに保存できるようになると、Webアプリ感が一気に出ます。
しかし、保存できるということは、責任も増えるということです。
受け取る ↓ チェックする ↓ 保存する ↓ あとから確認する ↓ 必要なら対応状況を管理する
フォーム送信は、送信ボタンで終わりではありません。
保存したデータをどう扱うかまで考えることで、実務のシステムに近づきます。
LG流で言うなら、保存はゴールではなく、管理の入口です。
今回わかったこと
今回は、お問い合わせ内容をMySQLに保存しました。
学んだことは以下です。
- MySQLはデータを保存するための仕組み
- お問い合わせ保存用のデータベースを作れる
- inquiriesテーブルに送信内容を保存できる
- PHPからPDOでMySQLへ接続できる
- INSERT文で新しいレコードを追加できる
- プリペアドステートメントで値を安全に渡せる
- DB保存後もPRGパターンで完了画面へ移動できる
- 保存できると、一覧表示や管理機能へ発展できる
ここまで来ると、お問い合わせフォームはかなりWebシステムらしくなります。
次回予告
次回は、保存したお問い合わせ内容を一覧表示します。
DBに保存しただけでは、まだ確認しづらい状態です。
保存したデータを取り出し、管理者が見られる一覧ページを作ります。
次回のテーマは、以下です。
【MySQL SELECT入門】保存したお問い合わせ内容を一覧表示する
保存するだけでなく、取り出して確認する。
ここから、問い合わせ管理システムの入口に入っていきます。