Hoàn thiện bộ CRUD: form sửa điền sẵn dữ liệu cũ, lưu thay đổi bằng UPDATE, xóa bằng DELETE qua POST có xác nhận và CSRF token, kiểm tra số dòng bị ảnh hưởng và xử lý ràng buộc khóa ngoại.
- Tạo form sửa điền sẵn dữ liệu
- UPDATE bằng prepared statement, đọc affected_rows
- Xóa an toàn bằng POST + xác nhận + CSRF
- Xử lý lỗi khóa ngoại khi xóa; xóa mềm
- affected_rows
- Số dòng bị thay đổi bởi lệnh vừa chạy.
- Xóa mềm
- Đánh dấu đã xóa (deleted_at) thay vì xóa thật.
- CSRF token
- Chuỗi ngẫu nhiên chứng minh yêu cầu đến từ form của bạn.
Khái niệm và lý thuyết
Quy trình sửa: (1) GET sua.php?id=3 đọc bản ghi, điền vào form; (2) POST gửi dữ liệu mới; (3) kiểm tra hợp lệ; (4) UPDATE ... WHERE id = ?; (5) chuyển hướng. Thao tác xóa không được dùng liên kết GET (xoa.php?id=3): trình duyệt, công cụ tìm kiếm, hay một ảnh <img> trên trang khác có thể vô tình gọi đến. Dùng form POST kèm CSRF token.
Sửa sinh viên
<?php
// sua.php
session_start();
require_once __DIR__ . "/db_connect.php";
function e($s) { return htmlspecialchars((string)$s, ENT_QUOTES, "UTF-8"); }
$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT) ?: exit("Thiếu mã");
if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (!hash_equals($_SESSION["csrf"] ?? "", $_POST["csrf"] ?? "")) exit("Phiên không hợp lệ");
$ho_ten = trim($_POST["ho_ten"] ?? ""); $lop = trim($_POST["lop"] ?? ""); $email = trim($_POST["email"] ?? "");
if (mb_strlen($ho_ten) >= 3) {
$st = $conn->prepare("UPDATE sinhvien SET ho_ten = ?, lop = ?, email = ? WHERE id = ?");
$st->bind_param("sssi", $ho_ten, $lop, $email, $id);
$st->execute();
$_SESSION["flash"] = $st->affected_rows ? "Đã cập nhật" : "Không có thay đổi";
header("Location: danh-sach.php", true, 303); exit;
}
$loi = "Họ tên quá ngắn";
}
$st = $conn->prepare("SELECT * FROM sinhvien WHERE id = ?");
$st->bind_param("i", $id); $st->execute();
$sv = $st->get_result()->fetch_assoc() ?: exit("Không tìm thấy");
$_SESSION["csrf"] ??= bin2hex(random_bytes(16));
?>
<form method="post">
<input type="hidden" name="csrf" value="<?= e($_SESSION["csrf"]) ?>">
<p>Mã SV: <b><?= e($sv["ma_sv"]) ?></b> (không sửa)</p>
<p>Họ tên <input name="ho_ten" value="<?= e($_POST["ho_ten"] ?? $sv["ho_ten"]) ?>"> <?= e($loi ?? "") ?></p>
<p>Lớp <input name="lop" value="<?= e($_POST["lop"] ?? $sv["lop"]) ?>"></p>
<p>Email <input type="email" name="email" value="<?= e($_POST["email"] ?? $sv["email"]) ?>"></p>
<button>Lưu thay đổi</button> <a href="/danh-sach.php">Hủy</a>
</form>Xóa an toàn
<!-- Trong danh-sach.php, cột Thao tác -->
<form method="post" action="xoa.php" style="display:inline" onsubmit="return confirm('Xóa sinh viên này?')">
<input type="hidden" name="id" value="<?= (int)$r["id"] ?>">
<input type="hidden" name="csrf" value="<?= e($_SESSION["csrf"]) ?>">
<button>Xóa</button>
</form><?php
// xoa.php
session_start();
require_once __DIR__ . "/db_connect.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST" || !hash_equals($_SESSION["csrf"] ?? "", $_POST["csrf"] ?? "")) {
http_response_code(403); exit("Yêu cầu không hợp lệ");
}
$id = filter_input(INPUT_POST, "id", FILTER_VALIDATE_INT);
$st = $conn->prepare("DELETE FROM sinhvien WHERE id = ?");
$st->bind_param("i", $id);
try {
$st->execute();
$_SESSION["flash"] = $st->affected_rows ? "Đã xóa" : "Không tìm thấy bản ghi";
} catch (mysqli_sql_exception $e) {
// 1451: còn dữ liệu con tham chiếu (khóa ngoại RESTRICT)
$_SESSION["flash"] = $e->getCode() === 1451 ? "Không xóa được: sinh viên còn điểm" : "Lỗi khi xóa";
}
header("Location: danh-sach.php", true, 303);Xóa mềm
ALTER TABLE sinhvien ADD deleted_at DATETIME NULL;
-- "Xóa": đánh dấu
UPDATE sinhvien SET deleted_at = NOW() WHERE id = ?;
-- Mọi truy vấn hiển thị thêm điều kiện
SELECT * FROM sinhvien WHERE deleted_at IS NULL;
-- Khôi phục
UPDATE sinhvien SET deleted_at = NULL WHERE id = ?;Xóa mềm giữ lại lịch sử, cho phép khôi phục khi xóa nhầm; Laravel hỗ trợ sẵn bằng SoftDeletes.
Hiển thị thông báo flash (“Đã cập nhật”, “Đã xóa”) ở đầu trang danh sách; xem loạt PHP, Bài 14 phần session.
UPDATE/DELETE luôn có WHERE id = ?. Thiếu WHERE là sửa/xóa toàn bộ bảng.
- Hoàn thiện sua.php và xoa.php cho bảng sinhvien.
- Thử xóa sinh viên còn điểm khi FK là RESTRICT, rồi đổi sang CASCADE.
- Chuyển chức năng xóa sang xóa mềm và thêm trang “Thùng rác” để khôi phục.
- Viết chức năng sửa điểm hàng loạt cho một lớp trong một transaction.
- Form sửa: SELECT điền sẵn → POST → UPDATE ... WHERE id = ?.
- Xóa bằng POST + xác nhận + CSRF, không dùng liên kết GET.
- affected_rows cho biết có thay đổi hay không; mã 1451 là vướng khóa ngoại.
- Xóa mềm bằng deleted_at để khôi phục được.