Chuyên mục

Lavarel

Lavarel tutolrial

37 bài viết
Hướng dẫn Eloquent_ORM - Lavarel
18/03/2026

Hướng dẫn Eloquent_ORM - Lavarel

Chào mừng anh em đến với buổi "đào tạo nâng cao" về một trong những "siêu năng lực" của Laravel: Eloquent ORM. Nếu anh em nào còn đang vật lộn với SQL thuần túy, hay cảm thấy viết mấy câu lệnh SELECT * FROM users WHERE status = 'active' nó cứ "thô" và "cổ lỗ sĩ" thế nào ấy, thì hôm nay chúng ta sẽ cùng nhau "lột xác" nhé. Hãy hình dung thế này: Database của chúng ta là một kho tàng khổng lồ chứa đầy dữ liệu quý giá, được sắp xếp cẩn thận trong các "két sắt" (bảng). Bình thường, để lấy đồ từ két, anh em phải dùng chìa khóa, mở từng lớp khóa, rồi đọc từng tờ giấy hướng dẫn "đồ nằm ở ngăn số mấy, hàng số mấy". Đó là cách chúng ta vẫn làm với SQL thuần túy – tỉ mẩn, chi tiết, nhưng cũng dễ sai sót và tốn thời gian. Eloquent ORM sinh ra để biến anh em thành một... "phù thủy" quản lý kho. Thay vì tự tay mở từng két, anh em chỉ cần "vẫy đũa thần" và nói: "Ê, cho ta cái két tên là 'Người Dùng' đi, rồi lọc mấy người đang 'Hoạt Động' ra đây!". Và bùm! Dữ liệu sẽ tự động hiện ra dưới dạng những "vật phẩm" (object) mà anh em có thể cầm nắm, tương tác một cách tự nhiên bằng ngôn ngữ lập trình của mình (PHP). 1. Eloquent ORM là gì và để làm gì? ORM là viết tắt của Object-Relational Mapping – tạm dịch là "Ánh xạ Đối tượng - Quan hệ". Nó là một cây cầu nối thần kỳ giữa hai thế giới: Thế giới hướng đối tượng (OOP): Nơi anh em làm việc với các class, object, thuộc tính, phương thức... như User, Post, Order. Thế giới quan hệ (Relational Database): Nơi dữ liệu được lưu trữ trong các bảng, hàng, cột... như users table, posts table. Eloquent chính là phiên bản ORM được Laravel "độ" riêng, cực kỳ mạnh mẽ và thân thiện. Nó biến mỗi bảng trong database của anh em thành một Model PHP. Mỗi hàng (row) trong bảng đó sẽ trở thành một instance của Model tương ứng. Vậy nó để làm gì? Nói không với SQL "thô": Anh em không cần phải viết những câu SQL dài dòng, dễ lỗi cú pháp. Thay vào đó, anh em tương tác với database bằng cú pháp PHP quen thuộc, trực quan. Tăng tốc độ phát triển (Productivity): Giảm đáng kể thời gian viết code tương tác database. Code sạch hơn, dễ đọc, dễ bảo trì: Code PHP dễ hiểu hơn nhiều so với việc nhúng SQL string vào. Giảm thiểu lỗi bảo mật: Eloquent tự động "dọn dẹp" (sanitize) các input, giúp phòng chống SQL Injection một cách hiệu quả. Quản lý quan hệ dễ dàng: Định nghĩa các mối quan hệ giữa các bảng (một-nhiều, nhiều-nhiều...) chỉ bằng vài dòng code trong Model. Tóm lại, Eloquent là "người phiên dịch siêu đẳng" giúp anh em "trò chuyện" với database bằng ngôn ngữ của PHP, biến những con số và chuỗi ký tự khô khan thành những đối tượng sống động, dễ thao tác. 2. Code Ví Dụ Minh Hoạ Rõ Ràng, Ngầu Để bắt đầu "phù phép" với Eloquent, chúng ta cần một Model. Giả sử anh em có một bảng posts trong database với các cột id, title, content, user_id, created_at, updated_at. A. Tạo Model Dùng Artisan Command, "phù phép" ra một Model tên là Post: php artisan make:model Post File app/Models/Post.php sẽ được tạo ra. B. Định nghĩa Model cơ bản Bên trong Post.php, anh em có thể định nghĩa một vài thuộc tính quan trọng: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; // Tên bảng mà Model này tương ứng. Mặc định là tên số nhiều của Model (posts). // Nếu tên bảng của bạn không phải là 'posts', hãy ghi rõ ở đây. // protected $table = 'my_posts_table'; // Khóa chính của bảng. Mặc định là 'id'. // protected $primaryKey = 'post_id'; // Các cột có thể được gán giá trị hàng loạt (mass assignable). // Rất quan trọng để tránh lỗ hổng bảo mật "Mass Assignment". protected $fillable = [ 'title', 'content', 'user_id', 'is_published', // Thêm cột này nếu có ]; // Hoặc, ngược lại với $fillable, là danh sách các cột KHÔNG được gán hàng loạt. // protected $guarded = ['id']; // Mặc định 'id' luôn được guarded. } C. Các Thao Tác CRUD "Thần Tốc" Bây giờ, hãy xem cách chúng ta "vẫy đũa" để tương tác với database: 1. Tạo (Create) - "Sinh ra một bài viết mới!" use App\Models\Post; // Cách 1: Tạo và lưu ngay lập tức $newPost = Post::create([ 'title' => 'Bài viết đầu tiên của tôi', 'content' => 'Nội dung thật là hay ho và bổ ích.', 'user_id' => 1, // Giả sử user có ID là 1 'is_published' => true, ]); echo "Đã tạo bài viết với ID: " . $newPost->id; // Cách 2: Tạo đối tượng rồi mới gán giá trị và lưu $anotherPost = new Post(); $anotherPost->title = 'Bài viết thứ hai, cũng ngầu không kém'; $anotherPost->content = 'Laravel và Eloquent thật tuyệt vời!'; $anotherPost->user_id = 1; $anotherPost->is_published = false; $anotherPost->save(); // Lưu vào database echo "Đã tạo thêm bài viết với ID: " . $anotherPost->id; 2. Đọc (Read) - "Tìm kiếm kho báu!" use App\Models\Post; // Lấy tất cả bài viết - "Đưa ta toàn bộ kho báu!" $allPosts = Post::all(); foreach ($allPosts as $post) { echo "ID: {$post->id}, Tiêu đề: {$post->title}\n"; } // Tìm một bài viết theo ID - "Tìm két số 5!" $postById = Post::find(5); if ($postById) { echo "Tìm thấy bài viết ID 5: {$postById->title}\n"; } else { echo "Không tìm thấy bài viết ID 5.\n"; } // Tìm bài viết theo điều kiện - "Tìm tất cả két có nhãn 'ngầu'!" $publishedPosts = Post::where('is_published', true)->get(); echo "Các bài viết đã publish:\n"; foreach ($publishedPosts as $post) { echo "- {$post->title}\n"; } // Tìm bài viết theo nhiều điều kiện - "Tìm két có nhãn 'ngầu' và chủ sở hữu là 'admin'!" $adminPosts = Post::where('user_id', 1) ->where('is_published', true) ->orderBy('created_at', 'desc') // Sắp xếp giảm dần theo thời gian tạo ->limit(5) // Chỉ lấy 5 bài đầu tiên ->get(); echo "5 bài viết mới nhất của Admin:\n"; foreach ($adminPosts as $post) { echo "- {$post->title} (ID: {$post->id})\n"; } // Lấy bài viết đầu tiên thỏa mãn điều kiện - "Chỉ lấy một cái két đầu tiên có nhãn 'ngầu' thôi!" $firstDraft = Post::where('is_published', false)->first(); if ($firstDraft) { echo "Bài viết nháp đầu tiên: {$firstDraft->title}\n"; } // Tìm theo ID hoặc ném lỗi nếu không tìm thấy (thường dùng trong Controller) // $postOrAbort = Post::findOrFail(999); // Sẽ ném ra 404 Not Found nếu không có 3. Cập nhật (Update) - "Sửa đổi nội dung két!" use App\Models\Post; // Tìm bài viết cần cập nhật $postToUpdate = Post::find(2); // Giả sử muốn sửa bài viết có ID 2 if ($postToUpdate) { $postToUpdate->title = 'Tiêu đề đã được cập nhật, giờ "ngầu" hơn!'; $postToUpdate->content = 'Nội dung cũng được làm mới, đọc sướng hơn nhiều.'; $postToUpdate->is_published = true; $postToUpdate->save(); // Lưu thay đổi vào database echo "Bài viết ID 2 đã được cập nhật và publish.\n"; } // Cập nhật hàng loạt theo điều kiện - "Đánh dấu tất cả két có nhãn 'nháp' thành 'đã duyệt'!" Post::where('is_published', false)->update(['is_published' => true]); echo "Tất cả bài viết nháp đã được publish.\n"; 4. Xóa (Delete) - "Vứt bỏ mấy cái két không dùng nữa!" use App\Models\Post; // Tìm bài viết cần xóa $postToDelete = Post::find(3); // Giả sử muốn xóa bài viết có ID 3 if ($postToDelete) { $postToDelete->delete(); // Xóa khỏi database echo "Bài viết ID 3 đã bị xóa vĩnh viễn.\n"; } // Xóa nhiều bài viết theo ID Post::destroy([1, 4]); // Xóa bài viết ID 1 và 4 echo "Đã xóa bài viết ID 1 và 4.\n"; // Xóa hàng loạt theo điều kiện - "Xóa tất cả két cũ rích, không ai dùng!" Post::where('created_at', '<', now()->subMonths(6))->delete(); echo "Đã xóa tất cả bài viết cũ hơn 6 tháng.\n"; D. Mối Quan Hệ (Relationships) - "Liên kết các két lại với nhau!" Đây là lúc Eloquent thực sự tỏa sáng, nó giúp anh em định nghĩa các mối quan hệ giữa các bảng một cách tự nhiên như cách chúng ta tư duy. Giả sử: Một User có thể có nhiều Post. (One-to-Many) Một Post thuộc về một User. (Many-to-One) Bước 1: Tạo Model User (nếu chưa có) php artisan make:model User (Laravel đã có sẵn User Model, nhưng đây là ví dụ) Bước 2: Định nghĩa mối quan hệ trong Models app/Models/User.php: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; // Một User có thể có nhiều Post public function posts() { return $this->hasMany(Post::class); } } app/Models/Post.php: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; protected $fillable = [ 'title', 'content', 'user_id', 'is_published', ]; // Một Post thuộc về một User public function user() { return $this->belongsTo(User::class); } } Bước 3: Tương tác với mối quan hệ use App\Models\User; use App\Models\Post; // Lấy bài viết của một User cụ thể $user = User::find(1); // Lấy user có ID là 1 if ($user) { echo "Các bài viết của {$user->name}:\n"; foreach ($user->posts as $post) { // Truy cập thuộc tính `posts` (là một collection các Post) echo "- {$post->title}\n"; } } // Lấy User của một bài viết cụ thể $post = Post::find(5); // Lấy bài viết có ID là 5 if ($post && $post->user) { // Truy cập thuộc tính `user` (là một đối tượng User) echo "Bài viết '{$post->title}' được viết bởi: {$post->user->name}\n"; } // Tạo bài viết mới thông qua mối quan hệ $user = User::find(1); if ($user) { $user->posts()->create([ // Sử dụng phương thức `posts()` để tạo bài viết mới cho user này 'title' => 'Bài viết mới từ mối quan hệ', 'content' => 'Nội dung này được tạo bởi user ID 1.', 'is_published' => true, ]); echo "Đã tạo bài viết mới cho user {$user->name}.\n"; } Vấn đề N+1 và Eager Loading - "Đừng đi lấy từng cái két một!" Khi anh em lặp qua một danh sách các bài viết và muốn hiển thị tên tác giả cho mỗi bài: // Ví dụ gây ra N+1 problem (Lazy Loading) $posts = Post::all(); // 1 query để lấy tất cả posts foreach ($posts as $post) { echo "Bài viết: {$post->title}, Tác giả: {$post->user->name}\n"; // N queries để lấy user cho mỗi post } // Tổng cộng: 1 (Post) + N (User) queries. Nếu N lớn, hiệu suất sẽ rất tệ! Để giải quyết vấn đề này, chúng ta dùng Eager Loading với phương thức with(): // Eager Loading - "Lấy tất cả két Post, và tiện thể, lấy luôn thông tin chủ của từng két!" $posts = Post::with('user')->get(); // Chỉ 2 queries: 1 cho posts, 1 cho users liên quan foreach ($posts as $post) { echo "Bài viết: {$post->title}, Tác giả: {$post->user->name}\n"; } // Tổng cộng: 2 queries. Hiệu suất được cải thiện đáng kể! 3. Một Vài Mẹo (Best Practices) Để Ghi Nhớ Hoặc Dùng Thực Tế Hiểu rõ Database Schema và Mối Quan Hệ: Eloquent là ORM, nó ánh xạ database. Nếu anh em không hiểu cấu trúc database và các mối quan hệ (One-to-Many, Many-to-Many, One-to-One), anh em sẽ như người mù đi trong kho. Hãy vẽ sơ đồ ERD (Entity-Relationship Diagram) nếu cần. Đó là bản đồ kho báu của anh em. Luôn dùng $fillable hoặc $guarded: Đây là tấm khiên bảo vệ ứng dụng của anh em khỏi lỗ hổng "Mass Assignment Vulnerability". Đừng bao giờ bỏ qua nó. $fillable: Danh sách các cột ĐƯỢC PHÉP gán hàng loạt. $guarded: Danh sách các cột KHÔNG ĐƯỢC PHÉP gán hàng loạt (mặc định là ['*'] nếu không định nghĩa gì, nghĩa là không cho phép mass assignment). Lời khuyên: Nên dùng $fillable để an toàn hơn, chỉ định rõ ràng những gì được phép. Nắm vững Lazy Loading vs. Eager Loading (with()): Đây là một trong những yếu tố then chốt quyết định hiệu năng của ứng dụng. Lazy Loading: Tiện lợi, nhưng coi chừng "N+1 Problem" khi truy vấn quan hệ trong vòng lặp. Eager Loading (with()): Luôn ưu tiên dùng khi biết trước mình sẽ cần dữ liệu từ các quan hệ. Nó giúp giảm số lượng query xuống đáng kể, tăng tốc độ tải trang. Sử dụng Scopes cho các truy vấn phức tạp hoặc lặp lại: Nếu anh em có những câu truy vấn where hoặc join phức tạp mà phải dùng đi dùng lại ở nhiều nơi, hãy đóng gói chúng vào "local scopes" trong Model. // Trong Post.php public function scopePublished($query) { return $query->where('is_published', true); } public function scopeRecent($query) { return $query->orderBy('created_at', 'desc'); } // Cách dùng $recentPublishedPosts = Post::published()->recent()->get(); Nó giúp code DRY (Don't Repeat Yourself) và dễ đọc hơn rất nhiều. Factories và Seeders để tạo dữ liệu giả lập: Khi phát triển hoặc viết unit test, anh em sẽ cần rất nhiều dữ liệu mẫu. Eloquent kết hợp với Model Factories và Database Seeders là bộ đôi hoàn hảo để "đổ" hàng ngàn bản ghi vào database chỉ trong tích tắc. Sử dụng firstOrCreate, updateOrCreate: Tiết kiệm một bước kiểm tra if (exists) { update } else { create }. Các phương thức này giúp anh em tạo hoặc cập nhật bản ghi một cách gọn gàng. Đừng ngại đọc tài liệu: Tài liệu của Laravel về Eloquent cực kỳ chi tiết và dễ hiểu. Nó là "cuốn sách phép thuật" của anh em đấy. Lời kết Eloquent ORM không chỉ là một công cụ, nó là một triết lý. Nó thay đổi cách anh em tư duy về tương tác database, từ việc "nói chuyện" bằng những câu lệnh SQL khô khan sang việc "giao tiếp" bằng những đối tượng PHP sống động. Khi đã thành thạo Eloquent, anh em sẽ thấy tốc độ phát triển ứng dụng của mình tăng vọt, code sạch sẽ và dễ bảo trì hơn bao giờ hết. Hãy nhớ, "người phù thủy" không bao giờ sợ hãi những "con rồng" database phức tạp, bởi vì họ đã có Eloquent làm "đũa phép" trong tay. Thực hành thật nhiều, thử nghiệm đủ các trường hợp, và anh em sẽ nhanh chóng trở thành bậc thầy điều khiển dữ liệu trong thế giới Laravel. Chúc anh em "phù phép" thành công! Thuộc Series: Lavarel Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

8 Đọc tiếp