【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.phpPOST受信・バリデーション・DB保存・リダイレクト
thanks.php送信完了画面
db.phpMySQL接続処理
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 テーブルには、以下のカラムを用意します。

カラム役割
id1件ごとの識別番号
nameお名前
emailメールアドレス
messageお問い合わせ内容
created_at送信日時

まずはMVPなので、最小限の構成にしています。

あとで対応状況やメモを追加するなら、statusadmin_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 テーブルに nameemailmessage を保存します。

: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入門】保存したお問い合わせ内容を一覧表示する

保存するだけでなく、取り出して確認する。

ここから、問い合わせ管理システムの入口に入っていきます。