This time I will bring you a detailed explanation of the steps to implement Laravel model events. What are the precautions for implementing Laravel model events? The following is a practical case, let's take a look.
Preface
Laravel’s ORM model will trigger a series of events under certain circumstances. The currently supported events are as follows: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored, so how is this function implemented at the bottom level?
Not much to say below, let’s take a look at the detailed introduction.
1. How to use model events
Let’s first look at how to use model events. There are two methods written in the document. In fact, there are two methods. There are three ways to define a model event. Here we take the saved event as an example, and the other events are the same.
1.events attribute
Directly enter the code:
class User extends Authenticatable { use Notifiable; protected $events = [ 'saved' => UserSaved::class, ]; }
This is difficult to understand, and the document does not explain it in detail. At first I thought that saved was After triggering, the handle method in UserSaved will be called, but it is not actually the case. This array is just a mapping of events. It defines that the UserSaved event will be triggered when the model is saved. We also need to define the event and its listener:
namespace App\Events; use App\User; class UserSaved { public $user; public function construct(User $user){ $this->user = $user; } }
namespace App\Listeners; class UserSavedListener { public function handle(UserSaved $userSaved){ dd($userSaved); } }
Then we also need to go to the EventServiceProvider Register the event and listener:
class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'App\Events\UserSaved' => [ 'App\Listeners\UserSavedListener', ] ]; /** * Register any events for your application. * * @return void */ public function boot() { parent::boot(); } }
In this way, when the saved node is saved, the UserSaved event will be triggered, and the handle method of its listener UserSavedListener will be called.
2. Observer
This is a model event definition method recommended by the document, and it is also easier to understand. First define an observer:
use App\User; class UserObserver { /** * 监听用户创建事件. * * @param User $user * @return void */ public function created(User $user) { // } /** * 监听用户创建/更新事件. * * @param User $user * @return void */ public function saved(User $user) { // } }
Then register the observer in the boot method of a service provider:
namespace App\Providers; use App\User; use App\Observers\UserObserver; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { User::observe(UserObserver::class); } /** * Register the service provider. * * @return void */ public function register() { // } }
In this way, when the model event is triggered, the corresponding method of UserObserver will be called. In fact, when using observers, in addition to some of the ones that come with the system, we can also define some of our own events:
class User extends Authenticatable { use Notifiable; protected $observables = [ 'customing', 'customed' ]; }
Then define a method with the same name in the observer:
class UserObserver { /** * 监听用户创建/更新事件. * * @param User $user * @return void */ public function saved(User $user) { // } public function customing(User $user){ } public function customed(User $user){ } }
Because it is We define events ourselves, so they must be triggered manually. Just call a fireModelEvent method in the model where it needs to be triggered. However, since this method is protected, it can only be used in the model method defined by yourself. Of course, if it is called through reflection, it may be triggered directly on the $user object. I have not tried this, but you can test it yourself.
class User extends Authenticatable { use Notifiable; protected $observables = [ 'customing', 'awesoming' ]; public function custom(){ if ($this->fireModelEvent('customing') === false) { return false; } //TODO if ($this->fireModelEvent('customed') === false) { return false; } } }
3. Static method definition
We can also define an event through the corresponding static method on the model, defined in the boot method of EventServiceProvider:
class EventServiceProvider extends ServiceProvider{ /** * Register any events for your application. * * @return void */ public function boot() { parent::boot(); User::saved(function(User$user) { }); User::saved('UserSavedListener@saved'); } }
When defined through a static method, it can be passed directly into a closure, or it can be defined as a method of a certain class. When the event is triggered, the parameter passed in is the model instance.
2. Model event implementation principle
All the code for Laravel’s model events is in the Illuminate\Database\Eloquent\Concerns\HasEvents trait Next, let's first take a look at how Laravel registers these events. $dispatcher is an event dispatcher Illuminate\Contracts\Events\Dispatcher instance, which is injected in the boot method of Illuminate\Database\DatabaseServiceProvider.
protected static function registerModelEvent($event, $callback){ if (isset(static::$dispatcher)) { $name = static::class; static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback); } }
This is where Laravel events are registered, with eloquent.saved:App\User as the event name and $callback as the processor. This method of registering events will only register those defined as observers and static methods. If you define it as the $events attribute of the model, Laravel will not register it and will trigger it synchronously when the event is triggered, which will be analyzed next.
Then a bunch of methods are defined in HasEvents as follows. These are the principles of defining event listeners through static methods above. You can understand them at a glance without further explanation.
public static function saving($callback){ static::registerModelEvent('saving', $callback); } public static function saved($callback){ static::registerModelEvent('saved', $callback); }
So how to define event listeners in the form of observers? Look at the source code:
public static function observe($class){ $instance = new static; $className = is_string($class) ? $class : get_class($class); foreach ($instance->getObservableEvents() as $event) { if (method_exists($class, $event)) { static::registerModelEvent($event, $className.'@'.$event); } } } public function getObservableEvents() { return array_merge( [ 'creating', 'created', 'updating', 'updated', 'deleting', 'deleted', 'saving', 'saved', 'restoring', 'restored', ], $this->observables ); }
First get the class name of the observer, and then determine whether there is a method corresponding to the event name. If it exists, call registerModelEvent to register. The event name here also includes the one we defined in the observables array.
After the events and listeners are defined, it’s time to trigger them. As mentioned earlier, there is a method fireModelEvent. Let’s take a look at the source code:
protected function fireModelEvent($event, $halt = true) { if (! isset(static::$dispatcher)) { return true; } $method = $halt ? 'until' : 'fire'; $result = $this->filterModelEventResults( $this->fireCustomModelEvent($event, $method) ); if ($result === false) { return false; } return ! empty($result) ? $result : static::$dispatcher->{$method}( "eloquent.{$event}: ".static::class, $this ); }
其中比较关键的一个方法是fireCustomModelEvent,它接受一个事件名以及触发方式。顺带一提,filterModelEventResults这个方法的作用就是把监听器的返回值为null的过滤掉。
看看fireCustomModelEvent的源码:
protected function fireCustomModelEvent($event, $method) { if (! isset($this->events[$event])) { return; } $result = static::$dispatcher->$method(new $this->events[$event]($this)); if (! is_null($result)) { return $result; } }
这个就是用来触发我们通过$events定义的事件了,假如我们这么定义:
class User extends Model{ protected $events = [ 'saved' => UserSaved::class ] }
那这里的触发就是:
$result = static::$dispatcher->fire(new UserSaved($this));
顺带一提,Laravel中触发事件的方法有两个,一个是常用的fire,还有一个是util,这两个的差别就是fire会把监听器的返回值返回,而util永远返回null
然后接下来就是会去触发通过观察者和静态方法定义的监听器了,这一段代码:
if ($result === false) { return false; } return ! empty($result) ? $result : static::$dispatcher->{$method}( "eloquent.{$event}: ".static::class, $this );
这里会先判断$events定义的监听器是否返回false以及返回值是否为空,如果为false则直接结束事件,如果返回不为false而且为空的话,会再去触发通过观察者和静态方法定义的监听器,并且把监听器的返回值返回。
完。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
The above is the detailed content of Detailed explanation of Laravel model event implementation steps. For more information, please follow other related articles on the PHP Chinese website!