+1

Nhỏ nhưng có võ: Tại sao nên dùng exists() trước khi update() dữ liệu?

Chào anh em!

Trong quá trình phát triển các tính năng thao tác với cơ sở dữ liệu (Database), cập nhật dữ liệu (Update) là một trong những nghiệp vụ cơ bản và được sử dụng nhiều nhất. Tuy nhiên, có một cạm bẫy logic rất nhỏ mà nhiều lập trình viên (đặc biệt là người mới) thường bỏ qua: Chạy lệnh update mà không kiểm tra xem dữ liệu đó có thực sự tồn tại hay không.

Hôm nay, chúng ta sẽ cùng mổ xẻ vấn đề này và tìm hiểu tại sao nguyên tắc "Kiểm tra tồn tại (exists) -> Nếu có mới cập nhật (update)" lại là một tiêu chuẩn quan trọng khi viết code.

1. Vấn đề của kiểu "Cập nhật mù" (Blind Update)

Thông thường, khi nhận được yêu cầu cập nhật một bản ghi từ người dùng, một cách viết "ngây thơ" sẽ trông như thế này (ví dụ bằng mã giả):

// Nhận ID và dữ liệu mới từ request
$id = $request->input('id');
$newData = $request->input('data');

// Chạy thẳng lệnh update xuống Database
Model::where('id', $id)->update(['column' => $newData]);

// Trả về thông báo thành công
return response('Cập nhật thành công!');

Lỗ hổng ở đây là gì? Nếu người dùng (hoặc một hacker) cố tình gửi lên một $id ảo, không hề tồn tại trong hệ thống, hệ quản trị cơ sở dữ liệu (như MySQL, PostgreSQL) vẫn sẽ tiếp nhận câu lệnh UPDATE. Nó sẽ quét tìm $id đó, không thấy gì, và kết thúc mà không báo lỗi (số dòng bị ảnh hưởng = 0).

Tuy nhiên, đoạn code trên không hề bắt được trạng thái này và vẫn trả về thông báo: "Cập nhật thành công!". Điều này gây ra trải nghiệm người dùng rất tệ (Fake Success) và gây khó khăn cực lớn cho việc debug sau này.

2. Giải pháp: Nguyên tắc "Hỏi trước khi làm"

Để giải quyết triệt để vấn đề trên, chúng ta áp dụng luồng logic: Kiểm tra (Exists) -> Xử lý (Update).

Dưới đây là một ví dụ thực tế được viết bằng framework Laravel để xử lý cập nhật hàng loạt (Bulk Update):

$ids = $request->input('ids');
$transShipmentId = $request->input('trans_shipment_id');

// Khởi tạo câu truy vấn
$wardTransShipment = WardTransShipment::whereIn('id', $ids);

// Bước 1: KIỂM TRA TỒN TẠI
if ($wardTransShipment->exists()) {
    
    // Bước 2: THỰC THI CẬP NHẬT
    $wardTransShipment->update(['trans_shipment_id' => $transShipmentId]);
    
    // Trả về kết quả đúng với thực tế
    return back()->with('success', trans('custom.msg.update.success'));
}

// Bắt lỗi nếu dữ liệu không tồn tại
return back()->with('fail', trans('custom.msg.update.fail'));

3. Những lợi ích cốt lõi của phương pháp này

Việc thêm một dòng lệnh kiểm tra exists() mang lại 3 giá trị cực kỳ lớn cho chất lượng mã nguồn:

  • Tính chính xác của hệ thống (Data Integrity): Code của bạn sẽ luôn biết chính xác nó đang thao tác trên cái gì. Trạng thái phản hồi về cho người dùng (Thành công hay Thất bại) phản ánh đúng 100% những gì đã diễn ra trong Database.
  • Tối ưu hóa hiệu năng (Performance): Hàm exists() trong hầu hết các framework ORM đều được chuyển đổi thành câu lệnh SQL siêu nhẹ: SELECT EXISTS(...). Nó sẽ dừng ngay lập tức và trả về true khi tìm thấy bản ghi đầu tiên khớp điều kiện, thay vì phải quét toàn bộ bảng. Câu lệnh này tốn rất ít chi phí của hệ thống.
  • Bảo mật và an toàn dữ liệu: Trong nhiều trường hợp phức tạp, bạn không chỉ kiểm tra tồn tại, mà còn phải kiểm tra quyền sở hữu (Ví dụ: user này có quyền sửa bản ghi này không?). Khối lệnh if (exists) tạo ra một rào chắn an toàn trước khi lệnh update nguy hiểm được kích hoạt.

💡 Lời khuyên: Hãy biến việc "kiểm tra trước khi hành động" thành một thói quen vô điều kiện khi làm việc với Database. Một dòng code thêm vào hôm nay có thể cứu bạn khỏi hàng giờ ngồi cào đầu tìm lỗi (bug) trong tương lai.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí