特别为“ Framework Laravel”课程的学生准备了这篇文章的翻译。
现代的Web用户希望了解应用程序中发生的所有事情。您可能不想成为一个甚至没有通知下拉列表的网站,现在不仅可以在所有社交网站上找到这些通知,而且如今在几乎所有地方都可以找到。幸运的是,使用Laravel和Pusher,此功能的实现非常简单。实时通知
为了提供积极的用户体验,应实时显示通知。一种方法是定期向服务器发送AJAX请求并接收最新的通知(如果有)。最好的方法是使用WebSocket的功能并在发送通知时接收通知。这正是我们将在本文中实现的。推杆
Pusher是一种Web服务,用于通过WebSocket将实时双向功能集成到Web和移动应用程序中。它有一个非常简单的API,但是我们将通过Laravel Broadcasting和Laravel Echo使它使用起来更加容易。在本文中,我们将向现有博客添加实时通知。项目
初始化
首先,我们克隆一个简单的Laravel博客:git clone https:
然后,我们将创建MySQL数据库并设置环境变量,以使应用程序可以访问数据库。让我们拷贝env.example
中.env
并更新与数据库相关的变量。cp .env.example .envDB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
现在,使用以下命令安装项目依赖项composer install
并运行migration and populate命令以使用一些数据填充数据库:php artisan migrate --seed
如果运行该应用程序并转到/posts
,则可以看到生成的帖子列表。检查应用程序,注册用户并创建一些消息。这是一个非常简单的应用程序,但是非常适合演示。订阅用户
我们希望给用户提供彼此订阅的机会,因此我们必须在用户之间建立多对多关系以实现这一点。让我们创建一个将用户链接到用户的数据透视表。让我们进行新的迁移followers
:php artisan make:migration create_followers_table --create=followers
我们需要在此迁移中添加几个字段:user_id
代表订阅的用户,以及follows_id
代表他们订阅的用户的字段。更新迁移,如下所示:public function up()
{
Schema::create('followers', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->index();
$table->integer('follows_id')->index();
$table->timestamps();
});
}
现在让我们继续创建表:php artisan migrate
让我们向模型添加关系方法User
。
class extends Authenticatable
{
public function followers()
{
return $this->belongsToMany(self::class, 'followers', 'follows_id', 'user_id')
->withTimestamps();
}
public function follows()
{
return $this->belongsToMany(self::class, 'followers', 'user_id', 'follows_id')
->withTimestamps();
}
}
现在,该模型User
具有必要的关系,它followers
返回用户的所有订户,并follows
返回用户已预订的所有订户。我们将需要一些辅助功能,这些功能允许用户订阅其他用户- follow
,并检查该用户是否订阅了特定用户- isFollowing
。
class extends Authenticatable
{
public function follow($userId)
{
$this->follows()->attach($userId);
return $this;
}
public function unfollow($userId)
{
$this->follows()->detach($userId);
return $this;
}
public function isFollowing($userId)
{
return (boolean) $this->follows()->where('follows_id', $userId)->first(['id']);
}
}
精细。准备好模型后,您需要列出用户列表。用户列表
让我们从确定必要的路线开始。/...
Route::group(['middleware' => 'auth'], function () {
Route::get('users', 'UsersController@index')->name('users');
Route::post('users/{user}/follow', 'UsersController@follow')->name('follow');
Route::delete('users/{user}/unfollow', 'UsersController@unfollow')->name('unfollow');
});
然后是时候为用户创建一个新的控制器了:php artisan make:controller UsersController
我们将添加一个方法index
:
use App\User;
class UsersController extends Controller
{
public function index()
{
$users = User::where('id', '!=', auth()->user()->id)->get();
return view('users.index', compact('users'));
}
}
该方法需要介绍。让我们创建一个视图users.index
,并在其中添加以下标记:@extends('layouts.app')
@section('content')
<div class="container">
<div class="col-sm-offset-2 col-sm-8">
<!-- Following -->
<div class="panel panel-default">
<div class="panel-heading">
All Users
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<thead>
<th>User</th>
<th> </th>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td clphpass="table-text"><div>{{ $user->name }}</div></td>
@if (auth()->user()->isFollowing($user->id))
<td>
<form action="{{route('unfollow', ['id' => $user->id])}}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" id="delete-follow-{{ $user->id }}" class="btn btn-danger">
<i class="fa fa-btn fa-trash"></i>Unfollow
</button>
</form>
</td>
@else
<td>
<form action="{{route('follow', ['id' => $user->id])}}" method="POST">
{{ csrf_field() }}
<button type="submit" id="follow-user-{{ $user->id }}" class="btn btn-success">
<i class="fa fa-btn fa-user"></i>Follow
</button>
</form>
</td>
@endif
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
现在,您可以访问该页面/users
以查看用户列表。关注和取消关注
在UsersController
缺少的方法follow
和方法上unfollow
。让我们实现它们以完成这一部分。
class UsersController extends Controller
{
public function follow(User $user)
{
$follower = auth()->user();
if ($follower->id == $user->id) {
return back()->withError("You can't follow yourself");
}
if(!$follower->isFollowing($user->id)) {
$follower->follow($user->id);
$user->notify(new UserFollowed($follower));
return back()->withSuccess("You are now friends with {$user->name}");
}
return back()->withError("You are already following {$user->name}");
}
public function unfollow(User $user)
{
$follower = auth()->user();
if($follower->isFollowing($user->id)) {
$follower->unfollow($user->id);
return back()->withSuccess("You are no longer friends with {$user->name}");
}
return back()->withError("You are not following {$user->name}");
}
}
我们已经完成了此功能。现在,我们可以在页面上订阅用户并取消订阅/users
。通知事项
Laravel提供了一个用于通过多个渠道发送通知的API。可以使用Notification类发送电子邮件,SMS,Web通知和任何其他类型的通知。我们将有两种类型的通知:- 订阅通知:当另一个用户订阅时发送给用户
- 帖子通知:发布新帖子时发送给该用户的订阅者。
订阅通知
使用工匠命令,我们可以为通知生成迁移:php artisan notifications:table
让我们进行迁移并创建此表。php artisan migrate
我们将从订阅通知开始。让我们运行以下命令来创建通知类:php artisan make:notification UserFollowed
然后,我们修改刚刚创建的通知类文件:class UserFollowed extends Notification implements ShouldQueue
{
use Queueable;
protected $follower;
public function __construct(User $follower)
{
$this->follower = $follower;
}
public function via($notifiable)
{
return ['database'];
}
public function toDatabase($notifiable)
{
return [
'follower_id' => $this->follower->id,
'follower_name' => $this->follower->name,
];
}
}
使用这几行代码,我们已经可以实现很多目标。首先,我们要求在$follower
生成此通知时实现实例。使用该方法via
,我们告诉Laravel通过通道发送此通知database
。当Laravel遇到此问题时,它将在通知表中创建一个新条目。user_id
并type
通知被自动设置,再加上我们能够扩大与其他数据的通知。这就是它的用途toDatabase
。返回的数组将添加到data
通知字段。最后,感谢实施ShouldQueue
,Laravel会自动将此通知放置在后台运行的队列中,这将加快响应速度。这是有道理的,因为稍后使用Pusher时将添加HTTP调用。让我们实现一个用户订阅通知。
use App\Notifications\UserFollowed;
class UsersController extends Controller
{
public function follow(User $user)
{
$follower = auth()->user();
if ( ! $follower->isFollowing($user->id)) {
$follower->follow($user->id);
$user->notify(new UserFollowed($follower));
return back()->withSuccess("You are now friends with {$user->name}");
}
return back()->withSuccess("You are already following {$user->name}");
}
}
我们可以为User模型调用notify方法,因为它已经使用了Notifiable特征。您要通知的任何模型都应使用它来访问notify方法。我们将通知标记为已读通知将包含一些信息和资源链接。例如:当用户收到有关新消息的通知时,该通知应包含内容丰富的文本,在按下该消息时将用户重定向到该消息并标记为已读。我们将创建一个图层,该图层将检查请求中是否存在事件?read=notification_id
并将其标记为已读。让我们使用以下命令创建该层:php artisan make:middleware MarkNotificationAsRead
然后,将这段代码放在handle
interlayer 方法中:class MarkNotificationAsRead
{
public function handle($request, Closure $next)
{
if($request->has('read')) {
$notification = $request->user()->notifications()->where('id', $request->read)->first();
if($notification) {
$notification->markAsRead();
}
}
return $next($request);
}
}
为了针对每个请求执行我们的图层,我们将其添加到中$middlewareGroups
。
class Kernel extends HttpKernel
{
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\MarkNotificationAsRead::class,
],
];
}
之后,让我们显示通知。显示通知
我们需要使用AJAX显示通知列表,然后使用Pusher实时更新通知列表。首先,让我们notifications
为控制器添加一个方法:
class UsersController extends Controller
{
public function notifications()
{
return auth()->user()->unreadNotifications()->limit(5)->get()->toArray();
}
}
此代码将返回最后5条未读的通知。我们只需要添加一条路由即可访问它。
Route::group([ 'middleware' => 'auth' ], function () {
Route::get('/notifications', 'UsersController@notifications');
});
现在,在标题中添加一个用于通知的下拉列表。<head>
<!--
<!-- Scripts -->
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
<!-- id JavaScript -->
@if(!auth()->guest())
<script>
window.Laravel.userId = <?php echo auth()->user()->id; ?>
</script>
@endif
</head>
<body>
<!--
@if (Auth::guest())
<li><a href="{{ url('/login') }}">Login</a></li>
<li><a href="{{ url('/register') }}">Register</a></li>
@else
<!--
<li class="dropdown">
<a class="dropdown-toggle" id="notifications" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span class="glyphicon glyphicon-user"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="notificationsMenu" id="notificationsMenu">
<li class="dropdown-header">No notifications</li>
</ul>
</li>
<!-- // ... // -->
我们还向window.Laravel.userId
脚本添加了全局变量以获取当前用户ID。JavaScript和SASS
我们将使用Laravel Mix来编译JavaScript和SASS。首先,我们需要安装npm软件包。npm install
现在让我们将此代码添加到app.js
:window._ = require('lodash');
window.$ = window.jQuery = require('jquery');
require('bootstrap-sass');
var notifications = [];
const NOTIFICATION_TYPES = {
follow: 'App\\Notifications\\UserFollowed'
};
这只是初始化。我们将使用通知来存储所有通知对象,无论它们是通过AJAX还是Pusher检索的。您可能已经猜到了,它NOTIFICATION_TYPES
包含通知类型。现在,让我们通过AJAX获得(“ GET”)通知。
$(document).ready(function() {
if(Laravel.userId) {
$.get('/notifications', function (data) {
addNotifications(data, "#notifications");
});
}
});
function addNotifications(newNotifications, target) {
notifications = _.concat(notifications, newNotifications);
notifications.slice(0, 5);
showNotifications(notifications, target);
}
由于有了此代码,我们从API收到了最新的通知,并将它们放在了一个下拉列表中。在内部,addNotifications
我们使用Lodash将现有通知与新通知结合在一起,并只取最后5条,这将显示出来。我们还需要一些其他功能来完成这项工作。
function showNotifications(notifications, target) {
if(notifications.length) {
var htmlElements = notifications.map(function (notification) {
return makeNotification(notification);
});
$(target + 'Menu').html(htmlElements.join(''));
$(target).addClass('has-notifications')
} else {
$(target + 'Menu').html('<li class="dropdown-header">No notifications</li>');
$(target).removeClass('has-notifications');
}
}
此功能创建一行所有通知,并将其放置在下拉列表中。如果未收到任何通知,则仅显示“无通知”。它还向下拉按钮添加了一个类,该类仅在有通知时更改颜色。有点像Github通知。最后,一些帮助程序功能用于创建通知字符串。
function makeNotification(notification) {
var to = routeNotification(notification);
var notificationText = makeNotificationText(notification);
return '<li><a href="' + to + '">' + notificationText + '</a></li>';
}
function routeNotification(notification) {
var to = '?read=' + notification.id;
if(notification.type === NOTIFICATION_TYPES.follow) {
to = 'users' + to;
}
return '/' + to;
}
function makeNotificationText(notification) {
var text = '';
if(notification.type === NOTIFICATION_TYPES.follow) {
const name = notification.data.follower_name;
text += '<strong>' + name + '</strong> followed you';
}
return text;
}
现在,我们将其添加到我们的文件中app.scss
:
color:
}
让我们编译资产:npm run dev
现在,如果您尝试订阅用户,则该用户将收到通知。当他单击它时,他将被重定向到/users
,通知本身也消失了。新帖通知
当用户发布新帖子时,我们将通知订阅者。让我们从创建一个通知类开始。php artisan make:notification NewPost
让我们修改生成的类,如下所示:
use App\Post;
use App\User;
class NewArticle extends Notification implements ShouldQueue
{
protected $following;
protected $post;
public function __construct(User $following, Post $post)
{
$this->following = $following;
$this->post = $post;
}
public function via($notifiable)
{
return ['database'];
}
public function toDatabase($notifiable)
{
return [
'following_id' => $this->following->id,
'following_name' => $this->following->name,
'post_id' => $this->post->id,
];
}
}
接下来,我们需要发送通知。有几种方法可以做到这一点。我喜欢使用雄辩的观察者。让我们为Post创建一个观察者,并监听其事件。我们将创建一个新类:app/Observers/PostObserver.php
namespace App\Observers;
use App\Notifications\NewPost;
use App\Post;
class PostObserver
{
public function created(Post $post)
{
$user = $post->user;
foreach ($user->followers as $follower) {
$follower->notify(new NewPost($user, $post));
}
}
}
然后在AppServiceProvider
以下位置注册观察者:
use App\Observers\PostObserver;
use App\Post;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Post::observe(PostObserver::class);
}
}
现在我们只需要格式化消息以在JS中显示:
const NOTIFICATION_TYPES = {
follow: 'App\\Notifications\\UserFollowed',
newPost: 'App\\Notifications\\NewPost'
};
function routeNotification(notification) {
var to = `?read=${notification.id}`;
if(notification.type === NOTIFICATION_TYPES.follow) {
to = 'users' + to;
} else if(notification.type === NOTIFICATION_TYPES.newPost) {
const postId = notification.data.post_id;
to = `posts/${postId}` + to;
}
return '/' + to;
}
function makeNotificationText(notification) {
var text = '';
if(notification.type === NOTIFICATION_TYPES.follow) {
const name = notification.data.follower_name;
text += `<strong>${name}</strong> followed you`;
} else if(notification.type === NOTIFICATION_TYPES.newPost) {
const name = notification.data.following_name;
text += `<strong>${name}</strong> published a post`;
}
return text;
}
瞧!通知用户订阅和新帖子!自己尝试!使用Pusher实时退出
是时候使用Pusher通过Web套接字接收实时通知了。在pusher.com上注册免费的Pusher帐户并创建一个新应用。...
BROADCAST_DRIVER=pusher
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_ID=
在配置文件中为您的帐户设置参数broadcasting
:
'connections' => [
'pusher' => [
'options' => [
'cluster' => 'eu',
'encrypted' => true
],
],
然后我们将App\Providers\BroadcastServiceProvider
在数组中注册providers
。
'providers' => [
App\Providers\BroadcastServiceProvider
],
现在我们需要从Pusher安装PHP SDK和Laravel Echo:composer require pusher/pusher-php-server
npm install --save laravel-echo pusher-js
我们需要设置广播通知数据。让我们修改通知UserFollowed
:
class UserFollowed extends Notification implements ShouldQueue
{
public function via($notifiable)
{
return ['database', 'broadcast'];
}
public function toArray($notifiable)
{
return [
'id' => $this->id,
'read_at' => null,
'data' => [
'follower_id' => $this->follower->id,
'follower_name' => $this->follower->name,
],
];
}
}
和NewPost
:
class NewPost extends Notification implements ShouldQueue
{
public function via($notifiable)
{
return ['database', 'broadcast'];
}
public function toArray($notifiable)
{
return [
'id' => $this->id,
'read_at' => null,
'data' => [
'following_id' => $this->following->id,
'following_name' => $this->following->name,
'post_id' => $this->post->id,
],
];
}
}
我们需要做的最后一件事是更新我们的JS。打开app.js
并添加以下代码
window.Pusher = require('pusher-js');
import Echo from "laravel-echo";
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key',
cluster: 'eu',
encrypted: true
});
var notifications = [];
$(document).ready(function() {
if(Laravel.userId) {
window.Echo.private(`App.User.${Laravel.userId}`)
.notification((notification) => {
addNotifications([notification], '#notifications');
});
}
});
仅此而已。通知是实时添加的。现在,您可以使用该应用程序,并查看通知的更新方式。结论
Pusher具有非常人性化的API,它使实时事件的实现变得异常简单。结合Laravel通知,我们可以从一个地方通过多个渠道(电子邮件,SMS,Slack等)发送通知。在本指南中,我们添加了在一个简单博客中跟踪用户活动的功能,并使用上述工具对其进行了改进,以获得一些流畅的实时功能。Pusher和Laravel具有更多通知:串联服务可让您将发布/订阅消息实时发送到浏览器,移动电话和IOT设备。还有一个用于获取在线/离线用户状态的API。请参阅他们的文档(Pusher文档,Pusher教程,Laravel文档),以了解有关其使用和真正潜力的更多信息。如果您有任何意见,问题或建议,请随时在下面的评论中分享!
了解有关该课程的更多信息。