こんにちは、かつコーチです。
ここまでの3回で、PDOでのMySQL接続・データ取得・プリペアドステートメントによる安全なSQLの書き方を学んできました。
今回はそれらを組み合わせて、Webアプリの基本であるCRUD処理を、PHPだけで一通り実装してみます。
CRUDとは、Create(登録)・Read(一覧・取得)・Update(更新)・Delete(削除)の頭文字を取った言葉で、会員管理・投稿管理など、ほとんどのWebアプリの土台になる処理です。
sample_dbのusersテーブルを引き続き使いながら、4つの処理を順番に作っていきましょう。
事前準備:共通の接続処理をまとめる
db.phpとして接続処理を切り出す
4つのファイルすべてで接続処理を書くのは非効率なので、まず共通の接続処理をdb.phpとして独立させます。
<?php
// db.php
function getPdo(): PDO
{
$dsn = 'mysql:host=localhost;dbname=sample_db;charset=utf8mb4';
try {
return new PDO($dsn, 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
exit('データベース接続エラー: ' . $e->getMessage());
}
}
?>
こうしておくことで、この後作る4つのファイルはrequire 'db.php';とgetPdo()の呼び出しだけで接続を使い回せます。
Create:登録処理を作る
insert.phpでユーザーを新規登録する
まずは新しいユーザーを登録する処理です。
<?php
// insert.php
require 'db.php';
$pdo = getPdo();
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
if ($name === '' || $email === '') {
exit('名前とメールアドレスを入力してください');
}
$sql = 'INSERT INTO users (name, email) VALUES (:name, :email)';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();
echo '登録が完了しました(ID: ' . $pdo->lastInsertId() . ')';
?>
前回の記事で学んだプリペアドステートメントを使い、フォームから送られてきた値を安全にINSERTしています。
$pdo->lastInsertId()を使うと、直前にINSERTしたレコードのIDを取得できるので、登録直後にそのIDを使って別の処理(詳細画面への遷移など)につなげることができます。
Read:一覧・詳細を表示する
list.phpで全件を一覧表示する
登録したデータを一覧で見られるようにします。
<?php
// list.php
require 'db.php';
$pdo = getPdo();
$stmt = $pdo->query('SELECT * FROM users ORDER BY id DESC');
$users = $stmt->fetchAll();
?>
<table border="1">
<tr><th>ID</th><th>名前</th><th>メールアドレス</th></tr>
<?php foreach ($users as $user): ?>
<tr>
<td><?= htmlspecialchars($user['id']) ?></td>
<td><?= htmlspecialchars($user['name']) ?></td>
<td><?= htmlspecialchars($user['email']) ?></td>
</tr>
<?php endforeach; ?>
</table>
ORDER BY id DESCをつけることで、新しく登録したユーザーが一覧の上に表示されるようにしています。
htmlspecialchars()でデータを出力しているのは、データベースの中身に<script>のようなHTMLタグが紛れ込んでいた場合のXSS(クロスサイトスクリプティング)対策です。
SQLインジェクション対策だけでなく、画面に表示する際のエスケープも忘れないようにしましょう。
Update:更新処理を作る
update.phpで指定したIDのユーザーを更新する
登録済みのユーザー情報を書き換える処理です。
<?php
// update.php
require 'db.php';
$pdo = getPdo();
$id = $_POST['id'] ?? '';
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
if ($id === '' || $name === '' || $email === '') {
exit('IDと名前とメールアドレスを入力してください');
}
$sql = 'UPDATE users SET name = :name, email = :email WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();
echo '更新が完了しました(' . $stmt->rowCount() . '件)';
?>
WHERE id = :idを忘れると、テーブル内の全レコードが同じ内容に上書きされてしまうので、UPDATE文を書くときはWHERE句を必ず確認する習慣が大切です。
$stmt->rowCount()を使うと、実際に何件のレコードが更新されたかを確認できるので、「更新したはずなのに反映されない」といった場合の切り分けにも役立ちます。
Delete:削除処理を作る
delete.phpで指定したIDのユーザーを削除する
最後に削除処理です。
<?php
// delete.php
require 'db.php';
$pdo = getPdo();
$id = $_POST['id'] ?? '';
if ($id === '') {
exit('削除するIDを指定してください');
}
$sql = 'DELETE FROM users WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
echo '削除が完了しました(' . $stmt->rowCount() . '件)';
?>
UPDATE文と同じく、WHERE句を書き忘れると全件が消えてしまう、取り返しのつかない処理です。
つまずきやすいポイント:WHERE句なしのUPDATEで全件が上書きされた
実際にやってしまった事故
これは私が実際にローカル環境でやってしまった、今でも忘れられない失敗です。
❌ Before:WHERE句を書き忘れる
<?php
// 修正前:WHERE句がない
$sql = 'UPDATE users SET name = :name';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', 'テスト太郎', PDO::PARAM_STR);
$stmt->execute();
?>
1人分のユーザー名を変更したかっただけなのに、WHERE id = :idを書き忘れたまま実行してしまい、テーブル内の全ユーザーの名前が「テスト太郎」に統一されてしまいました。
幸い学習用のローカル環境だったので実害はありませんでしたが、これが本番環境の会員テーブルだったらと思うとぞっとします。
✅ After:必ずWHERE句で対象を絞り込む
<?php
// 修正後:WHERE句で対象を1件に絞る
$sql = 'UPDATE users SET name = :name WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', 'テスト太郎', PDO::PARAM_STR);
$stmt->bindValue(':id', 3, PDO::PARAM_INT);
$stmt->execute();
?>
この経験以来、私はUPDATEやDELETEを書くときは、先に同じ条件でSELECT文を実行し、対象が想定した件数かを確認してから書き換える、というルールを自分に課すようになりました。
$stmt->rowCount()が想定より多い数を返したときは、WHERE句を疑うサインとして覚えておいてください。
まとめ
この記事のポイント
- CRUDはCreate・Read・Update・Deleteの4つの基本操作の総称
- 接続処理を
db.phpとして切り出すと、4つのファイルで使い回せる - INSERT・UPDATE・DELETEはすべてプリペアドステートメントで安全に書く
- 一覧表示では
htmlspecialchars()によるXSS対策も忘れない - UPDATE・DELETEの
WHERE句の書き忘れは、全件を書き換える重大事故につながる
次に読むべき記事
ここまで、接続・取得・安全なSQL・CRUDと一通り実装してきましたが、正直かなりのコード量になったと感じませんでしたか。
次回は、この「素のPHPでDB操作をするのがなぜこんなに大変なのか」を一度整理してみます。
→ 次の記事:PHPでのDB接続、なぜこれが大変なのか