Webサイト制作において、profile.php?id=1
やproduct.php?id=123
のように、URLの一部を変えるだけで表示される内容が変わる「動的なページ」は、アプリケーションの核となる機能です。
今回は、GETリクエストで渡されたIDをもとに、データベースから特定の会員情報を表示するプロフィールページの作り方を解説します。
この課題を通じて、複数のテーブルから関連するデータを一度に取得するための強力なSQLコマンド「JOIN
」の使い方をマスターしましょう。
1. データベースの設計:テーブルの正規化
まず、データを格納するデータベースのテーブルを準備します。今回は「会員(members
)」と「所属クラブ(clubs
)」の情報を扱います。
ここで重要なのは、「会員情報」と「クラブ情報」を別々のテーブルに分けて管理することです。なぜなら、もし同じクラブに100人の会員が所属していた場合、100人分のデータに同じクラブ名や活動概要を重複して保存するのは非効率で、修正も大変だからです。
このように、関連するデータごとにテーブルを分割して、重複を減らすことを「正規化」と呼びます。
【テーブル設計】
clubs
テーブル: クラブ情報を管理id
(主キー, INT, AUTO_INCREMENT)name
(クラブ名, VARCHAR)overview
(活動概要, TEXT)
members
テーブル: 会員情報を管理id
(主キー, INT, AUTO_INCREMENT)name
(会員名, VARCHAR)age
(年齢, INT)club_id
(所属クラブID, INT) ← これが2つのテーブルを繋ぐ鍵
members
テーブルのclub_id
カラムに、clubs
テーブルのid
を保存することで、「どの会員がどのクラブに所属しているか」という関連性を持たせます。
2. SQLのJOIN
句でテーブルを結合する
別々のテーブルに保存された会員情報とクラブ情報を一度に取得するには、SQLのJOIN
句を使います。JOIN
は、指定した条件(キー)が一致する行同士を、複数のテーブルから連結してくれる機能です。
【JOIN
を使ったSQLの基本構造】
SELECT
取得したいカラム名1, 取得したいカラム名2, ...
FROM
テーブル1
JOIN
テーブル2 ON テーブル1.関連キー = テーブル2.関連キー
WHERE
条件;
ON members.club_id = clubs.id
: これが結合条件です。「members
テーブルのclub_id
と、clubs
テーブルのid
が一致する行を連結してください」という意味になります。
このJOIN
を使うことで、ID=1の会員の「名前」と、その会員が所属するクラブの「クラブ名」を、たった一度の問い合わせで取得できます。
3. PHPでプロフィールページを実装する
それでは、実際にPHPでプロフィールページを実装していきましょう。1つのファイル(profile.php
)に、ID入力フォームとプロフィール表示の両方の機能を持たせます。
【処理の流れ】
- URLに
id
パラメータ(profile.php?id=1
など)があるかチェックする。 id
があれば、そのIDを使ってデータベースに問い合わせる。JOIN
を使ってmembers
テーブルとclubs
テーブルから関連データを取得する。- 取得したデータをHTMLに表示する。
id
がない場合や、該当するデータが見つからない場合は、その旨をメッセージで表示する。
完成版コード
【profile.php
】
<?php
// ===== 変数の初期化 =====
$member_data = null;
$error_message = '';
$id = '';
// ===== データベース接続情報 =====
$dsn = 'mysql:dbname=php_learning_db;host=localhost;charset=utf8mb4';
$user = 'root';
$password = ''; // XAMPPのデフォルトは空パスワード
// ===== GETリクエストでIDが指定されている場合の処理 =====
if (isset($_GET['id']) && !empty($_GET['id'])) {
try {
$id = (int)$_GET['id']; // IDを整数に変換
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// JOINを使ったSQL文の準備
$sql = <<<SQL
SELECT
m.name AS member_name,
m.age,
c.name AS club_name,
c.overview
FROM
members AS m
JOIN
clubs AS c ON m.club_id = c.id
WHERE
m.id = :id
LIMIT 1
SQL;
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$member_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$member_data) {
$error_message = '指定されたIDの会員は見つかりませんでした。';
}
} catch (PDOException $e) {
$error_message = 'データベースエラー: ' . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>会員プロフィール</title>
<style>
body { font-family: sans-serif; }
.container { width: 80%; margin: 20px auto; }
.profile-table { border-collapse: collapse; width: 100%; margin-top: 20px; }
.profile-table th, .profile-table td { border: 1px solid #ccc; padding: 10px; }
.profile-table th { background-color: #f2f2f2; text-align: right; width: 30%; }
.search-form { background-color: #f9f9f9; padding: 15px; border: 1px solid #ccc; }
.error { color: #d9534f; }
</style>
</head>
<body>
<div class="container">
<h1>会員プロフィール検索</h1>
<div class="search-form">
<p>確認したい会員のIDを入力してください。</p>
<form action="" method="GET">
<input type="number" name="id" value="<?php echo htmlspecialchars($id, ENT_QUOTES, 'UTF-8'); ?>">
<input type="submit" value="プロフィールを確認する">
</form>
</div>
<hr>
<?php if ($member_data): ?>
<h2>会員ID: <?php echo htmlspecialchars($id, ENT_QUOTES, 'UTF-8'); ?> のプロフィール</h2>
<table class="profile-table">
<tr>
<th>お名前</th>
<td><?php echo htmlspecialchars($member_data['member_name'], ENT_QUOTES, 'UTF-8'); ?></td>
</tr>
<tr>
<th>年齢</th>
<td><?php echo htmlspecialchars($member_data['age'], ENT_QUOTES, 'UTF-8'); ?></td>
</tr>
<tr>
<th>所属クラブ</th>
<td><?php echo htmlspecialchars($member_data['club_name'], ENT_QUOTES, 'UTF-8'); ?></td>
</tr>
<tr>
<th>クラブ概要</th>
<td><?php echo nl2br(htmlspecialchars($member_data['overview'], ENT_QUOTES, 'UTF-8')); ?></td>
</tr>
</table>
<?php elseif ($error_message): ?>
<p class="error"><?php echo $error_message; ?></p>
<?php endif; ?>
</div>
</body>
</html>
SQL文の中でmembers AS m
のようにAS
を使うと、テーブル名に短い別名を付けることができ、クエリが読みやすくなります。
まとめ
今回は、GETリクエストとデータベースのJOIN
を組み合わせた、動的なプロフィールページの作り方を学びました。
【重要ポイントの振り返り】
- URLのパラメータで表示内容を切り替えるにはGETリクエストが最適。
- 関連するデータはテーブルを分割して管理し、外部キーで関連付ける。
- 複数のテーブルからデータを一度に取得するには**
JOIN
句**を使用する。 - URLから受け取った値もユーザー入力の一種。必ずプリペアドステートメントで安全に処理する。
このパターンを応用すれば、商品詳細ページや記事ページなど、あらゆる動的ページの基本を実装できます。次のステップとして、全会員を一覧表示し、各会員の名前をクリックするとその人のプロフィールページに飛ぶ、といった連携機能に挑戦してみるのも良いでしょう。