跳转到内容

事件

Goravel 的事件提供了一个简单的观察者模式实现,允许您订阅和监听应用程序中发生的各种事件。 事件类通常存储在 app/events 目录中,而它们的监听器存储在 app/listeners 中。 如果您在应用程序中没有看到这些目录,请不要担心,因为当您使用 Artisan 控制台命令生成事件和监听器时,它们将为您创建。

事件是解耦应用程序各个方面的好方法,因为单个事件可以有多个不相互依赖的监听器。 例如,您可能希望在每次订单发货时向用户发送 Slack 通知。 与其将订单处理代码与 Slack 通知代码耦合在一起,不如触发一个 app\events\OrderShipped 事件,监听器可以接收该事件并用于发送 Slack 通知。

注册事件和监听器

Goravel 应用程序中包含的 app\providers\EventServiceProvider 提供了一个方便的地方来注册应用程序的所有事件监听器。 该 listen 方法包含一个数组,其中包含所有事件(键)及其监听器(值)。 您可以根据应用程序的需要向此数组添加任意数量的事件。 例如,让我们添加一个 OrderShipped 事件:

go
package providers

import (
  "github.com/goravel/framework/contracts/event"
  "github.com/goravel/framework/facades"

  "goravel/app/events"
  "goravel/app/listeners"
)

type EventServiceProvider struct {
}

...

func (receiver *EventServiceProvider) listen() map[event.Event][]event.Listener {
  return map[event.Event][]event.Listener{
    &events.OrderShipped{}: {
      &listeners.SendShipmentNotification{},
    },
  }
}

生成事件和监听器

您可以使用 make:eventmake:listener Artisan 命令来生成单独的事件和监听器:

go
go run . artisan make:event PodcastProcessed
go run . artisan make:event user/PodcastProcessed

go run . artisan make:listener SendPodcastNotification
go run . artisan make:listener user/SendPodcastNotification

定义事件

事件类本质上是一个数据容器,用于保存与事件相关的信息,eventHandle 方法传入并返回 []event.Arg 结构,可用于处理数据。 处理后的数据随后将传递给所有关联的 listeners。 例如,假设有一个 app\events\OrderShipped 事件:

go
package events

import "github.com/goravel/framework/contracts/event"

type OrderShipped struct {
}

func (receiver *OrderShipped) Handle(args []event.Arg) ([]event.Arg, error) {
  return args, nil
}

定义监听器

接下来,让我们看看我们示例事件的监听器。 事件监听器接收事件 Handle 方法返回的 []event.Arg。 在 Handle 方法中,您可以执行任何必要的操作来响应事件:

go
package listeners

import (
  "github.com/goravel/framework/contracts/event"
)

type SendShipmentNotification struct {
}

func (receiver *SendShipmentNotification) Signature() string {
  return "send_shipment_notification"
}

func (receiver *SendShipmentNotification) Queue(args ...any) event.Queue {
  return event.Queue{
    Enable:     false,
    Connection: "",
    Queue:      "",
  }
}

func (receiver *SendShipmentNotification) Handle(args ...any) error {
  return nil
}

停止事件的传播

有时,您可能希望停止事件向其他监听器的传播。 您可以通过从监听器的 Handle 方法返回错误来实现这一点。

队列事件监听器

如果您的监听器将执行诸如发送电子邮件或发出HTTP请求等耗时任务,队列监听器可能会很有用。 在使用队列监听器之前,请确保配置您的队列并在您的服务器或本地开发环境上启动队列工作进程。

go
package listeners

...

func (receiver *SendShipmentNotification) Queue(args ...any) event.Queue {
  return event.Queue{
    Enable:     false,
    Connection: "",
    Queue:      "",
  }
}

func (receiver *SendShipmentNotification) Handle(args ...any) error {
  name := args[0]

  return nil
}

队列事件监听器和数据库事务

当队列监听器在数据库事务中被调度时,队列可能会在数据库事务提交之前处理它们。 当这种情况发生时,你在数据库事务期间对模型或数据库记录所做的任何更新可能还没有反映在数据库中。 此外,在事务中创建的任何模型或数据库记录可能在数据库中不存在。 如果你的监听器依赖这些模型,当处理调度队列监听器的作业时可能会发生意外错误。 此时,需要将事件放置在数据库事务之外。

调度事件

我们可以通过 facades.Event().Job().Dispatch() 方法调度事件。

go
package controllers

import (
  "github.com/goravel/framework/contracts/event"
  "github.com/goravel/framework/contracts/http"
  "github.com/goravel/framework/facades"

  "goravel/app/events"
)

type UserController struct {
}

func (r UserController) Show(ctx http.Context) {
  err := facades.Event().Job(&events.OrderShipped{}, []event.Arg{
    {Type: "string", Value: "Goravel"},
    {Type: "int", Value: 1},
  }).Dispatch()
}

event.Arg.Type 支持的类型

go
bool
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
float32
float64
string
[]bool
[]int
[]int8
[]int16
[]int32
[]int64
[]uint
[]uint8
[]uint16
[]uint32
[]uint64
[]float32
[]float64
[]string

基于 MIT 许可发布