Home > Backend Development > PHP Tutorial > Detailed explanation of Laravel model event implementation steps

Detailed explanation of Laravel model event implementation steps

php中世界最好的语言
Release: 2023-03-26 14:16:01
Original
2041 people have browsed it

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,
 ];
}
Copy after login

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;
 }
}
Copy after login
namespace App\Listeners;
class UserSavedListener {
 public function handle(UserSaved $userSaved){
  dd($userSaved);
 }
}
Copy after login

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();
 }
}
Copy after login

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)
 {
  //
 }
}
Copy after login

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()
 {
  //
 }
}
Copy after login

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'
 ];
}
Copy after login

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){
 }
}
Copy after login

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;
  }
 }
}
Copy after login

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');
 }
}
Copy after login

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);
  }
 }
Copy after login

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);
}
Copy after login

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
  );
 }
Copy after login

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
  );
 }
Copy after login

其中比较关键的一个方法是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;
  }
 }
Copy after login

这个就是用来触发我们通过$events定义的事件了,假如我们这么定义:

class User extends Model{
 protected $events = [
  'saved' => UserSaved::class
 ]
}
Copy after login

那这里的触发就是:

 $result = static::$dispatcher->fire(new UserSaved($this));
Copy after login

顺带一提,Laravel中触发事件的方法有两个,一个是常用的fire,还有一个是util,这两个的差别就是fire会把监听器的返回值返回,而util永远返回null

然后接下来就是会去触发通过观察者和静态方法定义的监听器了,这一段代码:

  if ($result === false) {
   return false;
  }
  return ! empty($result) ? $result : static::$dispatcher->{$method}(
   "eloquent.{$event}: ".static::class, $this
  );
Copy after login

这里会先判断$events定义的监听器是否返回false以及返回值是否为空,如果为false则直接结束事件,如果返回不为false而且为空的话,会再去触发通过观察者和静态方法定义的监听器,并且把监听器的返回值返回。
完。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

phpStudy2018安装与配置步骤详解

ThinkPHP实现微信支付(jsapi支付)步骤详解

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!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template