Một số dự án yêu cầu các bạn cần chạy raw queries trong Laravel. Chạy raw queries có khó không? Thực ra không khó khi bạn biết cách thực hiện. Trong bài chia sẻ này, bạn sẽ biết cách làm và còn ngăn được SQL injection với raw queries. Laravel có cơ chế tốt để chúng ta thực hiện phần cơ sở dữ liệu trên ứng dụng một cách trọn vẹn. Đó là Eloquent và Query Builder. Trong nhiều trường hợp, các bạn cần chạy Query đơn thuần dưới dạng Raw Query.
RAW Queries Syntax
Để khởi chạy raw queries, các bạn nên sử dụng phương thức DB::select() với cú pháp dưới đây:
\DB::select("
/** Your Query */
");
Problem (SQL Injection)
Các bạn nên chạy các raw queries như dưới đây. Ví dụ các bạn muốn lấy các posts mà có author và published_on mà lớn hơn một số ngày:
$author = 'Channaveer';
$publishedDate = '2020-02-01';
$post = \DB::select("
SELECT
id, title, body, author, published_on
FROM posts
WHERE
published_on >= $publishedDate and author = $author
");
Vấn đề bảo mật đóng vai trò quan trọng trong các ứng dụng của bạn. Thậm chí bạn chạy bất kỳ ứng dụng cho mục đích nội bộ thì bạn cũng nên lưu ý đến chuyện bảo mật. Vì có thể phát sinh yêu cầu publish ở đâu đó để bạn truy cập từ xa. Bạn quan sát thấy published_on >= $publishedDate và author = $author đã harcoded hay chưa? Thông thường, đây chính là lỗ hổng phổ biến và đáng lưu tâm vì nó dễ bị SQL Injection và khai thác cơ sở dữ liệu ngầm của bạn.
Giải pháp (Positional Bindings & Named Bindings)
Positional Bindings ( ? )
Ở vị trí binding, các bạn sẽ sử dụng ? để tạo chỗ cho các giá trị và sau đó chuyển các giá trị đó vào trong tham số thứ nhì thành mảng thông thường. Đừng quên các bạn phải tuân thủ cùng một chuỗi các vị trí. Lưu ý là với cùng một chuỗi các vị trí trong ví dụ, published_on xuất hiện đầu tiên trong query. Do đó, $publishedDate xuất hiện trước trong mảng tham số thứ nhì và kế tiếp sẽ là author & $author tương ứng.
$author = 'Channaveer';
$publishedDate = '2020-02-01';
$post = \DB::select("
SELECT
id, title, body, author, published_on
FROM
posts
WHERE
published_on >= ?
and
author = ?
",
[ $publishedDate, $author ]
);
Named Binding ( : )
Trong các binding được đặt tên, các bạn nên sử dụng : với tên là placeholder (ví dụ :publishedOn). Tại đây, các bạn không nhất thiết phải theo thứ tự 1,2,3…như trước đó.
$author = 'Channaveer';
$publishedDate = '2020-02-01';
$post = \DB::select("
SELECT
id, title, body, author, published_on
FROM
posts
WHERE
published_on >= :publishedDate
and
author = :author
",
[ ":publishedDate" => $publishedDate, ":author" => $author ]
);
Khi thực hiện như vậy, ứng dụng của các bạn sẽ tránh được trường hợp SQL injection.
Fun Part
Các bạn có thể chạy các phép toán CRUD trong hàm \DB:select(). Tuy nhiên, các bạn không nên thực hiện như vậy. Vì Laravel đã có DB:select DB::insert DB::update DB::delete DB::statement.
CRUD OPERATIONS ( DB::select(), DB::update(), DB::insert(), DB::delete(), DB::statement() )
Fetch Details - DB::select()
Để lấy bất cứ chi tiết nào từ cơ sở dữ liệu của bạn, bạn nên sử dụng phương thức đó. Nó giúp bạn thấy hiện trạng trước đó như thế nào và bạn cũng có thể thấy được mảng kết quả hiện tại.
$author = 'Channaveer';
$publishedDate = '2020-02-01';
$post = \DB::select("
SELECT
id, title, body, author, published_on
FROM
posts
WHERE
published_on >= :publishedDate
and
author = :author
",
[ ":publishedDate" => $publishedDate, ":author" => $author ]
);
Insert Details - DB::insert()
Để thêm vào bảng cơ sở dự liệu, bạn nên dùng phương thức này. Vì nó có thể nhận query trong tham số đầu tiên và các giá trị trong tham số thứ 2.
$post = \DB::insert("
INSERT INTO
posts
(title, body, author, published_on)
VALUES
(:title, :body, :author, :published_on)
", [
":title" => request('title'),
":body" => request('body') ,
":author" => session()->get('user_details')->id,
":published_on" => request('published_on')
]
);
Update Details - DB::update()
Để cập nhật các bản ghi đã tồn tại trước đó, các bạn nên cập nhật trả về số lượng row bị ảnh hưởng.
$post = \DB::update("
UPDATE
posts
SET
title = :title,
body = :body,
published_on = :published_on
WHERE
id = :id
", [
"id" => $id
":title" => request('title'),
":body" => request('body') ,
":published_on" => request('published_on')
]
);
Delete Details - DB::delete()
Để xóa bất cứ bản ghi nào từ cơ sở dữ liệu của bạn, thì bạn sẽ xóa và trả về số row bị ảnh hưởng.
$post = \DB::delete("
DELETE
FROM
posts
WHERE
id = :id
", [
"id" => $id
]
);
Generic Statements - DB::statement()
Trong quá trình thực hiện dự án, các bạn sẽ đối mặt với tình trạng nhiều queries không thể trả về kết quả. Do đó, bạn nên chạy các câu lệnh chung mà sẽ sử dụng phương thức này.
\DB::statement("DROP TABLE posts");
Nguy hiểm lớn nhất mà các bạn phải đối mặt khi thực hiện raw queries là nó không tự động xử lý các tham số truyền vào. Vì vậy, nếu bạn truyền vào bất kỳ tham số nào với câu truy vấn, thì bạn nên:
- Kiểm tra dòng lệnh
- Validate giá trị
- Định dạng chúng cẩn thận
Bài viết hữu ích.
Huu ich
Cảm ơn Admin. Bài viết khá hay !
OK bạn, mình sẽ sắp xếp viết một bài về Extend Validation. Cảm ơn bạn đã quan tâm nhé.
Đúng rồi bạn, cái hàm này mình định nghĩa ở model User nhé.