<article><h1>Laravel 的事件广播系统</h1><ul><li><a href="#introduction">简介</a><ul><li><a href="#configuration">配置</a></li><li><a href="#driver-prerequisites">对驱动器的要求</a></li></ul></li><li><a href="#concept-overview">概念综述</a><ul><li><a href="#using-example-application">使用示例程序</a></li></ul></li><li><a href="#defining-broadcast-events">定义广播事件</a><ul><li><a href="#broadcast-name">广播名称</a></li><li><a href="#broadcast-data">广播数据</a></li><li><a href="#broadcast-queue">广播队列</a></li></ul></li><li><a href="#authorizing-channels">频道授权</a><ul><li><a href="#defining-authorization-routes">定义授权路由</a></li><li><a href="#defining-authorization-callbacks">定义授权回调</a></li></ul></li><li><a href="#broadcasting-events">对事件进行广播</a><ul><li><a href="#only-to-others">只广播给他人</a></li></ul></li><li><a href="#receiving-broadcasts">接收广播</a><ul><li><a href="#installing-laravel-echo">安装 Laravel Echo</a></li><li><a href="#listening-for-events">对事件进行监听</a></li><li><a href="#leaving-a-channel">退出频道</a></li><li><a href="#namespaces">命名空间</a></li></ul></li><li><a href="#presence-channels">Presence 频道</a><ul><li><a href="#authorizing-presence-channels">授权 Presence 频道</a></li><li><a href="#joining-presence-channels">加入 Presence 频道</a></li><li><a href="#broadcasting-to-presence-channels">广播到 Presence 频道</a></li></ul></li><li><a href="#client-events">客户端事件</a></li><li><a href="#notifications">消息通知</a></li></ul><p><a name="introduction"></a></p><h2><a href="#introduction">简介</a></h2><p>在现代的 web 应用程序中,WebSockets 被用来实现需要实时、即时更新的接口。当服务器上的数据被更新后,更新信息将通过 WebSocket 连接发送到客户端等待处理。相比于不停地轮询应用程序,WebSocket 是一种更加可靠和高效的选择。</p><p>为了帮助你建立这类应用,Laravel 将通过 WebSocket 连接来使「广播」<a href="/docs/5.4/events">事件</a> 变得更加轻松。广播事件允许你在服务端代码和客户端 JavaScript 应用之间共享相同的事件名。</p><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 在深入了解事件广播之前,请确认你已阅读所有关于 Laravel <a href="/docs/5.4/events">事件和侦听器</a> 的文档。</p></blockquote><p><a name="configuration"></a></p><h3>配置</h3><p>所有关于事件广播的配置都被保存在 <code class=" language-php">config<span class="token operator">/</span>broadcasting<span class="token punctuation">.</span>php</code> 文件中。Laravel 自带了几个广播驱动器:<a href="https://pusher.com">Pusher</a>, <a href="/docs/5.4/redis">Redis</a>, 和一个用于本地开发与调试的 <code class=" language-php">log</code> 驱动器。另外,还有一个 <code class=" language-php"><span class="token keyword">null</span></code> 驱动器可以让你完全关闭广播功能。每一个驱动的示例配置都可以在 <code class=" language-php">config<span class="token operator">/</span>broadcasting<span class="token punctuation">.</span>php</code> 文件中被找到。</p><h4>广播服务提供者</h4><p>在对事件进行广播之前,你必须先注册 <code class=" language-php">App\<span class="token package">Providers<span class="token punctuation">\</span>BroadcastServiceProvider</span></code>。对于一个全新安装的 Laravel 应用程序,你只需在 <code class=" language-php">config<span class="token operator">/</span>app<span class="token punctuation">.</span>php</code> 配置文件的 <code class=" language-php">providers</code> 数组中取消对该提供者的注释即可。该提供者将允许你注册广播授权路由和回调。</p><h4>CSRF 令牌</h4><p><a href="#installing-laravel-echo">Laravel Echo</a> 会需要访问当前会话的 CSRF 令牌。如果可用,Echo 会从 <code class=" language-php">Laravel<span class="token punctuation">.</span>csrfToken</code> JavaScript 对象中获取该令牌。如果你运行了 <code class=" language-php">make<span class="token punctuation">:</span>auth</code> Artisan 命令,该对象会在 <code class=" language-php">resources<span class="token operator">/</span>views<span class="token operator">/</span>layouts<span class="token operator">/</span>app<span class="token punctuation">.</span>blade<span class="token punctuation">.</span>php</code> 布局文件中被定义。如果你未使用该布局文件,可以在应用程序的 <code class=" language-php">head</code> HTML 元素中定义一个 <code class=" language-php">meta</code> 标签:</p><pre class=" language-php"><code class=" language-php"><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>csrf-token<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{{ csrf_token() }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre><p><a name="driver-prerequisites"></a></p><h3>对驱动器的要求</h3><h4>Pusher</h4><p>如果你使用 <a href="https://pusher.com">Pusher</a> 对事件进行广播,请用 Composer 包管理器来安装 Pusher PHP SDK:</p><pre class=" language-php"><code class=" language-php">composer <span class="token keyword">require</span> pusher<span class="token operator">/</span>pusher<span class="token operator">-</span>php<span class="token operator">-</span>server</code></pre><p>然后,你需要在 <code class=" language-php">config<span class="token operator">/</span>broadcasting<span class="token punctuation">.</span>php</code> 配置文件中填写你的 Pusher 证书。该文件中已经包含了一个 Pusher 示例配置,你只需指定 Pusher key、secret 和 application ID 即可。<code class=" language-php">config<span class="token operator">/</span>broadcasting<span class="token punctuation">.</span>php</code> 中的 <code class=" language-php">pusher</code> 配置项同时也允许你指定 Pusher 支持的 <code class=" language-php">options</code>,例如 cluster:</p><pre class=" language-php"><code class=" language-php"><span class="token string">'options'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span>
<span class="token string">'cluster'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">'eu'</span><span class="token punctuation">,</span>
<span class="token string">'encrypted'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span></code></pre><p>当把 Pusher 与 <a href="#installing-laravel-echo">Laravel Echo</a> 一起使用时,你应该在实例化 Echo 对象时指定 broadcaster 为 <code class=" language-php">pusher</code>:</p><pre class=" language-php"><code class=" language-php">import <span class="token keyword">Echo</span> from <span class="token string">"laravel-echo"</span>
window<span class="token punctuation">.</span><span class="token keyword">Echo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Echo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
broadcaster<span class="token punctuation">:</span> <span class="token string">'pusher'</span><span class="token punctuation">,</span>
key<span class="token punctuation">:</span> <span class="token string">'your-pusher-key'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h4>Redis</h4><p>如果你使用 Redis 广播器,请安装 Predis 库:</p><pre class=" language-php"><code class=" language-php">composer <span class="token keyword">require</span> predis<span class="token operator">/</span>predis</code></pre><p>Redis 广播器会使用 Redis 的「生产者/消费者」特性来广播消息;尽管如此,你仍需将它与 WebSocket 服务器一起使用。WebSocket 服务器会从 Redis 接收消息,然后再将消息广播到你的 WebSocket 频道上去。</p><p>当 Redis 广播器发布一个事件时,该事件会被发布到它指定的频道上去,传输的数据是一个采用 JSON 编码的字符串。该字符串包含了事件名、<code class=" language-php">data</code> 数据和生成该事件套接字 ID 的用户(如果可用的话)。</p><h4>Socket.IO</h4><p>如果你想把 Redis 广播器和 Socket.IO 服务器一起使用,你需要将 Socket.IO JavaScript 客户端库文件包含到应用程序的 <code class=" language-php">head</code> HTML 元素中。当 Socket.IO 服务启动的时候,它会自动把 Socket.IO JavaScript 客户端库暴露在一个标准的 URL中。例如,如果你的应用和 Socket.IO 服务器运行在同域名下,你可以像下面这样来访问你的 Socket.IO JavaScript 客户端库:</p><pre class=" language-php"><code class=" language-php"><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>//{{ Request::getHost() }}:6001/socket.io/socket.io.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre><p>接着,你需要在实例化 Echo 时指定 <code class=" language-php">socket<span class="token punctuation">.</span>io</code> 连接器和 <code class=" language-php">host</code> 。</p><pre class=" language-php"><code class=" language-php">import <span class="token keyword">Echo</span> from <span class="token string">"laravel-echo"</span>
window<span class="token punctuation">.</span><span class="token keyword">Echo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Echo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
broadcaster<span class="token punctuation">:</span> <span class="token string">'socket.io'</span><span class="token punctuation">,</span>
host<span class="token punctuation">:</span> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>hostname <span class="token operator">+</span> <span class="token string">':6001'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>最后,你需要运行一个与 Laravel 兼容的 Socket.IO 服务器。Laravel 官方并没有实现 Socket.IO 服务器;不过,可以选择一个由社区驱动维护的项目 <a href="https://github.com/tlaverdure/laravel-echo-server">tlaverdure/laravel-echo-server</a>,目前托管在 GitHub。</p><h4>对队列的要求</h4><p>在开始广播事件之前,你还需要配置和运行 <a href="/docs/5.4/queues">队列侦听器</a>。所有的事件广播都是通过队列任务来完成的,因此应用程序的响应时间不会受到明显影响。</p><p><a name="concept-overview"></a></p><h2><a href="#concept-overview">概念综述</a></h2><p>Laravel 的事件广播允许你使用基于驱动的 WebSockets 将服务端的 Larevel 事件广播到客户端的 JavaScript 应用程序。当前的 Laravel 自带了 <a href="http://pusher.com">Pusher</a> and Redis 驱动。通过使用 <a href="#installing-laravel-echo">Laravel Echo</a> 的 Javascript 包,我们可以很方便地在客户端消费事件。</p><p>事件通过「频道」来广播,这些频道可以被指定为公开的或私有的。任何访客都可以订阅一个不需要认证和授权的公开频道;然而,如果想订阅一个私有频道,那么该用户必须通过认证,并获得该频道的授权。</p><p><a name="using-example-application"></a></p><h3>使用示例程序</h3><p>让我们先用一个电子商务网站作为例子来概览一下事件广播。我们不会讨论如何配置 <a href="http://pusher.com">Pusher</a> 和 <a href="#echo">Laravel Echo</a> 的细节,因为这些会在本文档的其他章节被详细介绍。</p><p>在我们的应用程序中,让我们假设有一个允许用户查看订单配送状态的页面。有一个 <code class=" language-php">ShippingStatusUpdated</code> 事件会在配送状态更新时被触发:</p><pre class=" language-php"><code class=" language-php"><span class="token function">event<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">ShippingStatusUpdated</span><span class="token punctuation">(</span><span class="token variable">$update</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h4><code class=" language-php">ShouldBroadcast</code> 接口</h4><p>当用户在查看自己的订单时,我们不希望他们必须通过刷新页面才能看到状态更新。我们希望一旦有更新时就主动将更新信息广播到客户端。所以,我们必须让 <code class=" language-php">ShippingStatusUpdated</code> 事件实现 <code class=" language-php">ShouldBroadcast</code> 接口。这会让 Laravel 在事件被触发时广播该事件:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Events</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>Channel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>SerializesModels</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>PrivateChannel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>PresenceChannel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>InteractsWithSockets</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>ShouldBroadcast</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">ShippingStatusUpdated</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldBroadcast</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* Information about the shipping status update.
*
* @var string
*/</span>
<span class="token keyword">public</span> <span class="token variable">$update</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p><code class=" language-php">ShouldBroadcast</code> 接口要求事件实现 <code class=" language-php">broadcastOn</code> 方法。该方法负责指定事件被广播到哪些频道。在通过 Artisan 命令生成的事件类中,一个空的 <code class=" language-php">broadcastOn</code> 方法已经被预定义好了,所以我们要做的仅仅是指定频道。我们希望只有订单的创建者能够看到状态更新,所以我们要把该事件广播到与这个订单绑定的私有频道上去:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* Get the channels the event should broadcast on.
*
* @return array
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">broadcastOn<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PrivateChannel</span><span class="token punctuation">(</span><span class="token string">'order.'</span><span class="token punctuation">.</span><span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">update</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">order_id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><h4>频道授权</h4><p>记住,用户只有在被授权后才能监听私有频道。我们可以在 <code class=" language-php">routes<span class="token operator">/</span>channels<span class="token punctuation">.</span>php</code> 文件中定义频道的授权规则。在本例中,我们需要对试图监听私有 <code class=" language-php">order<span class="token number">.1</span></code> 频道的所有用户进行验证,确保只有订单的创建者才能进行监听:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'order.{orderId}'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">,</span> <span class="token variable">$orderId</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span> <span class="token operator">===</span> <span class="token scope">Order<span class="token punctuation">::</span></span><span class="token function">findOrNew<span class="token punctuation">(</span></span><span class="token variable">$orderId</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user_id</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><code class=" language-php">channel</code> 方法接收两个参数:频道名称和一个回调函数,该回调通过返回 <code class=" language-php"><span class="token boolean">true</span></code> 或 <code class=" language-php"><span class="token boolean">false</span></code> 来表示用户是否被授权监听该频道。</p><p>所有的授权回调接收当前被认证的用户作为第一个参数,任何额外的通配符参数作为后续参数。在本例中,我们使用 <code class=" language-php"><span class="token punctuation">{</span>orderId<span class="token punctuation">}</span></code> 占位符来表示频道名称的「ID」部分是通配符。</p><h4>对事件广播进行监听</h4><p>接下来,就只剩下在 JavaScript 应用程序中监听事件了。我们可以使用 Laravel Echo 来实现。首先,使用 <code class=" language-php"><span class="token keyword">private</span></code> 方法来订阅私有频道。然后,使用 <code class=" language-php">listen</code> 方法来监听 <code class=" language-php">ShippingStatusUpdated</code> 事件。默认情况下,事件的所有公有属性会被包括在广播事件中:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token keyword">private</span><span class="token punctuation">(</span>`order<span class="token punctuation">.</span>$<span class="token punctuation">{</span>orderId<span class="token punctuation">}</span>`<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'ShippingStatusUpdated'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>e<span class="token punctuation">.</span>update<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="defining-broadcast-events"></a></p><h2><a href="#defining-broadcast-events">定义广播事件</a></h2><p>要告知 Laravel 一个给定的事件是广播类型,只需在事件类中实现 <code class=" language-php">Illuminate\<span class="token package">Contracts<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>ShouldBroadcast</span></code> 接口即可。该接口已经被导入到所有由框架生成的事件类中,所以你可以很方便地将它添加到你自己的事件中。</p><p><code class=" language-php">ShouldBroadcast</code> 接口要求你实现一个方法:<code class=" language-php">broadcastOn</code>。<code class=" language-php">broadcastOn</code> 方法返回一个频道或一个频道数组,事件会被广播到这些频道。频道必须是 <code class=" language-php">Channel</code>、<code class=" language-php">PrivateChannel</code> 或 <code class=" language-php">PresenceChannel</code> 的实例。<code class=" language-php">Channel</code> 实例表示任何用户都可以订阅的公开频道,而 <code class=" language-php">PrivateChannels</code> 和 <code class=" language-php">PresenceChannels</code> 则表示需要 <a href="#authorizing-channels">频道授权</a> 的私有频道:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Events</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>Channel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>SerializesModels</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>PrivateChannel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>PresenceChannel</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>InteractsWithSockets</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Broadcasting<span class="token punctuation">\</span>ShouldBroadcast</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">ServerCreated</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldBroadcast</span>
<span class="token punctuation">{</span>
<span class="token keyword">use</span> <span class="token package">SerializesModels</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token variable">$user</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 创建一个新的事件实例
*
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span>User <span class="token variable">$user</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user</span> <span class="token operator">=</span> <span class="token variable">$user</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 指定事件在哪些频道上进行广播
*
* @return Channel|array
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">broadcastOn<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PrivateChannel</span><span class="token punctuation">(</span><span class="token string">'user.'</span><span class="token punctuation">.</span><span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>然后,你只需要像你平时那样 <a href="/docs/5.4/events">触发事件</a>。一旦事件被触发,一个 <a href="/docs/5.4/queues">队列任务</a> 会自动广播事件到你指定的广播驱动器上。</p><p><a name="broadcast-name"></a></p><h3>广播名称</h3><p>Laravel 默认会使用事件的类名作为广播名称来广播事件。不过,你也可以在事件类中通过定义一个 <code class=" language-php">broadcastAs</code> 方法来自定义广播名称:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 事件的广播名称。
*
* @return string
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">broadcastAs<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">'server.created'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p><a name="broadcast-data"></a></p><h3>广播数据</h3><p>当一个事件被广播时,它所有的 <code class=" language-php"><span class="token keyword">public</span></code> 属性会自动被序列化为广播数据,这允许你在你的 JavaScript 应用中访问事件的公有数据。因此,举个例子,如果你的事件有一个公有的 <code class=" language-php"><span class="token variable">$user</span></code> 属性,它包含了一个 Elouqent 模型,那么事件的广播数据会是:</p><pre class=" language-php"><code class=" language-php"><span class="token punctuation">{</span>
<span class="token string">"user"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"Patrick Stewart"</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>然而,如果你想更细粒度地控制你的广播数据,你可以添加一个 <code class=" language-php">broadcastWith</code> 方法到你的事件中。这个方法应该返回一个数组,该数组中的数据会被添加到广播数据中:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 指定广播数据
*
* @return array
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">broadcastWith<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token string">'id'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p><a name="broadcast-queue"></a></p><h3>广播队列</h3><p>默认情况下,每一个广播事件都被添加到默认的队列上,默认的队列连接在 <code class=" language-php">queue<span class="token punctuation">.</span>php</code> 配置文件中指定。你可以通过在事件类中定义一个 <code class=" language-php">broadcastQueue</code> 属性来自定义广播器使用的队列。该属性用于指定广播使用的队列名称:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 指定事件被放置在哪个队列上
*
* @var string
*/</span>
<span class="token keyword">public</span> <span class="token variable">$broadcastQueue</span> <span class="token operator">=</span> <span class="token string">'your-queue-name'</span><span class="token punctuation">;</span></code></pre><p><a name="authorizing-channels"></a></p><h2><a href="#authorizing-channels">频道授权</a></h2><p>对于私有频道,用户只有被授权后才能监听。实现过程是用户向你的 Laravel 应用程序发起一个携带频道名称的 HTTP 请求,你的应用程序判断该用户是否能够监听该频道。在使用 <a href="#installing-laravel-echo">Laravel Echo</a> 时,上述 HTTP 请求会被自动发送;尽管如此,你仍然需要定义适当的路由来响应这些请求。</p><p><a name="defining-authorization-routes"></a></p><h3>定义授权路由</h3><p>幸运的是,我们可以在 Laravel 里很容易地定义路由来响应频道授权请求。在 <code class=" language-php">BroadcastServiceProvider</code> 中,你会看到一个对 <code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span>routes</code> 方法的调用。该方法会注册 <code class=" language-php"><span class="token operator">/</span>broadcasting<span class="token operator">/</span>auth</code> 路由来处理授权请求:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">routes<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span>routes</code> 方法会自动把它的路由放进 <code class=" language-php">web</code> 中间件组中;另外,如果你想对一些属性自定义,可以向该方法传递一个包含路由属性的数组:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">routes<span class="token punctuation">(</span></span><span class="token variable">$attributes</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="defining-authorization-callbacks"></a></p><h3>定义授权回调</h3><p>接下来,我们需要定义真正用于处理频道授权的逻辑。这是在 <code class=" language-php">routes<span class="token operator">/</span>channels<span class="token punctuation">.</span>php</code> 文件中完成。在该文件中,你可以用 <code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span>channel</code> 方法来注册频道授权回调函数:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'order.{orderId}'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">,</span> <span class="token variable">$orderId</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span> <span class="token operator">===</span> <span class="token scope">Order<span class="token punctuation">::</span></span><span class="token function">findOrNew<span class="token punctuation">(</span></span><span class="token variable">$orderId</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user_id</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><code class=" language-php">channel</code> 方法接收两个参数:频道名称和一个回调函数,该回调通过返回 <code class=" language-php"><span class="token boolean">true</span></code> 或 <code class=" language-php"><span class="token boolean">false</span></code> 来表示用户是否被授权监听该频道。</p><p>所有的授权回调接收当前被认证的用户作为第一个参数,任何额外的通配符参数作为后续参数。在本例中,我们使用 <code class=" language-php"><span class="token punctuation">{</span>orderId<span class="token punctuation">}</span></code> 占位符来表示频道名称的「ID」部分是通配符。</p><h4>授权回调模型绑定</h4><p>就像 HTTP 路由一样,频道路由也可以利用显式或隐式<a href="/docs/5.4/routing#route-model-binding">路由模型绑定</a>。例如,相比于接收一个字符串或数字类型的 order ID,你也可以请求一个真正的 <code class=" language-php">Order</code> 模型实例:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Order</span><span class="token punctuation">;</span>
<span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'order.{order}'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">,</span> Order <span class="token variable">$order</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span> <span class="token operator">===</span> <span class="token variable">$order</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user_id</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="broadcasting-events"></a></p><h2><a href="#broadcasting-events">对事件进行广播</a></h2><p>一旦你已经定义好了一个事件并实现了 <code class=" language-php">ShouldBroadcast</code> 接口,剩下的就是使用 <code class=" language-php">event</code> 函数来触发该事件。事件分发器会识别出实现了 <code class=" language-php">ShouldBroadcast</code> 接口的事件并将它们加入到队列进行广播:</p><pre class=" language-php"><code class=" language-php"><span class="token function">event<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">ShippingStatusUpdated</span><span class="token punctuation">(</span><span class="token variable">$update</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="only-to-others"></a></p><h3>只广播给他人</h3><p>当创建一个使用到事件广播的应用程序时,你可以用 <code class=" language-php">broadcast</code> 函数来替代 <code class=" language-php">event</code> 函数。和 <code class=" language-php">event</code> 函数一样,<code class=" language-php">broadcast</code> 函数将事件分发到服务端侦听器:</p><pre class=" language-php"><code class=" language-php"><span class="token function">broadcast<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">ShippingStatusUpdated</span><span class="token punctuation">(</span><span class="token variable">$update</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>不同的是 <code class=" language-php">broadcast</code> 函数有一个 <code class=" language-php">toOthers</code> 方法允许你将当前用户从广播接收者中排除:</p><pre class=" language-php"><code class=" language-php"><span class="token function">broadcast<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">ShippingStatusUpdated</span><span class="token punctuation">(</span><span class="token variable">$update</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">toOthers<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>为了更好地理解什么时候使用 <code class=" language-php">toOthers</code> 方法,让我们假设有一个任务列表的应用程序,用户可以通过输入任务名称来新建任务。为了新建任务,你的应用程序需要发起一个请求到 <code class=" language-php"><span class="token operator">/</span>task</code> 路由,该路由在接收到请求并成功创建新任务后会触发一个任务被新建的事件广播,并返回新任务的 JSON 响应。当你的 JavaScript 应用程序接收到来自该路由的响应时,它会直接将新任务插入到任务列表,就像这样:</p><pre class=" language-php"><code class=" language-php">axios<span class="token punctuation">.</span><span class="token function">post<span class="token punctuation">(</span></span><span class="token string">'/task'</span><span class="token punctuation">,</span> task<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then<span class="token punctuation">(</span></span><span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
this<span class="token punctuation">.</span>tasks<span class="token punctuation">.</span><span class="token function">push<span class="token punctuation">(</span></span>response<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>然而,别忘了我们还将接收到一个事件广播。如果你的 JavaScript 应用程序同时监听该事件以便添加新任务到任务列表,你将会在你的列表中看到重复的任务:一份来自路由响应,另一份来自广播。</p><p>你可以通过使用 <code class=" language-php">toOthers</code> 方法让广播器只广播事件到其他用户来解决该问题。</p><h4>配置</h4><p>当你实例化 Laravel Echo 实例时,一个套接字 ID 会被指定到该连接。如果你使用 <a href="https://vuejs.org">Vue</a> 和 <a href="https://github.com/mzabriskie/axios">Axios</a>,套接字 ID 会自动被添加到每一个请求的 <code class=" language-php">X<span class="token operator">-</span>Socket<span class="token operator">-</span><span class="token constant">ID</span></code> 头中。然后,当你调用 <code class=" language-php">toOthers</code> 方法时,Laravel 会从头中提取出套接字 ID,并告诉广播器不要广播任何消息到该套接字 ID 的连接上。</p><p>如果你没有使用 Vue 和 Axios,则需要手动配置 JavaScript 应用程序来发送 <code class=" language-php">X<span class="token operator">-</span>Socket<span class="token operator">-</span><span class="token constant">ID</span></code> 头。你可以用 <code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span>socketId</code> 方法来获取套接字 ID:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">var</span> socketId <span class="token operator">=</span> <span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">socketId<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="receiving-broadcasts"></a></p><h2><a href="#receiving-broadcasts">接收广播</a></h2><p><a name="installing-laravel-echo"></a></p><h3>安装 Laravel Echo</h3><p>Laravel Echo 是一个 JavaScript 库,它使得订阅频道和监听由 Laravel 广播的事件变得非常容易。你可以通过 NPM 包管理器来安装 Echo。在本例中,因为我们将使用 Pusher 广播器,请安装 <code class=" language-php">pusher<span class="token operator">-</span>js</code> 包:</p><pre class=" language-php"><code class=" language-php">npm install <span class="token operator">--</span>save laravel<span class="token operator">-</span><span class="token keyword">echo</span> pusher<span class="token operator">-</span>js</code></pre><p>一旦 Echo 被安装好,你就可以在你应用程序的 JavaScript 中创建一个全新的 Echo 实例。做这件事的一个理想地方是在 <code class=" language-php">resources<span class="token operator">/</span>assets<span class="token operator">/</span>js<span class="token operator">/</span>bootstrap<span class="token punctuation">.</span>js</code> 文件的底部,Laravel 框架自带了该文件:</p><pre class=" language-php"><code class=" language-php">import <span class="token keyword">Echo</span> from <span class="token string">"laravel-echo"</span>
window<span class="token punctuation">.</span><span class="token keyword">Echo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Echo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
broadcaster<span class="token punctuation">:</span> <span class="token string">'pusher'</span><span class="token punctuation">,</span>
key<span class="token punctuation">:</span> <span class="token string">'your-pusher-key'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>当你使用 <code class=" language-php">pusher</code> 连接器来创建一个 Echo 实例的时候,你需要指定 <code class=" language-php">cluster</code> 以及指定连接是否需要加密:</p><pre class=" language-php"><code class=" language-php">window<span class="token punctuation">.</span><span class="token keyword">Echo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Echo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
broadcaster<span class="token punctuation">:</span> <span class="token string">'pusher'</span><span class="token punctuation">,</span>
key<span class="token punctuation">:</span> <span class="token string">'your-pusher-key'</span><span class="token punctuation">,</span>
cluster<span class="token punctuation">:</span> <span class="token string">'eu'</span><span class="token punctuation">,</span>
encrypted<span class="token punctuation">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="listening-for-events"></a></p><h3>对事件进行监听</h3><p>一旦你安装好并实例化了 Echo,你就可以开始监听事件广播了。首先,使用 <code class=" language-php">chennel</code> 方法来获取一个频道实例,然后调用 <code class=" language-php">listen</code> 方法来监听指定的事件:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'orders'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'OrderShipped'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>e<span class="token punctuation">.</span>order<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>如果你想监听私有频道上的事件,请使用 <code class=" language-php"><span class="token keyword">private</span></code> 方法。你可以通过链式调用 <code class=" language-php">listen</code> 方法来监听一个频道上的多个事件:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token keyword">private</span><span class="token punctuation">(</span><span class="token string">'orders'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="leaving-a-channel"></a></p><h3>退出频道</h3><p>如果想退出频道,你需要在你的 Echo 实例上调用 <code class=" language-php">leave</code> 方法:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">leave<span class="token punctuation">(</span></span><span class="token string">'orders'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="namespaces"></a></p><h3>命名空间</h3><p>你可能注意到了在上面的例子中我们没有为事件类指定完整的命名空间。这是因为 Echo 会自动认为事件在 <code class=" language-php">App\<span class="token package">Events</span></code> 命名空间下。你可以在实例化 Echo 的时候传递一个 <code class=" language-php"><span class="token keyword">namespace</span></code> 配置选项来指定根命名空间:</p><pre class=" language-php"><code class=" language-php">window<span class="token punctuation">.</span><span class="token keyword">Echo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Echo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
broadcaster<span class="token punctuation">:</span> <span class="token string">'pusher'</span><span class="token punctuation">,</span>
key<span class="token punctuation">:</span> <span class="token string">'your-pusher-key'</span><span class="token punctuation">,</span>
<span class="token keyword">namespace</span><span class="token punctuation">:</span> <span class="token string">'App.Other.Namespace'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>另外,你也可以在使用 Echo 订阅事件的时候为事件类加上 <code class=" language-php"><span class="token punctuation">.</span></code> 前缀。这时需要填写完全限定名称的类名:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'orders'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'.Namespace.Event.Class'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="presence-channels"></a></p><h2><a href="#presence-channels">Presence 频道</a></h2><p>Presence 频道是在私有频道的安全性基础上,额外暴露出有哪些人订阅了该频道。这使得它可以很容易地建立强大的、协同的应用,如当有一个用户在浏览页面时,通知其他正在浏览相同页面的用户。</p><p><a name="joining-a-presence-channel"></a></p><h3>授权 Presence 频道</h3><p>Presence 频道也是私有频道;因此,用户必须 <a href="#authorizing-channels">获得授权后才能访问他们</a>。与私有频道不同的是,在给 presence 频道定义授权回调函数时,如果一个用户已经加入了该频道,那么不应该返回 <code class=" language-php"><span class="token boolean">true</span></code>,而应该返回一个关于该用户信息的数组。</p><p>由授权回调函数返回的数据能够在你的 JavaScript 应用程序中被 presence 频道事件侦听器所使用。如果用户没有获得加入该 presence 频道的授权,那么你应该返回 <code class=" language-php"><span class="token boolean">false</span></code> 或 <code class=" language-php"><span class="token keyword">null</span></code>:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Broadcast<span class="token punctuation">::</span></span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'chat.*'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">,</span> <span class="token variable">$roomId</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">canJoinRoom<span class="token punctuation">(</span></span><span class="token variable">$roomId</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token string">'id'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span><span class="token punctuation">,</span> <span class="token string">'name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">name</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="joining-presence-channels"></a></p><h3>加入 Presence 频道</h3><p>你可以用 Echo 的 <code class=" language-php">join</code> 方法来加入 presence 频道。<code class=" language-php">join</code> 方法会返回一个实现了 <code class=" language-php">PresenceChannel</code> 的对象,它通过暴露 <code class=" language-php">listen</code> 方法,允许你订阅 <code class=" language-php">here</code>、<code class=" language-php">joining</code> 和 <code class=" language-php">leaving</code> 事件。</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">join<span class="token punctuation">(</span></span>`chat<span class="token punctuation">.</span>$<span class="token punctuation">{</span>roomId<span class="token punctuation">}</span>`<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">here<span class="token punctuation">(</span></span><span class="token punctuation">(</span>users<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">joining<span class="token punctuation">(</span></span><span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>user<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">leaving<span class="token punctuation">(</span></span><span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>user<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><code class=" language-php">here</code> 回调函数会在你成功加入频道后被立即执行,它接收一个包含用户信息的数组,用来告知当前订阅在该频道上的其他用户。<code class=" language-php">joining</code> 方法会在其他新用户加入到频道时被执行,<code class=" language-php">leaving</code> 会在其他用户退出频道时被执行。</p><p><a name="broadcasting-to-presence-channels"></a></p><h3>广播到 Presence 频道</h3><p>Presence 频道可以像公开和私有频道一样接收事件。使用一个聊天室的例子,我们要把 <code class=" language-php">NewMessage</code> 事件广播到聊天室的 presence 频道。要实现它,我们将从事件的 <code class=" language-php">broadcastOn</code> 方法中返回一个 <code class=" language-php">PresenceChannel</code> 实例:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 指定事件在哪些频道上进行广播
*
* @return Channel|array
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">broadcastOn<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PresenceChannel</span><span class="token punctuation">(</span><span class="token string">'room.'</span><span class="token punctuation">.</span><span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">message</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">room_id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p>和公开或私有事件一样,presence 频道事件也能使用 <code class=" language-php">broadcast</code> 函数来广播。同样的,你还能用 <code class=" language-php">toOthers</code> 方法将当前用户从广播接收者中排除:</p><pre class=" language-php"><code class=" language-php"><span class="token function">broadcast<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">NewMessage</span><span class="token punctuation">(</span><span class="token variable">$message</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">broadcast<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">NewMessage</span><span class="token punctuation">(</span><span class="token variable">$message</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">toOthers<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>你可以通过 Echo 的 <code class=" language-php">listen</code> 方法来监听 join 事件:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">join<span class="token punctuation">(</span></span>`chat<span class="token punctuation">.</span>$<span class="token punctuation">{</span>roomId<span class="token punctuation">}</span>`<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">here<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">joining<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">leaving<span class="token punctuation">(</span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'NewMessage'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="client-events"></a></p><h2><a href="#client-events">客户端事件</a></h2><p>有时候你可能希望广播一个事件给其他已经连接的客户端,但并不需要通知你的 Laravel 程序。试想一下当你想提醒用户,另外一个用户正在输入中的时候,显而易见客服端事件对于「输入中」之类的通知就显得非常有用了。你可以使用 Echo 的 <code class=" language-php">whisper</code> 方法来广播客户端事件:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'chat'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">whisper<span class="token punctuation">(</span></span><span class="token string">'typing'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
name<span class="token punctuation">:</span> this<span class="token punctuation">.</span>user<span class="token punctuation">.</span>name
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>你可以使用 <code class=" language-php">listenForWhisper</code> 方法来监听客户端事件:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token function">channel<span class="token punctuation">(</span></span><span class="token string">'chat'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">listenForWhisper<span class="token punctuation">(</span></span><span class="token string">'typing'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>e<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="notifications"></a></p><h2><a href="#notifications">消息通知</a></h2><p>将 <a href="/docs/5.4/notifications">消息通知</a> 和事件广播一同使用,你的 JavaScript 应用程序可以在不刷新页面的情况下接收新的消息通知。首先,请先阅读关于如何使用 <a href="/docs/5.4/notifications#broadcast-notifications">广播消息通知频道</a> 的文档。</p><p>一旦你将一个消息通知配置为使用广播频道,你需要使用 Echo 的 <code class=" language-php">notification</code> 方法来监听广播事件。谨记,频道名称应该和接收消息通知的实体类名相匹配:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">Echo</span><span class="token punctuation">.</span><span class="token keyword">private</span><span class="token punctuation">(</span>`App<span class="token punctuation">.</span>User<span class="token punctuation">.</span>$<span class="token punctuation">{</span>userId<span class="token punctuation">}</span>`<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">notification<span class="token punctuation">(</span></span><span class="token punctuation">(</span>notification<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>notification<span class="token punctuation">.</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>在本例中,所有通过 <code class=" language-php">broadcast</code> 频道发送到 <code class=" language-php">App\<span class="token package">User</span></code> 实例的消息通知都会被该回调接收到。一个针对 <code class=" language-php">App<span class="token punctuation">.</span>User<span class="token punctuation">.</span><span class="token punctuation">{</span>id<span class="token punctuation">}</span></code> 频道的授权回调函数已经被包含在 Laravel 的 <code class=" language-php">BroadcastServiceProvider</code> 中了。</p></article>
- 入门指南
- 安装
- 配置信息
- 文件夹结构
- 请求周期
- 开发环境部署
- Valet
- Homestead
- 核心概念
- 服务提供者
- Facades
- Contracts
- 服务容器
- HTTP 层
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- Session
- 表单验证
- 前端
- Blade 模板
- 本地化
- 前端指南
- 编辑资源 Mix
- 安全
- API 认证
- 用户认证
- 用户授权
- 加密解密
- 哈希
- 重置密码
- 数据库
- 快速入门
- 查询构造器
- 分页
- 数据库迁移
- Redis
- 数据填充
- Eloquent ORM
- Eloquent ORM快速入门
- 模型关联
- Eloquent 集合
- 修改器
- 序列化
- 综合话题
- Artisan 命令行
- 广播系统
- 缓存系统
- 集合
- 错误与日志
- 事件系统
- 文件存储
- 辅助函数
- 邮件发送
- 消息通知
- 扩展包开发
- 队列
- 任务调度