Laravel中的事件与监听使用的了观察者模式,观察者模式可以做到优雅的处理一连串的动作,动态的增加和减少动作,而不用去改变主线业务代码。事件类通常存放在app/Events
目录中,而这些事件类的监听器则存放在 app/Listeners
中。是应用中实现解耦的非常的好的一种方法。例如:当用户在应用中使用会员权益购买一样商品,在购买时,需要消耗用户相应的剩余权益。这时候你就使用监听可以把订单生成和权益消耗区分开。
参考: 前人栽树、后人乘凉1:观察者模式和监听者模式
Laravel 事先已经定义好了 10多 个模型事件以供我们使用,它们分别是:
public function getObservableEvents()
{
return array_merge(
[
'retrieved', 'creating', 'created', 'updating', 'updated',
'saving', 'saved', 'restoring', 'restored', 'replicating',
'deleting', 'deleted', 'forceDeleted',
],
$this->observables
);
}
这些事件名称都很浅显易懂。具体使用可以打开**Illuminate\Database\Eloquent\Model ,**找到save()
方法:
public function save(array $options = [])
{
$query = $this->newModelQuery();
// If the "saving" event returns false we'll bail out of the save and return
// false, indicating that the save failed. This provides a chance for any
// listeners to cancel save operations if validations fail or whatever.
if ($this->fireModelEvent('saving') === false) {
return false;
}
// If the model already exists in the database we can just update our record
// that is already in this database using the current IDs in this "where"
// clause to only update this model. Otherwise, we'll just insert them.
if ($this->exists) {
$saved = $this->isDirty() ?
$this->performUpdate($query) : true;
}
// If the model is brand new, we'll insert it into our database and set the
// ID attribute on the model to the value of the newly inserted row's ID
// which is typically an auto-increment value managed by the database.
else {
$saved = $this->performInsert($query);
if (! $this->getConnectionName() &&
$connection = $query->getConnection()) {
$this->setConnection($connection->getName());
}
}
// If the model is successfully saved, we need to do a few more things once
// that is done. We will call the "saved" method here to run any actions
// we need to happen after a model gets successfully saved right here.
if ($saved) {
$this->finishSave($options);
}
return $saved;
}
可以看到首先触发的是 saving:
$this->fireModelEvent('saving')
接着会判断此 model 是不是新创建的。
如果是新创建的,执行 $saved = $this->performInsert($query);
进行 insert 操
作,否则执行$saved = $this->isDirty() ? $this->performUpdate($query) : true;
先判
调用 isDirty 方法判断此 model 是否进行了修改。
如果进行了修改,那么会调用 performUpdate 方法进行更新操作,如果没有进行修改,则直接返回 true。
performUpdate /performInsert
方法执行以后,可以看到 Laravel 最后做了一个判断:if ($saved) {$this->finishSave($options)};
在 update/insert 成功的情况下,会触发 finishSave() 方法,此方法会接收一个参数 $options, 即是修改的属性。
之后就是finishSave()
方法,里面时:
protected function finishSave(array $options)
{
$this->fireModelEvent('saved', false);
if ($this->isDirty() && ($options['touch'] ?? true)) {
$this->touchOwners();
}
$this->syncOriginal();
}
可以看到在此方法内会触发 saved 事件;
也就是说:
修改EventEventServiceProvider
public function boot()
{
parent::boot();
SrtVideoTranslate::observe(SrtVideoTranslateTaskObserve::class);//新增的
}
或者在AppServiceProvider
里注册
<?php
namespace App\Observers;
use App\Models\SrtVideoTranslate;
use App\Services\TranslateEvents\SrtVideoTranslateStatusChange;
class SrtVideoTranslateTaskObserve
{
public function saved(SrtVideoTranslate $task)
{
info(123456789);
info(json_encode($task));
if ($task->project_id !== "web") {
return;
}
// if (!$task->isDirty("status")) {
// return;
// }
if ($task->status === SrtVideoTranslate::TASK_STARTED) {
SrtVideoTranslateStatusChange::started($task);//这里是扣除用户会员权益的逻辑
} else if ($task->status === SrtVideoTranslate::TASK_ERROR) {
SrtVideoTranslateStatusChange::failed($task);//这是恢复用户已扣除会员权益的逻辑
}
}
}