[PHPのはじめ方] Part25: 簡単な掲示板アプリの作成

PHP

はじめに

これまでのステップで、PHPの基本的な文法、フォーム処理、データベース操作などを学んできました。いよいよ実践編です!💪 このステップでは、学んだ知識を総動員して、簡単な掲示板アプリを作成します。

この掲示板アプリでは、以下の機能を実現します。

  • 名前とメッセージを投稿できるフォーム
  • 投稿された名前とメッセージを一覧表示する機能

シンプルな機能ですが、Webアプリケーション開発の基本要素が詰まっています。さっそく挑戦してみましょう!🚀

1. データベースの準備 💾

まずは、投稿されたメッセージを保存するためのデータベースとテーブルを準備します。phpMyAdminなどのツールを使って、新しいデータベース(例: `php_bbs`)を作成し、その中に以下の構造を持つテーブル(例: `posts`)を作成しましょう。

カラム名 データ型 制約 説明
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY 投稿ID (自動採番)
name VARCHAR(50) NOT NULL 投稿者名
message TEXT NOT NULL メッセージ本文
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 投稿日時

SQLでテーブルを作成する場合は、以下のようになります。


CREATE TABLE posts (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  message TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
      

2. データベース接続設定 ⚙️

次に、PHPからデータベースに接続するための設定ファイルを作成します。config.php という名前でファイルを作成し、以下のようにデータベース接続情報を記述します。(実際の接続情報に合わせてください)


<?php
define('DB_HOST', 'localhost'); // データベースサーバーのホスト名
define('DB_NAME', 'php_bbs');   // データベース名
define('DB_USER', 'your_db_username'); // データベースユーザー名
define('DB_PASS', 'your_db_password'); // データベースパスワード

// データベース接続オプション
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // エラーモードを例外に設定
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // フェッチモードを連想配列に設定
    PDO::ATTR_EMULATE_PREPARES   => false,                  // プリペアドステートメントのエミュレーションを無効化
];

// データソース名 (DSN)
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";

try {
    // PDOインスタンスの作成
    $pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
} catch (PDOException $e) {
    // 接続エラー時の処理
    die("データベース接続に失敗しました: " . $e->getMessage());
}
?>
      

このファイルは、データベース操作が必要な他のPHPファイルから読み込んで使用します。

3. 掲示板のメインファイル作成 (index.php) 📄

いよいよ掲示板のメインとなるファイル `index.php` を作成します。このファイルは、投稿フォームの表示、投稿処理、投稿一覧の表示を担当します。

3.1. ファイルの基本構造と設定ファイルの読み込み


<!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="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css"> <!-- Bulma CSS -->
</head>
<body>
  <section class="section">
    <div class="container">
      <h1 class="title">📝 簡単掲示板</h1>

      <?php
      // データベース接続設定ファイルを読み込む
      require_once 'config.php';

      // エラーメッセージ格納用変数
      $errors = [];
      // 成功メッセージ格納用変数
      $success_message = '';

      // --- ここに投稿処理を記述 ---

      // --- ここに投稿一覧表示処理を記述 ---

      ?>

      <!-- 投稿フォーム -->
      <div class="box mt-5">
        <h2 class="subtitle is-5">メッセージを投稿する</h2>

        <?php // エラーメッセージの表示
        if (!empty($errors)): ?>
          <div class="notification is-danger is-light">
            <ul>
              <?php foreach ($errors as $error): ?>
                <li><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></li>
              <?php endforeach; ?>
            </ul>
          </div>
        <?php endif; ?>

        <?php // 成功メッセージの表示
        if (!empty($success_message)): ?>
          <div class="notification is-success is-light">
            <?php echo htmlspecialchars($success_message, ENT_QUOTES, 'UTF-8'); ?>
          </div>
        <?php endif; ?>

        <form action="index.php" method="post">
          <div class="field">
            <label class="label" for="name">お名前</label>
            <div class="control">
              <input class="input" type="text" id="name" name="name" required>
            </div>
          </div>

          <div class="field">
            <label class="label" for="message">メッセージ</label>
            <div class="control">
              <textarea class="textarea" id="message" name="message" rows="3" required></textarea>
            </div>
          </div>

          <div class="field">
            <div class="control">
              <button class="button is-primary" type="submit">投稿する</button>
            </div>
          </div>
        </form>
      </div>
      <!-- /投稿フォーム -->

      <!-- 投稿一覧 -->
      <div class="mt-5">
        <h2 class="subtitle is-5">投稿一覧</h2>
        <div id="post-list">
          <!-- ここに投稿一覧が表示される -->
        </div>
      </div>
      <!-- /投稿一覧 -->

    </div>
  </section>
</body>
</html>
      

まずはHTML部分と、設定ファイルの読み込み、メッセージ表示用の変数を準備します。フォームの `action` 属性は `index.php`(自分自身)、`method` 属性は `post` に設定します。

3.2. 投稿処理の実装 (POSTリクエストの処理)

フォームからデータが送信された (POSTリクエストが来た) 場合の処理を記述します。場所は `// — ここに投稿処理を記述 —` の部分です。


      // --- ここに投稿処理を記述 ---
      if ($_SERVER['REQUEST_METHOD'] === 'POST') {
          // フォームからのデータを取得し、サニタイズ
          $name = isset($_POST['name']) ? trim($_POST['name']) : '';
          $message = isset($_POST['message']) ? trim($_POST['message']) : '';

          // 簡単なバリデーション
          if ($name === '') {
              $errors[] = 'お名前を入力してください。';
          } elseif (mb_strlen($name) > 50) {
              $errors[] = 'お名前は50文字以内で入力してください。';
          }

          if ($message === '') {
              $errors[] = 'メッセージを入力してください。';
          }

          // エラーがない場合のみデータベースに挿入
          if (empty($errors)) {
              try {
                  // プリペアドステートメントを使用して安全にデータを挿入
                  $sql = "INSERT INTO posts (name, message) VALUES (:name, :message)";
                  $stmt = $pdo->prepare($sql);
                  $stmt->bindParam(':name', $name, PDO::PARAM_STR);
                  $stmt->bindParam(':message', $message, PDO::PARAM_STR);

                  // SQLを実行
                  if ($stmt->execute()) {
                      $success_message = 'メッセージを投稿しました!🎉';
                      // フォームの再送信を防ぐためにリダイレクト(任意)
                      // header('Location: index.php');
                      // exit;
                  } else {
                      $errors[] = '投稿に失敗しました。もう一度お試しください。';
                  }
              } catch (PDOException $e) {
                  $errors[] = 'データベースエラー: ' . $e->getMessage();
                  // 本番環境では詳細なエラーメッセージをユーザーに見せないように注意
                  // error_log('PDO Error: ' . $e->getMessage()); // エラーログに記録
                  // $errors[] = 'データベースエラーが発生しました。';
              }
          }
      }
      // --- 投稿処理 ここまで ---
      

POSTリクエストかどうかを確認し、送信されたデータを `$_POST` スーパーグローバル変数から取得します。`trim()` で前後の空白を除去し、簡単なバリデーション(空チェック、文字数チェック)を行います。

エラーがなければ、PDOを使ってデータベースにデータを挿入します。SQLインジェクション対策として、必ずプリペアドステートメント (`prepare`, `bindParam`, `execute`) を使用しましょう。

投稿成功時には成功メッセージを設定します。コメントアウトされている `header()` 関数を使うと、投稿後にページをリダイレクトさせ、ブラウザの更新ボタンによる意図しない再投稿(二重投稿)を防ぐことができます(今回はシンプルにするためリダイレクトは行いません)。

3.3. 投稿一覧表示処理の実装

次に、データベースから投稿データを取得し、一覧表示する処理を記述します。場所は `// — ここに投稿一覧表示処理を記述 —` の部分です。


      // --- ここに投稿一覧表示処理を記述 ---
      try {
          // postsテーブルからデータを新しい順に取得
          $sql_select = "SELECT name, message, created_at FROM posts ORDER BY created_at DESC";
          $stmt_select = $pdo->query($sql_select); // クエリを実行
          $posts = $stmt_select->fetchAll(); // 結果をすべて取得

      } catch (PDOException $e) {
          // エラー発生時はエラーメッセージを表示して処理を中断
          echo '<div class="notification is-danger">投稿の読み込みに失敗しました: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . '</div>';
          $posts = []; // エラー時は空の配列にする
          // ここで処理を中断する場合は exit; などを使用
      }
      // --- 投稿一覧表示処理 ここまで ---
      

`SELECT` 文で `posts` テーブルから必要なカラム (`name`, `message`, `created_at`) を取得します。`ORDER BY created_at DESC` で新しい投稿が上に表示されるように並び替えます。 取得したデータは `$posts` 配列に格納します。

最後に、取得した投稿データをHTMLとして表示する部分(`<div id=”post-list”>` の中)を修正します。


      <!-- 投稿一覧 -->
      <div class="mt-5">
        <h2 class="subtitle is-5">投稿一覧</h2>
        <div id="post-list">
          <?php if (empty($posts)): ?>
            <p>まだ投稿はありません。</p>
          <?php else: ?>
            <?php foreach ($posts as $post): ?>
              <article class="message is-info mb-4">
                <div class="message-header">
                  <p>
                    <strong><?php echo htmlspecialchars($post['name'], ENT_QUOTES, 'UTF-8'); ?></strong>
                    <small class="ml-2">(<?php echo htmlspecialchars($post['created_at'], ENT_QUOTES, 'UTF-8'); ?>)</small>
                  </p>
                </div>
                <div class="message-body">
                  <?php echo nl2br(htmlspecialchars($post['message'], ENT_QUOTES, 'UTF-8')); ?>
                </div>
              </article>
            <?php endforeach; ?>
          <?php endif; ?>
        </div>
      </div>
      <!-- /投稿一覧 -->
      

`$posts` 配列を `foreach` でループし、各投稿を表示します。ここで重要なのは、ユーザーが入力した内容(名前やメッセージ)をHTMLに出力する際に、必ず `htmlspecialchars()` 関数を使ってXSS(クロスサイトスクリプティング)対策を行うことです。これにより、悪意のあるHTMLタグやJavaScriptコードが埋め込まれるのを防ぎます。

また、メッセージ本文は改行がそのまま表示されるように `nl2br()` 関数も併用しています。

4. 実行してみよう! ▶️

これで、`config.php` と `index.php` の2つのファイルが完成しました。これらのファイルをWebサーバー(XAMPPやMAMPのhtdocs/wwwディレクトリなど)に配置し、Webブラウザから `index.php` にアクセスしてみてください。

フォームが表示され、名前とメッセージを入力して投稿すると、下に一覧表示されるはずです。🎉

まとめと次のステップ ✨

お疲れ様でした!簡単な掲示板アプリを作成することで、PHPによるWebアプリケーション開発の一連の流れを体験できましたね。

  • HTMLフォームの作成
  • POSTデータの受け取りと処理
  • データベースへの接続と操作 (PDO, プリペアドステートメント)
  • データベースからのデータ取得と表示
  • 基本的なセキュリティ対策 (XSS対策, SQLインジェクション対策)

これらの要素は、より複雑なWebアプリケーションを開発する上での基礎となります。

今回作成した掲示板は非常にシンプルですが、ここからさらに機能を拡張していくことができます。例えば…

  • 投稿の編集・削除機能
  • パスワードによる投稿の保護
  • ページネーション(投稿が多い場合にページを分ける)
  • 画像のアップロード機能

次のステップでは、ログイン機能の実装に挑戦してみましょう! 💪

コメント

タイトルとURLをコピーしました