列表頁 (list.php) → 勾選 ID → 送出 → 處理頁 (delete.php) → 驗證/刪除 → 導回列表
<?php
// list.php
$pdo = new PDO('mysql:host=localhost;dbname=school;charset=utf8', 'root', 'password');
$stmt = $pdo->query('SELECT sID, sName, sSex, sPhone FROM student');
$students = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>學生名單</title></head><body>
<form action="delete.php" method="post">
<table border="1" cellpadding="5" cellspacing="0">
<thead>
<tr><th>選擇</th><th>學號</th><th>姓名</th><th>性別</th><th>電話</th></tr>
</thead>
<tbody>
<?php foreach ($students as $stu): ?>
<tr>
<td><input type="checkbox" name="ids[]" value="<?= htmlspecialchars($stu['sID']) ?>"></td>
<td><?= htmlspecialchars($stu['sID']) ?></td>
<td><?= htmlspecialchars($stu['sName']) ?></td>
<td><?= htmlspecialchars($stu['sSex']) ?></td>
<td><?= htmlspecialchars($stu['sPhone']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p><input type="submit" value="刪除選取" onclick="return confirm('確定刪除選中的學生嗎?')"></p>
</form>
</body></html>
<?php
// delete.php
declare(strict_types=1);
$pdo = new PDO('mysql:host=localhost;dbname=school;charset=utf8', 'root', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false, // 真實預處理
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// 1. 取得並驗證 ID 陣列
$ids = $_POST['ids'] ?? [];
if (!is_array($ids) || empty($ids)) {
die('未選擇任何記錄');
}
// 2. 過濾:只保留正整數
$ids = array_filter($ids, fn($v) => ctype_digit((string)$v));
if (empty($ids)) {
die('無效的 ID 參數');
}
// 3. 建立佔位符字串 (?,?,...)
$placeholders = implode(',', array_fill(0, count($ids), '?'));
// 4. 事務執行刪除
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("DELETE FROM student WHERE sID IN ($placeholders)");
$stmt->execute($ids);
$deleted = $stmt->rowCount();
$pdo->commit();
echo "<p>成功刪除 $deleted 筆記錄</p>";
} catch (PDOException $e) {
$pdo->rollBack();
error_log('刪除失敗: ' . $e->getMessage());
echo "<p style='color:red'>刪除過程發生錯誤,已回滾</p>";
}
echo '<p><a href="list.php">返回列表</a></p>';
// 資料表新增欄位
ALTER TABLE student ADD COLUMN deleted_at DATETIME NULL DEFAULT NULL;
ALTER TABLE student ADD INDEX idx_deleted (deleted_at);
// 刪除邏輯改為 UPDATE
$stmt = $pdo->prepare("UPDATE student SET deleted_at = NOW() WHERE sID IN ($placeholders)");
// 同步寫入審計表
$auditStmt = $pdo->prepare('INSERT INTO audit_log (action, table_name, record_ids, operator, created_at) VALUES (?,?,?,?,NOW())');
$auditStmt->execute(['soft_delete', 'student', json_encode($ids), $_SESSION['admin_id'] ?? 'unknown']);
// 查詢時自動排除軟刪除
$stmt = $pdo->query('SELECT * FROM student WHERE deleted_at IS NULL');
<!-- list.php 表單上方加入 -->
<button type="button" id="selectAll">全選</button>
<button type="button" id="invertSelect">反選</button>
<script>
document.getElementById('selectAll').onclick = () =>
document.querySelectorAll('input[name="ids[]"]').forEach(cb => cb.checked = true);
document.getElementById('invertSelect').onclick = () =>
document.querySelectorAll('input[name="ids[]"]').forEach(cb => cb.checked = !cb.checked);
// AJAX 刪除(可選)
document.querySelector('form').onsubmit = async (e) => {
e.preventDefault();
const ids = Array.from(document.querySelectorAll('input[name="ids[]"]:checked')).map(cb => cb.value);
if (!ids.length) return alert('請先勾選');
if (!confirm(`確定刪除 ${ids.length} 筆記錄?`)) return;
const res = await fetch('delete_ajax.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ids})
});
const data = await res.json();
alert(data.msg);
if (data.success) location.reload();
};
</script>
<?php
header('Content-Type: application/json');
$input = json_decode(file_get_contents('php://input'), true);
$ids = array_filter($input['ids'] ?? [], 'ctype_digit');
if (empty($ids)) {
echo json_encode(['success' => false, 'msg' => '無效 ID']);
exit;
}
$pdo = new PDO('mysql:host=localhost;dbname=school;charset=utf8', 'root', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("DELETE FROM student WHERE sID IN ($placeholders)");
$stmt->execute($ids);
echo json_encode(['success' => true, 'msg' => "刪除 {$stmt->rowCount()} 筆"]);
核取方塊批次刪除看似簡單,實則包含表單陣列傳遞、輸入驗證、SQL 注入防禦、事務一致性、軟刪除模式、審計追蹤、前端體驗優化等工程細節。掌握「佔位符動態生成 + PDO 事務 + 軟刪除」三板斧,即可構建安全、可維護、可擴展的批次操作功能。