Eloquent ORM là gì? Hướng dẫn sử dụng Eloquent trong Laravel
1. Eloquent ORM là gì?
Eloquent ORM là hệ thống Object-Relational Mapping (ORM) được tích hợp trong Laravel, một framework phát triển ứng dụng Web bằng PHP. Eloquent cho phép lập trình viên tương tác với cơ sở dữ liệu thông qua các class và object, thay vì phải viết câu lệnh SQL thủ công cho mọi thao tác.
Trong Eloquent, mỗi model thường đại diện cho một bảng trong database. Model cung cấp các phương thức để truy vấn, thêm, cập nhật và xóa dữ liệu. Eloquent cũng hỗ trợ khai báo quan hệ giữa các model, quản lý các truy vấn liên quan và ánh xạ kết quả truy vấn thành object PHP.
Ví dụ, để lấy sản phẩm có ID bằng 1, có thể sử dụng:
$product = Product::find(1);
Thay vì tự viết câu SQL:
SELECT * FROM products WHERE id = 1;
Eloquent xây dựng và thực thi truy vấn tương ứng thông qua hệ thống truy vấn của Laravel, sau đó trả về model chứa dữ liệu nếu tìm thấy bản ghi.
Tuy nhiên, Eloquent không loại bỏ SQL. Nó cung cấp một lớp trừu tượng giúp lập trình viên thao tác với database bằng PHP dễ dàng hơn, trong khi các truy vấn vẫn được thực thi trên hệ quản trị cơ sở dữ liệu.
2. ORM giải quyết vấn đề gì?
Trong ứng dụng Backend, dữ liệu thường được lưu trữ dưới dạng bảng, hàng và cột trong database. Ngược lại, chương trình PHP làm việc với biến, array, object và class.
Sự khác biệt giữa hai cách biểu diễn này tạo ra một vấn đề: lập trình viên phải chuyển đổi dữ liệu giữa mô hình quan hệ của database và mô hình đối tượng trong chương trình.
Giả sử hệ thống có bảng products:
| id | name | price |
|---|---|---|
| 1 | Keyboard | 500000 |
| 2 | Mouse | 200000 |
Nếu sử dụng SQL thuần, lập trình viên viết truy vấn, thực thi truy vấn và xử lý kết quả trả về. Nếu sử dụng ORM, nhiều thao tác có thể được thực hiện thông qua các phương thức của model.
ORM giúp:
- Giảm lượng mã lặp lại khi thực hiện các thao tác CRUD.
- Tổ chức code theo các model tương ứng với dữ liệu nghiệp vụ.
- Khai báo và truy xuất quan hệ giữa các đối tượng dữ liệu.
- Hỗ trợ xây dựng truy vấn thông qua các phương thức dễ đọc.
- Giúp việc bảo trì và mở rộng ứng dụng thuận tiện hơn.
Dù vậy, ORM không tự động khiến truy vấn nhanh hơn hoặc thiết kế database tốt hơn. Lập trình viên vẫn cần hiểu SQL, cấu trúc dữ liệu, index và cách database thực thi truy vấn.
3. Model trong Eloquent là gì?
Model là thành phần trung tâm của Eloquent, dùng để tương tác với dữ liệu trong database.
Một model thường đại diện cho một loại thực thể trong hệ thống, chẳng hạn:
User: người dùng.Product: sản phẩm.Order: đơn hàng.Category: danh mục sản phẩm.
Tạo model
Laravel cung cấp Artisan CLI để tạo model:
php artisan make:model Product
Thông thường, Laravel tạo file Product.php trong thư mục app/Models.
Một model cơ bản có thể được khai báo như sau:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = [
'name',
'price',
];
}
Trong ví dụ này:
Productlà model dùng để tương tác với dữ liệu sản phẩm.extends Modelcho phép class sử dụng các chức năng của Eloquent.$fillablexác định những thuộc tính được phép gán hàng loạt thông qua các phương thức nhưcreate()vàfill().
Quy ước của Eloquent
Theo quy ước mặc định, Laravel sử dụng tên model để suy ra tên bảng:
| Model | Bảng mặc định |
|---|---|
Product |
products |
User |
users |
Order |
orders |
Category |
categories |
Eloquent cũng mặc định sử dụng cột id làm khóa chính và hai cột created_at, updated_at để theo dõi thời điểm tạo và cập nhật bản ghi.
Nếu bảng không tuân theo các quy ước này, lập trình viên có thể cấu hình model tương ứng.
Ví dụ, nếu model Product sử dụng bảng store_items:
class Product extends Model
{
protected $table = 'store_items';
}
Những quy ước này giúp giảm lượng cấu hình cần viết, nhưng không có nghĩa Eloquent bắt buộc mọi database phải sử dụng cùng một cách đặt tên.
4. Eloquent thực hiện CRUD như thế nào?
CRUD là viết tắt của Create, Read, Update và Delete — bốn thao tác cơ bản khi làm việc với dữ liệu.
4.1. Create — Thêm dữ liệu
Có thể tạo bản ghi mới bằng cách khởi tạo model:
$product = new Product();
$product->name = 'Keyboard';
$product->price = 500000;
$product->save();
Hoặc sử dụng create():
$product = Product::create([
'name' => 'Keyboard',
'price' => 500000,
]);
Phương thức save() lưu các thay đổi của model xuống database. Với model mới, nó thực hiện thao tác thêm bản ghi; với model đã tồn tại, nó có thể cập nhật bản ghi tương ứng.
Phương thức create() tạo và lưu model trong một bước. Tuy nhiên, những thuộc tính được truyền vào phải đáp ứng cấu hình mass assignment của model.
4.2. Read — Truy vấn dữ liệu
Lấy một bản ghi theo khóa chính:
$product = Product::find(1);
Nếu không tìm thấy bản ghi có ID bằng 1, find() trả về null.
Nếu muốn Laravel ném ngoại lệ khi không tìm thấy bản ghi, có thể sử dụng:
$product = Product::findOrFail(1);
Lấy tất cả bản ghi:
$products = Product::all();
Lọc dữ liệu theo điều kiện:
$products = Product::where('price', '>', 300000)
->get();
Sắp xếp và giới hạn kết quả:
$products = Product::orderBy('price', 'desc')
->limit(10)
->get();
Eloquent thường cho phép kết hợp nhiều phương thức để xây dựng truy vấn trước khi thực thi. Chẳng hạn, where() bổ sung điều kiện lọc, còn get() thực thi truy vấn và trả về một collection chứa các model phù hợp.
4.3. Update — Cập nhật dữ liệu
Có thể truy xuất model rồi thay đổi thuộc tính:
$product = Product::findOrFail(1);
$product->price = 550000;
$product->save();
Hoặc cập nhật trực tiếp những bản ghi thỏa mãn điều kiện:
Product::where('id', 1)
->update([
'price' => 550000,
]);
Hai cách này có sự khác biệt. Cách đầu làm việc với một model cụ thể và lưu các thay đổi của model đó. Cách thứ hai thực hiện cập nhật theo điều kiện bằng một truy vấn hàng loạt.
Các thao tác cập nhật hàng loạt không khởi tạo từng model như cách cập nhật từng bản ghi. Vì vậy, những sự kiện Eloquent gắn với từng model có thể không được kích hoạt theo cách người mới sử dụng ORM thường kỳ vọng.
4.4. Delete — Xóa dữ liệu
Xóa một bản ghi theo ID:
$product = Product::findOrFail(1);
$product->delete();
Hoặc xóa theo điều kiện:
Product::where('price', '<', 100000)
->delete();
Phương thức delete() có thể xóa dữ liệu thực sự khỏi bảng. Nếu model được cấu hình sử dụng Soft Deletes, thao tác này sẽ đánh dấu bản ghi đã bị xóa thay vì xóa vật lý ngay lập tức.
Cần đặc biệt cẩn thận với thao tác xóa hàng loạt vì một điều kiện sai có thể tác động đến nhiều bản ghi ngoài dự kiến.
5. Query Builder trong Eloquent
Eloquent cung cấp một hệ thống xây dựng truy vấn theo dạng fluent interface, cho phép nối các phương thức để mô tả điều kiện truy vấn.
Ví dụ:
$products = Product::where('price', '>=', 200000)
->where('name', 'like', '%Key%')
->orderBy('price', 'desc')
->get();
Đoạn code trên yêu cầu lấy những sản phẩm có giá từ 200000 trở lên, tên chứa chuỗi Key, sau đó sắp xếp theo giá giảm dần.
Có thể hình dung truy vấn này tương ứng với SQL:
SELECT *
FROM products
WHERE price >= 200000
AND name LIKE '%Key%'
ORDER BY price DESC;
SQL trên nhằm minh họa ý nghĩa của truy vấn; câu SQL thực tế được sinh ra có thể khác nhau tùy database driver và cách Laravel xây dựng truy vấn.
Một số phương thức thường gặp
| Phương thức | Chức năng |
|---|---|
where() |
Thêm điều kiện lọc |
orWhere() |
Thêm điều kiện OR |
orderBy() |
Sắp xếp kết quả |
limit() |
Giới hạn số lượng kết quả |
select() |
Chọn các cột cần lấy |
first() |
Lấy bản ghi đầu tiên phù hợp |
get() |
Thực thi truy vấn và lấy collection |
count() |
Đếm số bản ghi phù hợp |
exists() |
Kiểm tra có bản ghi phù hợp hay không |
Một điểm cần nhớ là các phương thức xây dựng truy vấn như where() thường chưa thực thi truy vấn ngay. Việc truy vấn thường xảy ra khi gọi phương thức lấy kết quả như get(), first() hoặc count().
Ví dụ:
$query = Product::where('price', '>', 300000);
// Truy vấn được thực thi tại đây.
$products = $query->get();
Cách xây dựng truy vấn theo từng bước giúp code dễ đọc và thuận tiện khi bổ sung điều kiện theo logic nghiệp vụ.
6. Relationships — Quan hệ giữa các model
Trong database, dữ liệu thường được liên kết với nhau thông qua khóa chính và khóa ngoại. Eloquent cho phép khai báo các quan hệ này bằng phương thức trong model.
Các loại quan hệ phổ biến gồm:
- One to One — Một-một.
- One to Many — Một-nhiều.
- Many to Many — Nhiều-nhiều.
- Belongs To — Một model thuộc về model khác.
- Polymorphic Relationships — Quan hệ đa hình.
6.1. One to Many — Một-nhiều
Giả sử một danh mục có nhiều sản phẩm, nhưng mỗi sản phẩm chỉ thuộc về một danh mục.
Bảng products có thể chứa cột category_id làm khóa ngoại tham chiếu đến bảng categories.
Trong model Category:
public function products()
{
return $this->hasMany(Product::class);
}
Trong model Product:
public function category()
{
return $this->belongsTo(Category::class);
}
Sau khi khai báo quan hệ, có thể truy xuất sản phẩm thuộc một danh mục:
$category = Category::findOrFail(1);
$products = $category->products;
Hoặc lấy danh mục của một sản phẩm:
$product = Product::findOrFail(1);
$category = $product->category;
Eloquent sử dụng các quan hệ đã khai báo để xây dựng truy vấn tương ứng. Tên phương thức quan hệ không tự tạo ra khóa ngoại trong database; cấu trúc bảng và các cột liên quan vẫn phải được thiết kế đúng.
6.2. Many to Many — Nhiều-nhiều
Giả sử một sản phẩm có thể thuộc nhiều chương trình khuyến mãi và mỗi chương trình khuyến mãi có thể áp dụng cho nhiều sản phẩm.
Mô hình này thường cần một bảng trung gian, chẳng hạn product_promotion, chứa product_id và promotion_id.
Quan hệ trong model Product có thể được khai báo như sau:
public function promotions()
{
return $this->belongsToMany(Promotion::class);
}
Sau đó, có thể lấy các chương trình khuyến mãi của sản phẩm:
$product = Product::findOrFail(1);
$promotions = $product->promotions;
Với quan hệ nhiều-nhiều, Eloquent hỗ trợ truy vấn và thao tác thông qua bảng trung gian mà không cần tự viết toàn bộ câu SQL cho từng trường hợp thông thường.
7. Eager Loading và vấn đề N+1 Query
Một trong những vấn đề hiệu năng phổ biến khi sử dụng ORM là N+1 Query.
Giả sử cần lấy 100 sản phẩm và hiển thị tên danh mục của từng sản phẩm. Nếu truy vấn danh mục riêng cho từng sản phẩm, ứng dụng có thể thực hiện:
- Một truy vấn để lấy danh sách 100 sản phẩm.
- Thêm một truy vấn cho mỗi sản phẩm để lấy danh mục.
Tổng cộng có thể lên tới 101 truy vấn. Đây là ví dụ điển hình của vấn đề N+1.
Lazy Loading
Khi truy cập một quan hệ chưa được tải, Eloquent thường thực hiện lazy loading:
$products = Product::all();
foreach ($products as $product) {
echo $product->category->name;
}
Nếu quan hệ category chưa được tải trước, việc truy cập nó trong vòng lặp có thể tạo thêm truy vấn cho từng sản phẩm.
Eager Loading
Có thể sử dụng with() để tải trước quan hệ:
$products = Product::with('category')->get();
foreach ($products as $product) {
echo $product->category->name;
}
Eager loading cho phép Eloquent lấy dữ liệu liên quan theo cách giảm đáng kể số truy vấn trong tình huống này.
Tuy nhiên, eager loading không phải lúc nào cũng tốt hơn. Nếu tải nhiều quan hệ hoặc quá nhiều cột không cần thiết, ứng dụng vẫn có thể tiêu tốn bộ nhớ và tài nguyên không cần thiết.
Một số nguyên tắc tối ưu gồm:
- Chỉ truy vấn những cột cần sử dụng.
- Sử dụng
with()khi biết trước cần dữ liệu quan hệ. - Kiểm tra SQL được sinh ra và số lượng truy vấn thực tế.
- Dùng phân trang hoặc xử lý theo từng nhóm khi dữ liệu lớn.
- Sử dụng index phù hợp với điều kiện lọc và cách truy vấn.
8. Mass Assignment và bảo mật
Mass assignment là cơ chế cho phép gán nhiều thuộc tính cho model cùng lúc bằng một mảng dữ liệu.
Ví dụ:
$product = Product::create([
'name' => 'Keyboard',
'price' => 500000,
]);
Cơ chế này tiện lợi, nhưng có thể gây lỗ hổng bảo mật nếu ứng dụng gán trực tiếp dữ liệu không đáng tin cậy từ request mà không kiểm soát những trường được phép cập nhật.
Giả sử bảng users có cột is_admin. Nếu ứng dụng cho phép client truyền tùy ý thuộc tính vào model, người dùng có thể cố gửi thêm trường is_admin để nâng quyền tài khoản.
Sử dụng $fillable
Trong model, có thể chỉ định các trường được phép gán hàng loạt:
class Product extends Model
{
protected $fillable = [
'name',
'price',
];
}
Với cấu hình này, chỉ những thuộc tính được cho phép mới có thể được gán thông qua các phương thức mass assignment.
Một lựa chọn khác là $guarded, dùng để chỉ định những thuộc tính không được phép gán hàng loạt. Cần hiểu rõ cấu hình này trước khi sử dụng, đặc biệt khi danh sách thuộc tính bị chặn có thể không đầy đủ.
Ngoài $fillable, ứng dụng cần xác thực dữ liệu đầu vào, kiểm tra quyền của người dùng và tránh đưa trực tiếp toàn bộ dữ liệu request vào create() hoặc update().
ORM không thay thế các biện pháp bảo mật của ứng dụng. Cấu hình model chỉ là một lớp bảo vệ trong quá trình gán thuộc tính.
9. Eloquent và Transaction
Trong các thao tác nghiệp vụ gồm nhiều bước, chỉ sử dụng save() cho từng model không đảm bảo toàn bộ quy trình thành công hoặc thất bại như một đơn vị.
Ví dụ, tạo đơn hàng có thể bao gồm:
- Tạo đơn hàng.
- Tạo các dòng chi tiết đơn hàng.
- Cập nhật số lượng tồn kho.
Nếu bước thứ ba thất bại sau khi hai bước đầu đã thành công, database có thể rơi vào trạng thái không mong muốn nếu không có cơ chế transaction phù hợp.
Laravel cung cấp transaction thông qua DB facade:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
$order = Order::create([
'user_id' => 1,
'total' => 700000,
]);
$order->items()->createMany([
[
'product_id' => 10,
'quantity' => 1,
'price' => 500000,
],
[
'product_id' => 11,
'quantity' => 1,
'price' => 200000,
],
]);
});
Ví dụ này minh họa việc tạo đơn hàng và các dòng chi tiết trong một transaction. Để chạy đúng, ứng dụng cần khai báo quan hệ items() cùng cấu trúc bảng và các trường phù hợp.
Khi callback hoàn tất thành công, transaction được commit. Nếu xảy ra exception, Laravel sẽ rollback transaction theo cơ chế của database.
Transaction giúp bảo đảm tính nguyên tử của các thao tác database trong phạm vi transaction đó. Tuy nhiên, nó không tự động giải quyết mọi vấn đề đồng thời, khóa dữ liệu hay giao dịch trải qua nhiều dịch vụ độc lập.
10. Ưu điểm và hạn chế của Eloquent ORM
Ưu điểm
1. Tăng khả năng đọc hiểu code
Các phương thức như Product::where(...)->get() giúp thể hiện ý định truy vấn trực tiếp trong code PHP.
2. Tổ chức dữ liệu theo model
Mỗi model tập trung vào một loại thực thể và cung cấp nơi khai báo quan hệ cũng như một số hành vi liên quan đến dữ liệu.
3. Hỗ trợ quan hệ giữa các bảng
Eloquent cung cấp các phương thức để truy cập dữ liệu quan hệ mà không phải tự viết SQL cho mọi truy vấn.
4. Tích hợp với Laravel
Eloquent hoạt động cùng các thành phần khác của Laravel, bao gồm migration, validation, transaction và hệ thống xử lý HTTP.
Hạn chế
1. Có thể che khuất chi phí truy vấn
Code ngắn không có nghĩa truy vấn hiệu quả. Lazy loading hoặc việc truy vấn quá nhiều dữ liệu có thể làm giảm hiệu năng.
2. Không loại bỏ nhu cầu hiểu SQL
Khi truy vấn phức tạp, cần tối ưu hoặc sử dụng tính năng đặc thù của database, lập trình viên vẫn phải hiểu SQL và cách database vận hành.
3. Có thêm chi phí xử lý
ORM cần tạo và quản lý object model, ánh xạ kết quả truy vấn và thực hiện một số công việc bổ sung. Với một số tác vụ xử lý dữ liệu lớn, query builder hoặc SQL trực tiếp có thể phù hợp hơn.
4. Cần chú ý đến hành vi của các phương thức
Các thao tác theo từng model và thao tác hàng loạt có thể khác nhau về sự kiện, cách xử lý model và lượng dữ liệu được tải.
Do đó, lựa chọn Eloquent hay SQL trực tiếp nên dựa trên yêu cầu về khả năng bảo trì, độ phức tạp của truy vấn và hiệu năng thực tế, thay vì cho rằng một cách luôn tốt hơn cách còn lại.
11. Eloquent ORM khác gì SQL thuần?
| Tiêu chí | Eloquent ORM | SQL thuần |
|---|---|---|
| Cách thao tác | Model và phương thức PHP | Câu lệnh SQL |
| CRUD phổ biến | Thường ít mã hơn | Cần viết truy vấn trực tiếp |
| Quan hệ dữ liệu | Có thể khai báo qua model | Sử dụng JOIN hoặc truy vấn liên quan |
| Kiểm soát truy vấn | Thông qua API của ORM | Trực tiếp hơn |
| Truy vấn đặc thù | Có thể cần query builder hoặc SQL thô | Linh hoạt theo hệ quản trị database |
| Tối ưu hiệu năng | Cần hiểu truy vấn ORM tạo ra | Cần hiểu và tối ưu SQL |
Eloquent phù hợp với nhiều thao tác dữ liệu thường gặp trong ứng dụng Laravel. SQL thuần vẫn hữu ích khi cần kiểm soát truy vấn chính xác hoặc sử dụng những tính năng đặc thù.
Trong thực tế, hai cách không loại trừ nhau. Một ứng dụng có thể sử dụng Eloquent cho phần lớn CRUD và sử dụng query builder hoặc SQL trực tiếp cho những truy vấn phù hợp hơn.
12. Eloquent ORM trong hệ sinh thái Backend
Eloquent là một ví dụ cụ thể về cách ORM hỗ trợ ứng dụng Backend làm việc với database. Tuy nhiên, ORM không phải là một khái niệm riêng của PHP hoặc Laravel.
Các hệ sinh thái khác cũng có những thư viện tương tự, chẳng hạn:
- GORM: ORM phổ biến trong hệ sinh thái Go.
- Entity Framework Core: ORM trong hệ sinh thái .NET.
- Hibernate: framework ORM phổ biến trong Java.
- SQLAlchemy: thư viện SQL và ORM trong Python.
Mỗi thư viện có API, quy ước và cách quản lý truy vấn khác nhau, nhưng chúng cùng hướng đến việc kết nối mô hình đối tượng trong chương trình với dữ liệu quan hệ trong database.
13. Kết luận
Eloquent ORM là một thành phần quan trọng của Laravel, giúp lập trình viên thao tác với database thông qua model và object PHP. Từ các thao tác CRUD, xây dựng truy vấn, khai báo quan hệ đến eager loading và transaction, Eloquent cung cấp nhiều công cụ để phát triển ứng dụng Backend một cách rõ ràng và thuận tiện hơn.
Tuy nhiên, sử dụng ORM hiệu quả không chỉ là ghi nhớ các phương thức như find(), where(), create() hay with(). Điều quan trọng là hiểu mỗi thao tác tạo ra loại truy vấn nào, dữ liệu được tải ra sao, những vấn đề đồng thời có thể xảy ra và cách bảo vệ dữ liệu khỏi các thao tác không hợp lệ.
Mục tiêu không phải là tránh viết SQL, mà là biết khi nào nên sử dụng ORM, khi nào cần kiểm soát truy vấn trực tiếp và làm thế nào để cân bằng giữa khả năng bảo trì, tính đúng đắn và hiệu năng của ứng dụng.








Khoá học lập trình game con rắn cho trẻ em




