1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHPで学生管理に「追加機能」をつける(Docker編・CRUDのCreate)

1
Last updated at Posted at 2026-06-25

前回作った「学生一覧画面」(Docker編)に、学生の追加機能を付けます。

  • 一覧に「学生を追加」ボタンを置く
  • ボタンを押すと登録フォーム画面が出る
  • 登録すると一覧画面に戻り、追加した学生が表示される

という、CRUD の C(Create) にあたる部分です。SQLインジェクション対策(プリペアドステートメント)や、登録後の PRG(Post/Redirect/Get)パターン といった実務でも重要なポイントを押さえます。

前提

Docker編で作った以下の構成が動いている状態から始めます。

myportal/
├── docker-compose.yml
├── Dockerfile
├── init.sql
├── db.php                # 接続先 "db"
├── manage_students.php   # 学生一覧画面
└── style.css

students テーブルは次の通り。

CREATE TABLE students (
    s_id       VARCHAR(20)  PRIMARY KEY,
    name       VARCHAR(100) NOT NULL,
    department VARCHAR(50),
    semester   INT,
    email      VARCHAR(100)
);

ステップ1: 一覧に「追加」ボタンを置く

manage_students.php の見出しの下に、登録画面へのリンクを追加します。

    <h1>学生一覧</h1>
    <p><a class="btn" href="add_student.php"> 学生を追加</a></p>
    <table>

ステップ2: 登録画面 add_student.php

1つのファイルで「フォーム表示」と「登録処理」の両方を担当します。POSTで来たら登録、それ以外(GET)ならフォームを表示、という典型パターンです。

<?php
include 'db.php';

$error = "";

// フォームが送信されたら登録処理
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $s_id       = trim($_POST['s_id'] ?? '');
    $name       = trim($_POST['name'] ?? '');
    $department = trim($_POST['department'] ?? '');
    $semester   = trim($_POST['semester'] ?? '');
    $email      = trim($_POST['email'] ?? '');

    if ($s_id === '' || $name === '') {
        $error = "学籍番号と氏名は必須です。";
    } else {
        // SQLインジェクション対策のプリペアドステートメント
        $stmt = $con->prepare(
            "INSERT INTO students (s_id, name, department, semester, email) VALUES (?, ?, ?, ?, ?)"
        );
        $sem = ($semester === '') ? 0 : (int)$semester;
        $stmt->bind_param("sssis", $s_id, $name, $department, $sem, $email);

        try {
            $stmt->execute();
            // 登録成功 → 一覧画面に戻る(PRGパターン)
            header("Location: manage_students.php");
            exit;
        } catch (mysqli_sql_exception $e) {
            if ($e->getCode() == 1062) {            // 主キー重複
                $error = "その学籍番号は既に登録されています。";
            } else {
                $error = "登録に失敗しました: " . htmlspecialchars($e->getMessage());
            }
        }
    }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>学生を追加</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>学生を追加</h1>

    <?php if ($error !== ''): ?>
        <p class="error"><?= $error ?></p>
    <?php endif; ?>

    <form method="post" action="add_student.php" class="form">
        <label>学籍番号 <span class="req">*</span>
            <input type="text" name="s_id" value="<?= htmlspecialchars($_POST['s_id'] ?? '') ?>" required>
        </label>
        <label>氏名 <span class="req">*</span>
            <input type="text" name="name" value="<?= htmlspecialchars($_POST['name'] ?? '') ?>" required>
        </label>
        <label>学科
            <input type="text" name="department" value="<?= htmlspecialchars($_POST['department'] ?? '') ?>">
        </label>
        <label>セメスター
            <input type="number" name="semester" min="1" max="12" value="<?= htmlspecialchars($_POST['semester'] ?? '') ?>">
        </label>
        <label>メール
            <input type="email" name="email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">
        </label>

        <div class="actions">
            <button type="submit" class="btn">登録</button>
            <a class="btn btn-cancel" href="manage_students.php">キャンセル</a>
        </div>
    </form>
</body>
</html>

ここが重要なポイント

  1. プリペアドステートメント
    $con->prepare(...)bind_param(...) で値を埋め込む。文字列連結でSQLを組み立てないので、SQLインジェクションを防げる。"sssis" は各プレースホルダの型(s=文字列, i=整数)。

  2. PRG(Post/Redirect/Get)パターン
    登録成功後に header("Location: ...") でリダイレクトする。これをしないと、登録完了画面でブラウザを再読み込みしたときにフォームが再送信され二重登録になる。リダイレクトを挟むことで防げる。

  3. 重複エラーのハンドリング
    学籍番号は主キー。同じIDを登録するとMySQLがエラーコード 1062 を返すので、それを捕まえて分かりやすいメッセージを出す。

  4. 入力値の保持と表示
    エラー時もフォームに入力値を残す(value="<?= htmlspecialchars(...) ?>")。表示は必ず htmlspecialchars() を通してXSSを防ぐ。

ステップ3: CSS(ボタン・フォーム・エラー)

style.css に追記。

/* ボタン */
.btn {
    display:inline-block; padding:8px 16px; background:#4a90d9; color:#fff;
    text-decoration:none; border:none; border-radius:4px; cursor:pointer; font-size:14px;
}
.btn:hover { background:#3a7bc0; }
.btn-cancel { background:#999; }
.btn-cancel:hover { background:#777; }

/* フォーム */
.form {
    max-width:400px; background:#fff; padding:24px; border-radius:6px;
    box-shadow:0 1px 4px rgba(0,0,0,.1);
}
.form label { display:block; margin-bottom:14px; color:#333; font-size:14px; }
.form input {
    width:100%; padding:8px; margin-top:4px; box-sizing:border-box;
    border:1px solid #ccc; border-radius:4px;
}
.form .req { color:#e00; }
.actions { margin-top:8px; display:flex; gap:10px; }

/* エラー */
.error {
    color:#c0392b; background:#fdecea; padding:10px 14px;
    border-radius:4px; max-width:400px;
}

ステップ4: 動作確認

ファイルはボリュームマウントで即時反映されるので、再起動は不要。

http://localhost:8081/manage_students.php
  1. 「+ 学生を追加」ボタンを押す → 登録フォームが出る
  2. 学籍番号・氏名などを入れて「登録」
  3. 一覧画面に戻り、追加した学生が表示される

CLIでも確認できる。

curl -i -X POST http://localhost:8081/add_student.php \
  --data "s_id=ktu99&name=Tanaka&department=CS&semester=5&email=tanaka@example.com"
# → HTTP/1.1 302 Found / Location: manage_students.php が返ればOK

まとめ

CRUDの「C(Create)」を実装しました。要点は3つ。

  • プリペアドステートメント でSQLインジェクションを防ぐ
  • PRGパターン で二重登録を防ぐ
  • 主キー重複 をエラーハンドリングして親切なメッセージを出す

次は編集(Update)・削除(Delete)を付ければ、基本的なCRUDが揃います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?