PHP và MySQL Bài 40: Xử lý Form (POST), Bảo mật CSRF và Xác thực dữ liệu (Validation)
Trong PHP thuần, để xử lý một Form đăng bài, bạn phải viết hàng chục dòng code if...else lồng nhau để kiểm tra xem tiêu đề có bị bỏ trống không, nội dung có đủ độ dài không, và phải tự tay gán lại dữ liệu cũ nếu người dùng nhập sai .
Với Laravel, tất cả quy trình đau khổ đó được tự động hóa .
1. Chuẩn bị: Đăng ký Route cho Form
Chúng ta cần 2 Route: một cái dùng phương thức GET để hiển thị Form, và một cái dùng phương thức POST để hứng dữ liệu khi người dùng bấm nút Submit .
Mở file routes/web.php và thêm :
use App\Http\Controllers\PostController;
// 1. Hiển thị form thêm bài viết
Route::get('/posts/create', [PostController::class, 'create']);
// 2. Hứng dữ liệu từ form gửi lên (Chú ý dùng Route::post)
Route::post('/posts', [PostController::class, 'store']);
2. Giao diện Form và Tấm khiên bảo mật @csrf
Tạo một file giao diện resources/views/posts/create.blade.php với đoạn mã HTML cơ bản sau :
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Thêm bài viết mới</title>
</head>
<body>
<h1>Đăng bài viết mới</h1>
<form action="/posts" method="POST">
<!-- Lệnh bảo mật bắt buộc của Laravel -->
@csrf
<label>Tiêu đề:</label><br>
<input type="text" name="title"><br><br>
<label>Nội dung:</label><br>
<textarea name="content" rows="5"></textarea><br><br>
<button type="submit">Lưu bài viết</button>
</form>
</body>
</html>
Tại sao phải có @csrf?
Nếu bạn xóa dòng @csrf đi và bấm submit, Laravel sẽ lập tức quăng ra một lỗi trang đỏ lòm: 419 Page Expired .
Đây là cơ chế chống tấn công giả mạo (Cross-Site Request Forgery) . Hàm @csrf sẽ sinh ra một thẻ <input type="hidden"> chứa một chuỗi token mã hóa . Khi form được gửi lên, Laravel sẽ kiểm tra chuỗi token này . Nếu khớp, nó mới cho phép dữ liệu đi tiếp; nếu không (do hacker dùng tool bắn request từ domain khác sang), nó sẽ chặn đứng lập tức . Bạn không cần phải tự viết code bảo vệ như PHP thuần nữa .
3. Controller và Sức mạnh của Validation
Tiếp theo, tạo PostController (nếu chưa có) và viết 2 hàm create (để gọi View) và store (để xử lý dữ liệu POST) .
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
class PostController extends Controller
{
// Hàm hiển thị Form
public function create() {
return view('posts.create');
}
// Hàm xử lý dữ liệu
public function store(Request $request) {
// 1. Validation (Kiểm tra tính hợp lệ của dữ liệu)
$validatedData = $request->validate([
'title' => 'required|max:255|unique:posts,title', // Bắt buộc nhập, tối đa 255 ký tự, không được trùng tiêu đề trong bảng posts
'content' => 'required|min:10', // Bắt buộc nhập, tối thiểu 10 ký tự
]);
// 2. Lưu vào Database
Post::create($validatedData);
// 3. Chuyển hướng người dùng về trang danh sách (hoặc trang chủ)
return redirect('/home');
}
}
Ma thuật của
$request->validate(): Hàm này sẽ nhận vào các luật (rules) do bạn định nghĩa . Nếu dữ liệu người dùng gửi lên vi phạm bất kỳ luật nào, hàm này sẽ lập tức dừng lại . Nó tự động chuyển hướng người dùng quay ngược về trang Form và đính kèm theo toàn bộ thông báo lỗi . Bạn không cần viết lệnhifkiểm tra lỗi thủ công .
4. Bảo vệ Mass Assignment (Lỗi cực kỳ phổ biến)
Khoan đã! Nếu bạn chạy đoạn code trên và bấm Submit ngay lúc này, Laravel sẽ báo một lỗi cực kỳ đáng sợ: MassAssignmentException .
Đây là cơ chế bảo vệ lớp thứ 2 của Laravel . Khi bạn dùng lệnh Post::create(), bạn đang nhét cả một mảng dữ liệu vào Database cùng lúc (gọi là Mass Assignment) . Để ngăn hacker tự ý "nhét" thêm những trường nhạy cảm (như cố tình gửi thêm tham số role = admin vào form), Laravel yêu cầu bạn phải khai báo rõ ràng những cột nào được phép nhận dữ liệu hàng loạt .
Mở file Model app/Models/Post.php lên và khai báo biến $fillable :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
// Cho phép các cột này được nhận dữ liệu từ lệnh create()
protected $fillable = [
'title',
'content',
'status'
];
}
5. Hiển thị thông báo lỗi và Giữ lại dữ liệu cũ (old)
Khi Validation thất bại, Laravel hất người dùng quay lại Form . Nhưng làm sao để báo cho người dùng biết họ nhập sai ở đâu? Và làm sao để không bắt họ phải gõ lại từ đầu những trường đã nhập đúng?
Blade cung cấp 2 công cụ tuyệt vời là chỉ thị @error và hàm old() . Hãy quay lại file create.blade.php và cập nhật form như sau :
<form action="/posts" method="POST">
@csrf
<label>Tiêu đề:</label><br>
<!-- Hàm old('title') sẽ tự động in lại chữ người dùng đã gõ trước đó nếu có lỗi -->
<input type="text" name="title" value="{{ old('title') }}"><br>
<!-- In ra thông báo lỗi riêng cho trường title -->
@error('title')
<span style="color: red;">{{ $message }}</span>
@enderror
<br><br>
<label>Nội dung:</label><br>
<textarea name="content" rows="5">{{ old('content') }}</textarea><br>
<!-- In ra thông báo lỗi riêng cho trường content -->
@error('content')
<span style="color: red;">{{ $message }}</span>
@enderror
<br><br>
<button type="submit">Lưu bài viết</button>
</form>
Thử bỏ trống toàn bộ form và bấm Lưu, hoặc nhập nội dung ngắn hơn 10 ký tự, bạn sẽ thấy các thông báo lỗi màu đỏ (được Laravel tự động dịch chuẩn xác) hiện ra ngay bên dưới các ô input, đồng thời các dữ liệu bạn đã gõ trước đó hoàn toàn không bị mất .
All rights reserved