Trong các bài trước, chúng ta đã làm việc trực tiếp với Eloquent:
$user = User::create([
'name' => 'Nguyễn Văn A',
'email' => 'a@gmail.com',
'password' => bcrypt('123456'),
]);
Hoặc:
$user->update([
'name' => 'Trần Văn B',
]);
Nhưng sẽ có trường hợp:
Mỗi khi User được tạo, cập nhật hoặc xóa thì ứng dụng phải tự động thực hiện thêm một công việc nào đó.
Ví dụ:
User mới được tạo → ghi Log.
Post mới được tạo → tự tạo slug.
Post bị xóa → xóa luôn ảnh liên quan.
User được cập nhật → ghi lại lịch sử thay đổi.
Post được publish → thực hiện một xử lý bổ sung.
Nếu viết tất cả những logic này trong Controller, code sẽ rất nhanh trở nên khó quản lý.
Đây là lúc Observer phát huy tác dụng.
1. Observer là gì?
Observer là một class dùng để theo dõi các sự kiện xảy ra trên một Eloquent Model.
Thay vì viết:
$user = User::create([
// ...
]);
// xử lý thêm
ở nhiều Controller khác nhau, chúng ta có thể tập trung các xử lý liên quan đến User vào một Observer.
Mô hình:
User Model
│
├── creating
├── created
├── updating
├── updated
├── deleting
├── deleted
└── ...
│
▼
UserObserver
│
├── Ghi Log
├── Xử lý dữ liệu
├── Xóa file
└── Các tác vụ liên quan
Laravel cho phép tạo Observer bằng Artisan và đăng ký Observer trực tiếp với Model.
2. Khi nào nên sử dụng Observer?
Observer đặc biệt hữu ích khi một Model có nhiều hành động tự động đi kèm.
Ví dụ với Post:
Tạo Post
│
├── Tạo slug
├── Ghi log
└── Xử lý dữ liệu liên quan
Cập nhật Post
│
├── Kiểm tra thay đổi
└── Ghi log
Xóa Post
│
└── Xóa ảnh
Nếu đặt toàn bộ logic này trong PostController, Controller sẽ ngày càng lớn.
Observer giúp tách phần logic này ra:
PostController
│
│ CRUD
▼
Post Model
│
▼
PostObserver
│
├── created()
├── updated()
└── deleted()
3. Tạo Observer
Laravel cung cấp Artisan command:
php artisan make:observer UserObserver --model=User
Observer sẽ được tạo trong:
app/
└── Observers/
└── UserObserver.php
Laravel hỗ trợ make:observer để tạo Observer và có thể chỉ định Model bằng --model.
4. Cấu trúc UserObserver
Sau khi tạo:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
public function created(User $user): void
{
//
}
public function updated(User $user): void
{
//
}
public function deleted(User $user): void
{
//
}
public function restored(User $user): void
{
//
}
public function forceDeleted(User $user): void
{
//
}
}
Mỗi method tương ứng với một Eloquent Model Event.
Ví dụ:
public function created(User $user): void
{
//
}
method này được gọi sau khi User được tạo thành công.
5. Các Event thường dùng
Một Observer có thể theo dõi nhiều Eloquent event.
Một số event quan trọng:
| Event | Thời điểm |
|---|---|
retrieved | Model được lấy từ database |
creating | Trước khi tạo |
created | Sau khi tạo |
updating | Trước khi cập nhật |
updated | Sau khi cập nhật |
saving | Trước khi save |
saved | Sau khi save |
deleting | Trước khi xóa |
deleted | Sau khi xóa |
restoring | Trước khi restore |
restored | Sau khi restore |
forceDeleted | Sau khi force delete |
Laravel cho phép Observer lắng nghe nhiều Eloquent event khác nhau, không chỉ created, updated và deleted.
6. creating và created
Đây là hai event rất dễ nhầm.
creating
Chạy trước khi record được lưu vào database.
public function creating(User $user): void
{
//
}
created
Chạy sau khi record đã được tạo.
public function created(User $user): void
{
//
}
Có thể hình dung:
User::create()
│
▼
creating
│
▼
INSERT DATABASE
│
▼
created
Ví dụ:
public function created(User $user): void
{
logger("User {$user->id} đã được tạo");
}
Khi User được tạo:
User::create([
'name' => 'Nguyễn Văn A',
'email' => 'a@gmail.com',
'password' => bcrypt('123456'),
]);
Observer sẽ tự động chạy:
created()
7. updating và updated
Tương tự:
public function updating(User $user): void
{
//
}
chạy trước khi cập nhật.
Còn:
public function updated(User $user): void
{
//
}
chạy sau khi cập nhật thành công.
Luồng:
$user->update(...)
│
▼
updating
│
▼
UPDATE DATABASE
│
▼
updated
Ví dụ:
public function updated(User $user): void
{
logger("User {$user->id} đã được cập nhật");
}
8. deleting và deleted
Khi xóa Model:
$user->delete();
Laravel có thể kích hoạt:
deleting()
trước khi xóa và:
deleted()
sau khi xóa.
Ví dụ:
public function deleted(User $user): void
{
logger("User {$user->id} đã bị xóa");
}
Điều này đặc biệt hữu ích khi cần xử lý dữ liệu liên quan.
9. Observer với Soft Delete
Trong bài 30 chúng ta đã học:
$user->delete();
với Soft Delete.
Khi sử dụng:
use SoftDeletes;
thì:
$user->delete();
không nhất thiết xóa record khỏi database.
Record sẽ được đánh dấu bằng:
deleted_at
Observer vẫn có thể theo dõi các event liên quan đến Model.
Ví dụ:
public function deleted(User $user): void
{
logger("User {$user->id} đã được soft delete");
}
Khi restore:
$user->restore();
có thể xử lý:
public function restored(User $user): void
{
logger("User {$user->id} đã được khôi phục");
}
10. Đăng ký Observer bằng ObservedBy
Một cách hiện đại là sử dụng attribute:
#[ObservedBy([UserObserver::class])]
Trong Model:
<?php
namespace App\Models;
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Foundation\Auth\User as Authenticatable;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
Cách này giúp mối quan hệ giữa:
User
↓
UserObserver
được thể hiện ngay trên Model.
Laravel hỗ trợ ObservedBy để đăng ký Observer trực tiếp trên Model.
11. Đăng ký Observer bằng observe()
Một cách khác là đăng ký bằng:
User::observe(UserObserver::class);
Ví dụ trong AppServiceProvider:
<?php
namespace App\Providers;
use App\Models\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
User::observe(UserObserver::class);
}
}
Laravel cũng hỗ trợ cách đăng ký Observer thông qua phương thức observe().
12. Nên dùng cách nào?
Trong Laravel hiện đại, có thể dùng:
#[ObservedBy([UserObserver::class])]
hoặc:
User::observe(UserObserver::class);
Với project học tập và Blog CMS của chúng ta, ObservedBy khá dễ hiểu vì Model thể hiện trực tiếp Observer của nó.
Ví dụ:
app/
├── Models/
│ └── User.php
│
└── Observers/
└── UserObserver.php
Trong User.php:
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
13. Ví dụ thực tế với Post
Trong Blog CMS của chúng ta có Model:
Post
Tạo Observer:
php artisan make:observer PostObserver --model=Post
Sau đó:
app/
├── Models/
│ └── Post.php
│
└── Observers/
└── PostObserver.php
Observer:
<?php
namespace App\Observers;
use App\Models\Post;
class PostObserver
{
public function created(Post $post): void
{
logger("Post {$post->id} đã được tạo");
}
public function updated(Post $post): void
{
logger("Post {$post->id} đã được cập nhật");
}
public function deleted(Post $post): void
{
logger("Post {$post->id} đã bị xóa");
}
}
Đăng ký:
<?php
namespace App\Models;
use App\Observers\PostObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
#[ObservedBy([PostObserver::class])]
class Post extends Model
{
//
}
Bây giờ Controller chỉ cần:
$post = Post::create([
'title' => 'Laravel Observer',
'content' => 'Nội dung bài viết',
]);
Không cần gọi:
$postObserver->created($post);
Laravel tự xử lý Observer.
14. Observer giúp Controller gọn hơn
Không dùng Observer:
public function store(Request $request)
{
$post = Post::create($request->validated());
logger("Post {$post->id} đã được tạo");
// xử lý thêm...
return redirect()->route('posts.index');
}
Controller phải biết rất nhiều việc.
Có Observer:
public function store(Request $request)
{
$post = Post::create($request->validated());
return redirect()->route('posts.index');
}
Còn:
public function created(Post $post): void
{
logger("Post {$post->id} đã được tạo");
}
được đưa vào:
PostObserver
Kết quả:
Controller
│
└── Xử lý Request + CRUD
Observer
│
└── Xử lý các phản ứng sau Model Event
Đây chính là một trong những lợi ích lớn nhất của Observer.
15. Observer không phải là Controller
Hai thành phần này có vai trò khác nhau.
Controller
Nhận HTTP Request:
Browser
↓
Route
↓
Controller
↓
Model
Observer
Phản ứng với sự kiện của Model:
Model
↓
Eloquent Event
↓
Observer
Ví dụ:
POST /posts
│
▼
PostController
│
▼
Post::create()
│
▼
Post created event
│
▼
PostObserver::created()
16. Một lưu ý quan trọng
Observer hoạt động với các thao tác Eloquent Model thông thường.
Ví dụ:
$post = Post::find(1);
$post->update([
'title' => 'Tiêu đề mới',
]);
Observer của Model có thể nhận event tương ứng.
Nhưng khi thực hiện các thao tác query hàng loạt như:
Post::where('status', 'draft')
->update([
'status' => 'published',
]);
không nên mặc định nghĩ rằng Observer sẽ nhận được từng Model như khi bạn gọi:
$post->update();
Lý do là thao tác mass update trực tiếp trên query không hydrate từng Model.
Vì vậy, khi cần Observer xử lý từng record, cần thiết kế thao tác dữ liệu phù hợp.
17. Observer và Transaction
Trong những ứng dụng thực tế, Observer đôi khi thực hiện công việc liên quan đến database.
Ví dụ:
Tạo Order
│
├── Order Observer
│
└── Tạo dữ liệu liên quan
Nếu ứng dụng sử dụng database transaction, có trường hợp chúng ta muốn Observer chỉ thực hiện handler sau khi transaction commit thành công.
Laravel hỗ trợ ShouldHandleEventsAfterCommit cho Observer.
Ví dụ:
<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class UserObserver implements ShouldHandleEventsAfterCommit
{
public function created(User $user): void
{
//
}
}
Khi đó, nếu đang nằm trong transaction, event handler sẽ được xử lý sau khi transaction commit. Nếu không có transaction, handler thực hiện bình thường.
Đây là phần nâng cao; khi mới học Observer, chỉ cần hiểu nguyên tắc này là đủ.
18. Khi nào nên dùng Observer?
Observer phù hợp khi:
Model Event
│
▼
Một hành động tự động
Ví dụ:
User
created
↓
Ghi log
Post
created
↓
Xử lý dữ liệu liên quan
Post
deleted
↓
Xử lý file ảnh
User
updated
↓
Ghi lịch sử thay đổi
Observer đặc biệt hữu ích khi logic này có thể xảy ra từ nhiều nơi khác nhau trong ứng dụng.
19. Khi nào không nên lạm dụng Observer?
Không nên biến Observer thành nơi chứa toàn bộ business logic của ứng dụng.
Ví dụ không nên có một Observer khổng lồ:
class PostObserver
{
public function created(Post $post)
{
// 200 dòng code...
}
public function updated(Post $post)
{
// 300 dòng code...
}
public function deleted(Post $post)
{
// 250 dòng code...
}
}
Khi logic quá phức tạp, nên tách sang:
Service
Job
Event / Listener
Đặc biệt, các tác vụ nặng như gửi email, xử lý ảnh lớn hoặc gọi API bên ngoài thường không nên nhét trực tiếp vào Observer.
Những nội dung này sẽ liên quan đến các bài sau trong khóa học:
Bài 34 — Event & Listener
Bài 35 — Queue
Bài 36 — Mail
Bài 37 — Notification
20. Observer trong Blog CMS
Với Blog CMS mà chúng ta đang xây dựng, có thể tổ chức:
Post
│
└── PostObserver
│
├── created()
├── updated()
├── deleted()
└── restored()
Ví dụ:
Tạo Post
↓
PostObserver
↓
Ghi Log
Xóa Post:
Xóa Post
↓
PostObserver
↓
Xử lý dữ liệu/file liên quan
Restore Post:
Restore Post
↓
PostObserver
↓
Xử lý sau khi khôi phục
Như vậy Controller không cần biết toàn bộ những việc phụ xảy ra sau mỗi thao tác.
21. Tóm tắt bài học
Observer là cơ chế giúp chúng ta theo dõi các Eloquent Model Event và đưa những xử lý tự động ra khỏi Controller.
Tạo Observer:
php artisan make:observer UserObserver --model=User
Các event thường gặp:
creating
created
updating
updated
deleting
deleted
restoring
restored
forceDeleted
Đăng ký bằng Attribute:
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
Hoặc:
User::observe(UserObserver::class);
Mô hình cần nhớ:
Controller
│
▼
Eloquent Model
│
▼
Model Event
│
▼
Observer
│
▼
Xử lý tự động
Observer không thay thế Controller.
Observer giúp chúng ta tách những hành động cần tự động thực hiện khi Model được tạo, cập nhật, xóa hoặc khôi phục.
Đây là bước quan trọng để Blog CMS bắt đầu có kiến trúc sạch hơn trước khi chuyển sang Bài 34 — Event & Listener.
Ghi nhớ
Controller thực hiện hành động.
Model lưu trữ và thao tác dữ liệu.
Observer phản ứng khi Model xảy ra sự kiện.
Laravel 12 hỗ trợ cả cách đăng ký Observer bằng ObservedBy và observe().
x0
quay về MỤC LỤC




Không có nhận xét nào:
Đăng nhận xét