
Chào mừng các bạn đến với buổi học hôm nay!
Hôm nay, chúng ta sẽ "giải phẫu" một trong những trái tim của Laravel, một khái niệm mà nếu bạn nắm vững, việc tương tác với database sẽ trở nên dễ dàng, thanh lịch và mạnh mẽ hơn bao giờ hết. Đó chính là Model trong Laravel.
Model trong Laravel: Khi Dữ Liệu Biến Thành Đối Tượng Biết Nói
Bạn cứ hình dung thế này: database của chúng ta là một kho tàng khổng lồ chứa đầy những "viên ngọc" dữ liệu. Mỗi viên ngọc là một thông tin quý giá về người dùng, bài viết, sản phẩm, hay bất cứ thứ gì bạn đang quản lý. Tuy nhiên, những viên ngọc này nằm im lìm, vô tri vô giác trong các "hầm chứa" (các bảng).
Model trong Laravel, hay cụ thể hơn là Eloquent ORM (Object-Relational Mapping), chính là những "người đại diện" thông minh, những "phiên dịch viên" tài ba. Mỗi khi bạn có một "hầm chứa" dữ liệu (một bảng trong database), bạn sẽ có một Model tương ứng. Model này sẽ biến những hàng dữ liệu vô tri vô giác thành những đối tượng PHP có "linh hồn", biết nói, biết hành động.
Model Là Gì? Tại Sao Chúng Ta Cần Đến 'Đại Sứ' Này?
- Lớp Trừu Tượng (Abstraction Layer): Thay vì phải viết những câu lệnh SQL dài dòng, phức tạp (kiểu như
SELECT * FROM users WHERE id = 1), Model cho phép bạn tương tác với database thông qua các đối tượng PHP quen thuộc. Bạn chỉ cần gọiUser::find(1)là xong. Cứ như bạn đang nói chuyện với "người đại diện" của dữ liệu, chứ không phải mò mẫm trong kho bạc vậy. - Ánh Xạ Đối Tượng Quan Hệ (ORM - Object-Relational Mapping): Đây là "phép thuật" chính của Model. Nó tự động ánh xạ các hàng (rows) trong bảng database thành các đối tượng (objects) PHP và ngược lại. Mỗi dòng dữ liệu trong bảng
userssẽ trở thành một đối tượngUservới các thuộc tính nhưid,name,email. - Nơi Định Nghĩa Logic Dữ Liệu (Business Logic): Model không chỉ là cầu nối đơn thuần. Đây còn là nơi bạn định nghĩa các mối quan hệ giữa các bảng (một người dùng có nhiều bài viết, một bài viết thuộc về một người dùng), các phương thức truy vấn tùy chỉnh, các thuộc tính ảo (accessors/mutators), và thậm chí cả các quy tắc nghiệp vụ liên quan trực tiếp đến dữ liệu. Nó giúp giữ cho Controller của bạn "gọn gàng" hơn, chỉ tập trung vào việc điều phối.
- Bảo Toàn Dữ Liệu (Data Integrity): Với các tính năng như Mass Assignment Protection (bảo vệ gán dữ liệu hàng loạt) và các sự kiện (events), Model giúp bạn kiểm soát và đảm bảo dữ liệu luôn được lưu trữ một cách an toàn, đúng đắn.
Tóm lại, Model là "bộ não" của phần dữ liệu trong ứng dụng Laravel của bạn, giúp bạn thao tác với database một cách hiệu quả, an toàn và "nghệ thuật" hơn rất nhiều.

Code Ví Dụ: Biến Ý Tưởng Thành Hiện Thực Với Các 'Sứ Giả' Dữ Liệu
Hãy cùng xây dựng một hệ thống blog đơn giản để thấy Model hoạt động như thế nào nhé!
Tạo Model: Bước Đầu Tiên Của Sự Sáng Tạo
Bạn có bảng users và posts trong database. Để tạo Model tương ứng, chúng ta dùng Artisan CLI:
php artisan make:model User
php artisan make:model Post
Laravel sẽ tạo ra hai file app/Models/User.php và app/Models/Post.php.
app/Models/User.php (ví dụ đơn giản)
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
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;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
// Định nghĩa mối quan hệ tại đây
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;
// Các trường được phép gán giá trị hàng loạt
protected $fillable = [
'user_id',
'title',
'content',
'published_at',
];
// Định nghĩa mối quan hệ tại đây
public function user()
{
return $this->belongsTo(User::class);
}
// Một accessor ví dụ
public function getShortContentAttribute()
{
return substr($this->content, 0, 100) . '...';
}
}
CRUD: 4 Phép Thuật Cơ Bản Của Dữ Liệu
Đây là cách bạn "ra lệnh" cho các "sứ giả" dữ liệu của mình:
-
Create (Tạo mới):
use App\Models\User; use App\Models\Post; // Tạo một người dùng mới $user = User::create([ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => bcrypt('secret'), // Luôn hash mật khẩu! ]); // Tạo một bài viết mới cho người dùng này $post = Post::create([ 'user_id' => $user->id, 'title' => 'Bài viết đầu tiên của tôi', 'content' => 'Đây là nội dung rất hay và ý nghĩa của bài viết đầu tiên.', 'published_at' => now(), ]); // Hoặc cách khác, thông qua mối quan hệ (ngầu hơn!) $user->posts()->create([ 'title' => 'Bài viết thứ hai của John', 'content' => 'Nội dung bài viết thứ hai.', 'published_at' => now(), ]); -
Read (Đọc/Truy vấn):
use App\Models\User; use App\Models\Post; // Lấy tất cả người dùng $users = User::all(); // Tìm người dùng theo ID $john = User::find(1); // Trả về null nếu không tìm thấy $jane = User::findOrFail(2); // Ném exception nếu không tìm thấy // Tìm bài viết theo điều kiện $publishedPosts = Post::where('published_at', '<=', now()) ->orderBy('published_at', 'desc') ->get(); // Lấy một bài viết duy nhất $singlePost = Post::where('title', 'LIKE', '%đầu tiên%')->first(); // Truy cập các thuộc tính if ($singlePost) { echo $singlePost->title; // "Bài viết đầu tiên của tôi" echo $singlePost->content; echo $singlePost->short_content; // Sử dụng accessor! } -
Update (Cập nhật):
use App\Models\User; $user = User::find(1); if ($user) { $user->name = 'Johnathan Doe'; $user->email = 'johnathan.doe@example.com'; $user->save(); // Lưu thay đổi vào database // Cập nhật nhiều trường cùng lúc $user->update([ 'email' => 'new.johnathan@example.com' ]); } // Cập nhật nhiều bản ghi một lúc Post::where('published_at', null)->update(['published_at' => now()]); -
Delete (Xóa):
use App\Models\Post; $post = Post::find(1); if ($post) { $post->delete(); // Xóa bản ghi có ID là 1 } // Xóa nhiều bản ghi theo điều kiện Post::where('published_at', '<', '2023-01-01')->delete();
Quan Hệ (Relationships): Khi Các 'Sứ Giả' Bắt Đầu Kết Nối Với Nhau
Đây là phần khiến Model trở nên "sống động" và mạnh mẽ. Hãy tưởng tượng Model là các "đại sứ", và relationships là những "hiệp ước ngoại giao" giữa họ.
-
Usercó nhiềuPost(One-to-Many): TrongUser.php:public function posts() { return $this->hasMany(Post::class); }Sử dụng:
$user = User::find(1); foreach ($user->posts as $post) { echo $post->title . "\n"; } -
Postthuộc về mộtUser(Many-to-One): TrongPost.php:public function user() { return $this->belongsTo(User::class); }Sử dụng:
$post = Post::find(1); echo $post->user->name; // Lấy tên người tạo bài viết
Accessors & Mutators: 'Sứ Giả' Khoe Sắc Hay Thay Đổi Dung Nhan
Đây là cách bạn "biến hóa" dữ liệu khi đọc ra hoặc trước khi lưu vào database.
-
Accessor (Getter): Định nghĩa một phương thức
get{AttributeName}Attribute. Ví dụ, lấy tên đầy đủ từfirst_namevàlast_name(giả sử bảnguserscó 2 trường này).Trong
User.php:public function getFullNameAttribute() { return "{$this->first_name} {$this->last_name}"; }Sử dụng:
$user = User::find(1); echo $user->full_name; // Truy cập như một thuộc tính bình thường -
Mutator (Setter): Định nghĩa một phương thức
set{AttributeName}Attribute. Ví dụ, tự động viết hoa tên khi lưu.Trong
User.php:public function setFirstNameAttribute($value) { $this->attributes['first_name'] = ucfirst($value); }Sử dụng:
$user = User::find(1); $user->first_name = 'john'; // Sẽ tự động lưu thành 'John' $user->save();
Mẹo Vặt & Best Practices: 'Bí Kíp Luyện Rồng' Để Thành Thạo Model
Để trở thành một "cao thủ" điều khiển các "sứ giả" dữ liệu, bạn cần nắm vững vài bí kíp sau:
-
Quy Tắc Đặt Tên (Naming Conventions): Laravel rất "thông minh" trong việc suy luận tên bảng từ tên Model.
- Model: Luôn là danh từ số ít, viết hoa chữ cái đầu (PascalCase), ví dụ:
User,Product,BlogPost. - Table: Luôn là danh từ số nhiều, viết thường, dùng dấu gạch dưới (snake_case) nếu có nhiều từ, ví dụ:
users,products,blog_posts. - Primary Key: Mặc định là
id. - Foreign Key: Mặc định là
tên_model_số_ít_id, ví dụ:user_id,post_id. - Timestamp: Mặc định là
created_atvàupdated_at. Nếu bạn tuân thủ các quy tắc này, Laravel sẽ tự động làm mọi thứ cho bạn mà không cần cấu hình thêm.
- Model: Luôn là danh từ số ít, viết hoa chữ cái đầu (PascalCase), ví dụ:
-
$fillablevs.$guarded: "Cảnh Cổng An Ninh" Của Dữ Liệu: Đây là một trong những tính năng bảo mật quan trọng nhất của Model. Nó giúp ngăn chặn lỗ hổng "Mass Assignment Vulnerability".$fillable: Một mảng chứa tất cả các thuộc tính được phép gán giá trị hàng loạt (khi dùngcreate()hoặcupdate()với một mảng dữ liệu). Hãy luôn dùng$fillableđể chỉ rõ những gì được phép.$guarded: Một mảng chứa các thuộc tính không được phép gán giá trị hàng loạt. Nếu bạn để$guarded = [], tất cả các thuộc tính đều được phép gán hàng loạt. Thường thì$fillableđược ưu tiên hơn vì nó minh bạch và an toàn hơn.
// Trong Post.php protected $fillable = [ 'user_id', 'title', 'content', 'published_at', ]; // Không thêm 'id' hoặc 'created_at' vào đây! -
Eager Loading (Sử dụng
with()): "Tránh Hội Chứng N+1 Query": Đây là một lỗi hiệu năng kinh điển mà nhiều người mới mắc phải. Khi bạn truy vấn một danh sách bài viết, sau đó trong vòng lặp lại đi truy vấn tên người dùng cho từng bài viết:$posts = Post::all(); // 1 query foreach ($posts as $post) { echo $post->user->name; // N query (mỗi bài viết 1 query để lấy user) } // Tổng cộng: 1 + N queries (N+1 problem)Để giải quyết, hãy dùng
with()để "tải trước" các mối quan hệ:$posts = Post::with('user')->get(); // Chỉ 2 queries (1 cho posts, 1 cho users) foreach ($posts as $post) { echo $post->user->name; // User đã được tải sẵn, không có thêm query }Đây là một "chiêu thức" cực kỳ quan trọng để tối ưu hiệu năng ứng dụng.
-
Scopes: "Bộ Lọc Thông Minh" Cho Truy Vấn: Scopes cho phép bạn định nghĩa các tập hợp truy vấn tái sử dụng được. Rất tiện lợi khi bạn có những điều kiện truy vấn lặp đi lặp lại.
// Trong Post.php public function scopePublished($query) { return $query->where('published_at', '<=', now()); } public function scopePopular($query) { return $query->where('views', '>', 1000); }Sử dụng:
$publishedPosts = Post::published()->get(); $popularPublishedPosts = Post::published()->popular()->get(); -
Events và Observers: "Khi Dữ Liệu Tự Động Hành Động": Model có thể phát ra các sự kiện khi được tạo, cập nhật, xóa (
creating,created,updating,updated,deleting,deleted, v.v.). Bạn có thể lắng nghe các sự kiện này để thực hiện các hành động tự động (ví dụ: gửi email xác nhận khi người dùng mới được tạo, xóa các file liên quan khi một bài viết bị xóa). Observers giúp gom logic xử lý sự kiện vào một lớp duy nhất. -
Docblocks cho Model: Tuy không trực tiếp ảnh hưởng đến hoạt động của code, nhưng việc thêm Docblocks (PHP DocBlocks) với
@propertyvà@methodcho các thuộc tính và mối quan hệ sẽ giúp IDE (như PhpStorm) cung cấp gợi ý tự động (autocompletion) tốt hơn, giúp bạn code nhanh và ít lỗi hơn./** * @property int $id * @property string $name * @property string $email * @property \Illuminate\Support\Carbon|null $email_verified_at * @property string $password * @property string|null $remember_token * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Post[] $posts * @property-read int|null $posts_count * @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|User newQuery() * @method static \Illuminate\Database\Eloquent\Builder|User query() * @mixin \Eloquent */ class User extends Authenticatable { // ... } -
Model không phải là nơi chứa mọi logic: Mặc dù Model có thể chứa logic nghiệp vụ, nhưng hãy cẩn thận đừng biến nó thành một "God Object" (đối tượng chứa quá nhiều trách nhiệm). Các logic phức tạp, độc lập với dữ liệu cụ thể của Model, nên được tách ra thành các Service Class, Repository Pattern, hay Action Class để giữ cho Model được gọn gàng và dễ bảo trì.
Như vậy, Model trong Laravel không chỉ là một công cụ để tương tác với database, mà nó còn là một phần thiết yếu trong kiến trúc ứng dụng của bạn, giúp tổ chức code một cách logic, dễ đọc, dễ bảo trì và mạnh mẽ. Nắm vững Model là bạn đã nắm được một nửa sức mạnh của Laravel rồi đó! Hãy thực hành nhiều để các "sứ giả" dữ liệu này trở thành những trợ thủ đắc lực của bạn nhé!
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é!