Webアプリケーションを運営していると、古くなった投稿や不要になったデータを削除したい場面が出てきます。データベースから特定のレコードを削除するには、SQLのDELETE
文を使います。
しかし、削除処理の実装は、細心の注意を払わなければならない、非常にデリケートな作業です。安易に実装すると、誤操作や悪意のある攻撃によって、意図せず重要なデータを全て失ってしまう危険性があります。
この記事では、DELETE
文の基本的な使い方と、誤削除を防ぎ、安全性を確保するための「確認画面を挟む」という必須のプロセスについて解説します。
1. DELETE
文の基本と最大の注意点
DELETE
は、テーブルからレコード(行)を削除するためのSQL命令です。
基本構文: DELETE FROM テーブル名 WHERE 条件;
ここで、UPDATE
文と同様に絶対に忘れてはならないのがWHERE
句です。もしWHERE
句を付けずにDELETE FROM reviews;
のようなSQLを実行してしまうと、テーブル内の全レコードが削除されてしまいます。
そのため、削除対象をid
などで一意に特定するWHERE
句は、削除処理の生命線と言えます。
2. なぜ危険?リンクだけで削除する実装の問題点
初心者の方がやりがちな実装として、一覧ページに<a href="delete.php?id=5">削除</a>
のようなリンクを置き、delete.php
が$_GET
でIDを受け取って即座にDELETE
文を実行してしまう、というものがあります。
この方法は、以下の理由から極めて危険です。
- 誤クリック: ユーザーが誤って削除リンクをクリックしただけで、データが即座に消えてしまいます。
- クローラによる誤作動: Googleなどの検索エンジンが見つけたリンクを辿ることで、意図せずデータが削除されてしまう可能性があります。
- 悪意のある攻撃(CSRF): 攻撃者が、ログイン中の管理者に悪意のあるリンク(例:
<img src="yoursite.com/delete.php?id=10">
)を踏ませることで、管理者が意図しないままデータを削除させることができてしまいます。
鉄則:データの状態を変更する操作(特に削除)は、GETリクエスト(リンクをクリックするだけ)で実行してはいけません。
3. 安全な削除プロセスの実装フロー
安全な削除処理は、ユーザーに「本当に削除しますか?」と意思確認を求めるステップを挟むのが基本です。
安全なフロー: [一覧ページ]
→ [削除確認ページ (GET)]
→ [削除実行スクリプト (POST)]
Step 1: 一覧ページに「削除確認」へのリンクを設置
まず、一覧表示の各行に、削除確認ページへのリンクを設置します。
list.php
のforeach
ループ内:
<td>
<a href="delete_confirm.php?id=<?php echo htmlspecialchars($review['id'], ENT_QUOTES, 'UTF-8'); ?>">
削除
</a>
</td>
Step 2: 削除確認ページを作成する
delete_confirm.php
では、削除対象の情報を表示し、本当に削除してよいかユーザーに確認を求めます。
delete_confirm.php
のサンプルコード:
<?php
// (try...catchやIDのバリデーション、DB接続処理は省略)
// $id を元に、削除対象のレビュー情報を$reviewとして取得済みと仮定
if (!$review) {
// レビューが見つからない場合の処理
exit('指定されたレビューは見つかりませんでした。');
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>レビュー削除確認</title>
</head>
<body>
<h1>レビュー削除確認</h1>
<p>以下のレビューを本当に削除しますか?</p>
<dl>
<dt>書籍名:</dt>
<dd><?php echo htmlspecialchars($review['book_title'], ENT_QUOTES, 'UTF-8'); ?></dd>
</dl>
<form action="delete_process.php" method="post">
<input type="hidden" name="id" value="<?php echo htmlspecialchars($review['id'], ENT_QUOTES, 'UTF-8'); ?>">
<button type="submit">はい、削除します</button>
</form>
<br>
<a href="list.php">いいえ、一覧に戻ります</a>
</body>
</html>
ポイント:
- 削除の意思決定は、
POST
メソッドのフォームで行います。 - どのIDを削除するかを次のスクリプトに伝えるため、
type="hidden"
でid
をフォームに含めます。
Step 3: 削除を実行するスクリプトを作成する
最後に、確認フォームからPOST
送信されたデータを受け取り、実際にDELETE
文を実行するスクリプトを作成します。
delete_process.php
のサンプルコード:
<?php
// POSTリクエスト以外は処理を中断
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit('不正なリクエストです。');
}
// (データベース接続設定 $dsn, $user, $password は定義済みと仮定)
try {
// hiddenで送られたidをバリデーションして受け取る
if (empty($_POST['id']) || !ctype_digit($_POST['id'])) {
throw new Exception('IDが不正です。');
}
$id = (int)$_POST['id'];
// データベースに接続
$pdo = new PDO($dsn, $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// DELETE文を準備・実行
$sql = "DELETE FROM reviews WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id]);
echo "ID: " . htmlspecialchars($id, ENT_QUOTES, 'UTF-8') . " のレビューを削除しました。";
} catch (Exception $e) {
echo "エラー発生: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
} finally {
$pdo = null;
}
?>
<br>
<a href="list.php">レビュー一覧に戻る</a>
ポイント:
- スクリプトの冒頭で
POST
リクエストかどうかをチェックし、不正なアクセスを防ぎます。 $_POST
から受け取ったid
を使い、安全なプリペアドステートメントでDELETE
を実行します。
まとめ
DELETE
文ではWHERE
句が絶対に必要。- データの削除のような重要な操作は、リンク(GET)だけで実行してはならない。
- **「確認ページ(GET)→フォーム送信(POST)→削除実行」**というフローを徹底することで、誤操作や攻撃のリスクを大幅に減らすことができる。
安全な削除処理の実装は、信頼性の高いWebアプリケーションを構築するための必須スキルです。この多段階のプロセスを必ず守るようにしましょう。