带有 socket-io 的 Laravel 事件 [接收通知]

Posted

技术标签:

【中文标题】带有 socket-io 的 Laravel 事件 [接收通知]【英文标题】:Laravel event with socket-io [receive notifications] 【发布时间】:2020-08-26 17:41:43 【问题描述】:

第一次尝试使用laravelsocket-io 我正在尝试向管理员发送非常简单的通知。 到目前为止,我的事件正在触发,但我在接收事件通知方面需要帮助

逻辑

这是非常基础的,因为我想了解这个过程。

    用户打开页面Add Product 管理员收到user XApp Product 页面中的通知。

到目前为止

到目前为止,我可以触发事件并获取用户数据(Add Product 页面中的用户)

需要帮助

我需要帮助来了解管理员接收通知的方式。

代码

组件脚本

created() 
  let user = JSON.parse(localStorage.getItem("user"))
  this.listenForBroadcast(user);
,
methods: 
  listenForBroadcast(user) 
    Echo.join('userInAddProduct')
    .here((Loggeduser) => 
      console.log('My user data', Loggeduser);
    );
  

以上代码的结果

My user data […]
  0:
    id: 1
    name: "Test User"
    photo: "User-1588137335.png"
    __ob__: Observer value: …, dep: Dep, vmCount: 0
    get id: ƒ reactiveGetter()
    set id: ƒ reactiveSetter(newVal)
    get name: ƒ reactiveGetter()
    set name: ƒ reactiveSetter(newVal)
    get photo: ƒ reactiveGetter()
    set photo: ƒ reactiveSetter(newVal)
    __proto__: Object
    length: 1
    __proto__: Array(0)

渠道路线

Broadcast::channel('userInAddProduct', function ($user) 
    return [
        'id' => $user->id,
        'photo' => $user->photo,
        'name' => $user->name
    ];
);

MessagePushed(事件文件)

class MessagePushed implements ShouldBroadcast

    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;

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

    public function broadcastOn()
    
        return new PresenceChannel('userInAddProduct');
    

问题

如何接收有关此事件触发的通知?我想通知我的管理员用户 user x 在页面 Add Product 中?

更新

自从我发布这个问题以来,我做了一些更改,这是我最新的代码 + 问题。

bootstrap.js

window.io = require('socket.io-client');

window.Echo = new Echo(
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001',
    auth:  // added authentication token (because all my events are private)
        headers: 
            Authorization: localStorage.getItem('access_token'),
        ,
    ,
);

Add.vue (add product component where event has to be fired)

listenForBroadcast(user) 
    let ui = JSON.parse(localStorage.getItem("user"))
    Echo.join('userInAddProduct')
    .here((users) => 
        console.log('My user data', users)
    )
    .joining((user) => 
        this.$notify(
            title: '',
            message: user + 'joining',
            offset: 100,
            type: 'success'
        );
    )
    .leaving((user) => 
        this.$notify(
            title: '',
            message: user + 'is leaving new product',
            offset: 100,
            type: 'warning'
        );
    )
    .whisper('typing', (e) => 
        this.$notify(
            title: '',
            message: ui.username + 'is adding new product',
            offset: 100,
            type: 'success'
        )
    )
    .listenForWhisper('typing', (e) => 
        console.log(e)
        this.$notify(
            title: '',
            message: ui.username + 'is entered add new product page.',
            offset: 100,
            type: 'success'
        );
    )
    .notification((notification) => 
        console.log('noitication listener: ', notification.type);
    );
,

然后我制作了 4 个文件来处理事件:

事件 (passing data) 事件监听器(process the database storageshowing notifications 在线管理员) 观察者(射击事件) 通知(store data to database 为管理员提供,以防事件触发时他们不在线,以便他们稍后可以看到通知)

Event file

class MessagePushed extends Event implements ShouldBroadcast

    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;
    public $product;

    public function __construct(User $user, Product $product)
    
        $this->user = $user;
        $this->product = $product;
    

    public function broadcastOn()
    
        return new PresenceChannel('userInAddProduct');
    

Listener file

class ThingToDoAfterEventWasFired implements ShouldQueue

    public function handle(MessagePushed $event)
    
        //Log testing purpose only
        $user = $event->user->username;
        $product = $event->product->name;

        // Real data that should be broadcasts
        $user2 = $event->user;
        $product2 = $event->product;

        // inform all admins and authorized staffs about new product
        $admins = User::role(['admin', 'staff'])->get();
        foreach($admins as $admin) 
            $admin->notify(new UserAddProduct($user2, $product2));
        

        Log::info("Product $product was Created, by worker: $user");
    

Notification

class UserAddProduct extends Notification implements ShouldQueue

    use Queueable;

    protected $product;
    protected $user;

    public function __construct(User $user, Product $product)
    
        $this->product = $product;
        $this->user = $user;
    

    public function via($notifiable)
    
        return ['database', 'broadcast'];
    

    public function toDatabase($notifiable)
    
        return [
            'user_id' => $this->user->id,
            'user_username' => $this->user->username,
            'product_id' => $this->product->id,
            'product_name' => $this->product->name,
        ];
    

    public function toArray($notifiable)
    
        return [
            'id' => $this->id,
            'read_at' => null,
            'data' => [
                'user_id' => $this->user->id,
                'user_username' => $this->user->username,
                'product_id' => $this->product->id,
                'product_name' => $this->product->name,
            ],
        ];
    

Observer

public function created(Product $product)

    $user = Auth::user();
    event(new MessagePushed($user, $product));

问题

    如何在在整个应用程序中触发事件后立即返回实时通知?目前我的代码放在add.vue component admins get notify IF they are in same page only :/ 如何获得多个事件的通知?假设我有另一个 event, listener, observer 用于其他页面操作,我希望管理员在整个应用中同时收到 product eventother event 的通知。

谢谢

【问题讨论】:

你在客户端使用 socket.io:socket.io/docs 吗?如果是,您可以简单地使用 socket.on('event_fire_name', function() /* */ );在管理面板中接收通知 @AhmedRebai 是的,我在客户端使用套接字,您有任何示例代码可以分享作为答案吗? @maforis 看看官方文档socket.io/docs/client-api,它会指导你 ....没有人?! :// @Shizzen83 非常感谢你,迫不及待想看:) 【参考方案1】:

我们区分公共和私人等渠道类型。

公共:照亮\广播\频道。它无需任何身份验证即可工作,因此匿名用户可以收听此广播。就像所有人都可以看到的足球比赛比分一样。

私人:照亮\广播\私人频道。它与身份验证一起使用,因此只有经过身份验证的用户才能收听此广播。就像管理员查看特定用户的操作或用户查看他们的订单状态一样。

另一方面,还有另一个私有频道,即 Illuminate\Broadcasting\PresenceChannel。还需要认证。例如,就像任何聊天室一样。例如:只有经过身份验证的用户才能相互通信。

所以这些类型将定义您的方法。你必须走不同的道路。我非常建议阅读 Laravel 文档 (https://laravel.com/docs/broadcasting),它会非常仔细地指导你。

routes/channels.php 中,您必须决定或验证经过身份验证的用户是否可以收听此特定频道。所以你必须返回布尔值(真或假)。因此,在这里,您不会向客户端返回任何值。

MessagePushed 广播事件中的每个公共属性都将被序列化发送客户端。您必须将每个信息数据提供给 MessagePushed 构造函数并设置为属性/属性。现在您可以像这样处理接收到的数据,例如:

window.Echo = new Echo(
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001',
    auth: 
      headers: 
        'X-Auth-Token': "<token>"
      
    
);

window.Echo.private('userInAddProduct')
   .listen('MessagePushed ', (e) => 
       console.log(e.auction);
    );

我建议使用令牌创建自定义身份验证。上面我将令牌发送到服务器以验证用户。 https://laravel.com/docs/authentication#adding-custom-guards

在你的情况下,我会将这两种机制分开。

    检查用户是否进入应用产品页面 通知管理员

对于第二点,使用 Illuminate\Broadcasting\PrivateChannel 就足够了。所以在管理页面上需要 websocket。 1. 点可能很棘手,因为这取决于您的要求。通知管理员用户进入确切的应用产品页面就足够了?

如果是,那么当用户访问该页面时,您将在控制器中触发此广播事件。

如果还不够,那么您需要用户页面上的另一个 websocket 来“轮询”您的服务器并每隔 X 秒向管理员发送一次滴答广播。

触发广播事件:

broadcast(new App\Events\MessagePushed($user));

我的用法是:

客户端:socket.io-client 服务器端:laravel-echo-server redis 广播器 自定义身份验证机制

希望,我能帮上忙!

【讨论】:

感谢您的回答,您介意查看我的更新吗?我认为这可能会帮助您理解我的问题,从而更好地帮助我:) 欣赏它。 我认为您在实现目标方面走错了路。我怎么说 PresenceChannel 就像聊天室,并不是监控用户的最佳选择。您想以哪种方式通知管理员用户,仅使用 websocket? 老实说,我对此有点困惑,我的总体计划是:如果管理员在线(在任何页面中)获取实时通知。如果他的离线通知存储在数据库中,并且他一上线就看到它。 你有什么建议? 在这种情况下,我将为管理员用户创建脚本,该脚本将加载到每个页面上,该脚本包含 socket.io 连接。 (window.Echo = 新的 Echo ...)。当用户是管理员时,您可以包含此脚本。这些特定的控制器会调用一些 CustomHandlerClass 来将查看操作存储到数据库中。所以也许你可以创建一个监控模型来存储和获取用户视图。您也可以在此 CustomHandlerClass “broadcast(new App\Events\MessagePushed($user));”中触发广播。因此,您将每次都存储到数据库中,但您也发送广播。【参考方案2】:

我最近写了一篇文章How to use Laravel WebSockets for NuxtJs notifications,我在其中详细描述了 Laravel WebSockets 的事件设置。我希望你觉得它有用。

【讨论】:

谢谢你,如果没有工作或有任何问题,我会阅读并回复你 好吧,你在 nuxt 配置文件上迷路了,我不使用 nuxt(甚至没有安装在我的应用程序中)但是你放置在那里的配置已经存在于我的 bootstrap.js 文件中(你可以看到它在更新部分)你有什么建议?【参考方案3】:

已解决

这是我根据我的需要和我的问题完成的(如果你的情况与我的不同,它可能会有所不同)

在该活动发生的组件中(在我的情况下为Add.vue

Echo.join('userInAddProduct')
.here((users) => 
    console.log('here: ', users);
)
.joining((user) => 
    console.log('joining: ', user);
)
.leaving((user) => 
    console.log('leaving: ', user);
)
.whisper('typing', data: notf) // this part sends my data
.notification((notification) => 
    console.log('noitication listener1: ', notification.type);
);

由于我需要我的通知在所有页面中对管理员可见,我已将我的套接字发射(即Echo.listenForWhisper)添加到我的导航栏组件中。

mounted() 
  this.notifs();
    window.Echo.join('userInAddProduct')
       // listen to broadcast from Add.vue
      .listenForWhisper('typing', (product) => 
        // add it into my navbar notifications list
        this.notifications.unshift(product);
      );

现在每次用户向服务器管理员添加新产品时都会收到通知。

注意要查看我的其他设置,请滚动回我的更新部分并查看Event, Listener &amp; Observe 文件代码。 (您将这些文件和上面的所有这些代码制作成您喜欢的组件,然后您将获得实时通知)。

【讨论】:

以上是关于带有 socket-io 的 Laravel 事件 [接收通知]的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Echo 私人频道不听

带有私有通道的 Laravel Echo SocketIO

如何检查通过 socket-io 发送的消息是不是被读取?

Socket-IO 库中的版本是不是重要?

Socket-IOS

如何将backbone.js 与websockets/socket-io/nowjs 一起使用