嘗試說一說事件的使用

icecho發表於2019-03-26

事件驅動程式設計是一種程式設計模式,其中的程式流由諸如使用者動作(滑鼠點選,按鍵)、感測器輸出或來自其他程式/執行緒的訊息等事件來決定確定。事件驅動程式設計是圖形使用者介面和其他應用程式(例如 JavaScript Web 應用程式)中使用的主要範例,用於執行某些操作來響應使用者輸入。

在 Laravel 裡,有些事件是由她自動發起的,例如 Model 的 create、save、 update 或者是 delete 操作時,她會分別發起相應的事件,如果我們需要,可以監聽這些事件,完成不同的需求。除了她自動發起的事件,我們也可以自己定義我們需要的事件以及監聽器。

在應用中使用事件,是解耦應用的好方法,比如註冊一位新使用者。

Class UserController extends Controller
{
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            ...
            ...
        ]);

        $user = User::create($request->all());

        // 歡迎郵件
        if ($user->email) {
            Mail::to($user->email)->send(new Email($user));
        }

        // 推薦註冊邏輯
        if ($request->get('invite_code')) {
            ...
            ...
        }

        // 通知運營
        $user->notify(new NewUser($user));

        // 各種各樣的需求
        ...
        ...

        return Auth::login($user);
    }
}

在這個使用者註冊方法中,耦合了各種各樣的邏輯,各種各樣的需求,如果還要加一些奇怪的需求,這個方法就會越來越大,越來越長。

方法的關注點應該只有註冊使用者到應用中,它不應該關心其他邏輯。

這裡我們通過 Laravel 的 artisan 命令,建立我們需要的事件類和與之對應的監聽類。

php artisan make:event UserRegistered

php artisan make:listener SendWelcomeMail --event=UserRegistered

php artisan make:listener UpdateReferrer --event=UserRegistered

接下來,不要忘記到 EventServiceProvider 裡的 $listen 屬性中註冊事件與監聽器。

protected $listen = [ 
    UserRegistered::class => [ 
        SendWelcomeMail::class, 
        UpdateReferrer::class,
    ]
];

開啟 app/Events/UserRegistered.php

public $user;

public function __construct(User $user)
{
    $this->user = $user;
}

接著開啟 SendWelcomeMail.php

public function handle(UserRegistered $event)
{
    if (!$event->user->email) {
        return;
    }

    Mail::to($event->user->email)->send(new Email($event->user));
}

再開啟 UpdateReferrer.php

public function handle(UserRegistered $event)
{
    ...
    ...
}

可以看到,通過事件驅動,我們讓 UserController 的 store 方法,變得儘可能的少,並且專注於註冊使用者這件事上,其他邏輯由 UserRegistered 事件的監聽器來負責完成。如果有新增加的需求,可是建立一個新的監聽器來完成這件事。

當完成了事件與監聽器的編寫與註冊之後,我們可以把它放進控制器邏輯。

public function store(Request $request)
{
    ...
    ...
    ...

    \event(new UserRegistered($user));
}

我個人比較喜歡把它放在模型事件中觸發,可以嘗試這麼寫。

class User extends Model
{
    public static function boot()
    {
        parent::boot();

        static::created(function (User $user) {
            \event(new UserRegistered($user));
        });
    }
}

我覺得使用 Laravel 的事件系統,可以讓我們的作品變得更優雅,邏輯條理更清晰,也更具有擴充性。讓每部分程式碼塊,都知道自己是幹什麼的,要幹什麼,不需要關注與自己無關的事兒。程式碼塊之間分工合作,你中有我,我中有你,最終把這件事情完成好。

Hello。

相關文章