PHP và MySQL Bài 24: Thực hiện CRUD hoàn chỉnh bằng PHP và PDO (Thêm, Đọc, Sửa, Xóa)
Chào mừng bạn đến với trái tim của mọi ứng dụng Backend: CRUD.
Như đã đề cập ở Bài 19, CRUD là viết tắt của Create (Thêm), Read (Đọc), Update (Sửa) và Delete (Xóa).
Trong bài học này, chúng ta sẽ kết hợp đối tượng $pdo (đã khởi tạo ở Bài 23) với các câu lệnh SQL để xây dựng một luồng quản lý danh sách thiết bị (Ví dụ: Máy bán vé tự động - TVM) hoàn chỉnh.
Lưu ý chuẩn bị: Giả định rằng bạn đã có file
database.phpchứa đoạn code kết nối PDO thành công như ở bài trước. Trong các file xử lý dưới đây, chúng ta sẽ dùng lệnhrequire 'database.php';để gọi kết nối này vào.
1. R - Read (Truy xuất danh sách dữ liệu)
Để đọc dữ liệu từ MySQL và hiển thị ra giao diện (như danh sách báo cáo), chúng ta sử dụng phương thức query() của PDO.
Ví dụ: Lấy danh sách toàn bộ máy bán vé đang hoạt động.
<?php
// 1. Gọi file kết nối CSDL
require 'database.php';
try {
// 2. Viết câu lệnh SQL
$sql = "SELECT id, machine_code, status FROM tvm_machines WHERE status = 1";
// 3. Thực thi truy vấn bằng hàm query()
$stmt = $pdo->query($sql);
// 4. Lấy toàn bộ dữ liệu trả về dưới dạng Mảng kết hợp (Associative Array)
$machines = $stmt->fetchAll();
// 5. Hiển thị dữ liệu (Trong thực tế, đoạn này sẽ được nhúng vào HTML)
echo "<h3>Danh sách Máy bán vé đang hoạt động:</h3>";
echo "<ul>";
foreach ($machines as $machine) {
echo "<li>Mã máy: " . $machine['machine_code'] . " - Trạng thái: Bình thường</li>";
}
echo "</ul>";
} catch (PDOException $e) {
echo "Lỗi truy vấn: " . $e->getMessage();
}
?>
Giải thích hàm:
$pdo->query($sql): Gửi thẳng câu lệnh SQL lên MySQL để thực thi.fetchAll(): Gom tất cả các dòng kết quả (Rows) trả về thành một mảng đa chiều trong PHP để dễ dàng dùng vòng lặpforeachduyệt qua.
2. C - Create (Thêm dữ liệu mới)
Khi người dùng điền thông tin vào Form để thêm một máy bán vé mới, dữ liệu đó mang tính động (từ $_POST).
⚠️ Quy tắc sinh tử: Bất cứ khi nào SQL có chứa dữ liệu động (biến), bạn KHÔNG ĐƯỢC PHÉP nối chuỗi trực tiếp vào SQL để dùng hàm
query(). Bạn bắt buộc phải dùng hàmprepare()(Chuẩn bị) vàexecute()(Thực thi) để chống bị hack. (Chi tiết kỹ thuật chống hack này sẽ được giải phẫu sâu ở Bài 25).
Ví dụ: Thêm một máy TVM mới vào hệ thống.
<?php
require 'database.php';
// Giả lập dữ liệu nhận được từ Form POST
$newMachineCode = "TVM_BT_02";
$stationId = 1;
try {
// 1. Viết SQL với các "Tham số ảo" (Dấu chấm hỏi ?)
$sql = "INSERT INTO tvm_machines (machine_code, station_id, status) VALUES (?, ?, 1)";
// 2. Chuẩn bị câu lệnh (Prepare)
$stmt = $pdo->prepare($sql);
// 3. Thực thi câu lệnh (Execute) và truyền mảng dữ liệu thật vào thay thế cho dấu ?
$stmt->execute([$newMachineCode, $stationId]);
// 4. Lấy ID của dòng vừa được thêm thành công
$lastInsertId = $pdo->lastInsertId();
echo "Đã thêm máy bán vé thành công! ID mới là: " . $lastInsertId;
} catch (PDOException $e) {
echo "Lỗi thêm dữ liệu: " . $e->getMessage();
}
?>
3. U - Update (Cập nhật dữ liệu)
Quy trình Update hoàn toàn tương tự như Create. Chúng ta vẫn sử dụng bộ đôi prepare() và execute() để truyền dữ liệu một cách an toàn.
Ví dụ: Cập nhật trạng thái máy bán vé sang "Bảo trì" (status = 0).
<?php
require 'database.php';
// Giả lập dữ liệu từ người quản trị gửi lên
$machineIdToUpdate = 101;
$newStatus = 0; // 0 là Bảo trì
try {
// Sử dụng "Tham số định danh" (:ten_tham_so) thay vì dấu chấm hỏi (?) cho dễ đọc
$sql = "UPDATE tvm_machines SET status = :trang_thai WHERE id = :ma_id";
$stmt = $pdo->prepare($sql);
// Thực thi bằng cách truyền mảng ánh xạ Key => Value
$stmt->execute([
':trang_thai' => $newStatus,
':ma_id' => $machineIdToUpdate
]);
// Kiểm tra xem có bao nhiêu dòng dữ liệu thực sự bị thay đổi
$rowCount = $stmt->rowCount();
if ($rowCount > 0) {
echo "Đã cập nhật trạng thái bảo trì cho " . $rowCount . " thiết bị.";
} else {
echo "Không có thiết bị nào bị thay đổi (Có thể ID không tồn tại).";
}
} catch (PDOException $e) {
echo "Lỗi cập nhật: " . $e->getMessage();
}
?>
4. D - Delete (Xóa dữ liệu)
Như đã đề cập ở Bài 19, thao tác xóa dữ liệu vật lý khỏi CSDL thường không được khuyến khích trong các hệ thống lớn. Tuy nhiên, nếu nghiệp vụ bắt buộc (như xóa một dòng log lỗi sai), quy trình xử lý với PDO như sau:
<?php
require 'database.php';
$machineIdToDelete = 103; // ID máy cần xóa
try {
$sql = "DELETE FROM tvm_machines WHERE id = ?";
$stmt = $pdo->prepare($sql);
// Truyền biến vào theo đúng thứ tự của dấu ?
$stmt->execute([$machineIdToDelete]);
if ($stmt->rowCount() > 0) {
echo "Đã xóa thiết bị thành công khỏi hệ thống.";
} else {
echo "Không tìm thấy thiết bị để xóa.";
}
} catch (PDOException $e) {
echo "Lỗi xóa dữ liệu: " . $e->getMessage();
}
?>
Tổng kết Bài 24
Bạn đã chính thức nắm giữ quyền năng thao túng toàn bộ dữ liệu của hệ thống:
- Dùng
$pdo->query()vàfetchAll()để Đọc. - Dùng
$pdo->prepare()vàexecute()để Thêm, Sửa, Xóa. - Dùng
$stmt->rowCount()để biết số lượng dữ liệu bị ảnh hưởng.
Trong các ví dụ trên, tôi đã liên tục nhấn mạnh việc không được nối chuỗi biến trực tiếp vào SQL mà phải dùng dấu ? hoặc :tham_so qua hàm prepare(). Tại sao điều này lại sinh tử đến vậy? Nếu không làm thế, điều tồi tệ gì sẽ xảy ra?
Bài học tiếp theo sẽ giải mã toàn bộ bí mật đó. Đón chờ Bài 25: Kỹ thuật Prepared Statements - Vũ khí tối thượng chống lại lỗ hổng SQL Injection.
All rights reserved