NTM Solutions

Thứ Năm, 27 tháng 8, 2026

📘 Laravel 12 (2026) — BÀI 33 — OBSERVER TRONG LARAVEL

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:

EventThời điểm
retrievedModel được lấy từ database
creatingTrước khi tạo
createdSau khi tạo
updatingTrước khi cập nhật
updatedSau khi cập nhật
savingTrước khi save
savedSau khi save
deletingTrước khi xóa
deletedSau khi xóa
restoringTrước khi restore
restoredSau khi restore
forceDeletedSau khi force delete

Laravel cho phép Observer lắng nghe nhiều Eloquent event khác nhau, không chỉ created, updateddeleted.


6. creatingcreated

Đâ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. updatingupdated

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. deletingdeleted

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 ObservedByobserve().

x0
quay về MỤC LỤC

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

Đăng nhận xét

Facebook Youtube RSS