Bagaimana cara menambahkan notifikasi real-time ke Laravel menggunakan Pusher

Terjemahan artikel disiapkan khusus untuk siswa kursus "Framework Laravel" .




Pengguna web modern berharap mendapat informasi tentang semua yang terjadi dalam aplikasi. Anda tidak ingin menjadi situs web yang bahkan tidak memiliki daftar drop-down notifikasi, yang sekarang dapat ditemukan tidak hanya di semua situs jejaring sosial, tetapi umumnya di mana-mana saat ini.

Untungnya, dengan Laravel dan Pusher, penerapan fungsi ini cukup sederhana.

Pemberitahuan waktu nyata


Untuk memberikan pengalaman pengguna yang positif, pemberitahuan harus ditampilkan secara real time. Salah satu pendekatan adalah dengan secara teratur mengirim permintaan AJAX ke server dan menerima pemberitahuan terbaru, jika ada.

Pendekatan terbaik adalah dengan menggunakan kemampuan WebSockets dan menerima notifikasi ketika dikirim. Inilah yang akan kami terapkan dalam artikel ini.

Pendorong


Pusher adalah layanan web untuk mengintegrasikan fungsi dua arah real-time melalui WebSockets ke dalam aplikasi web dan seluler.

Ini memiliki API yang sangat sederhana, tetapi kita akan membuatnya lebih mudah dengan Laravel Broadcasting dan Laravel Echo.

Pada artikel ini, kami akan menambahkan notifikasi real-time ke blog yang ada.

Proyek


Inisialisasi


Pertama, kami mengkloning blog Laravel sederhana:

git clone   https://github.com/marslan-ali/laravel-blog

Kemudian kita akan membuat database MySQL dan mengatur variabel lingkungan untuk memberikan akses aplikasi ke database.

Copy Mari env.exampledi .envdan memperbarui variabel yang terkait dengan database.

cp .env.example .envDB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

Sekarang mari kita instal dependensi proyek menggunakan

composer install

Dan jalankan perintah migrasi dan isi untuk mengisi basis data dengan beberapa data:

php artisan migrate --seed

Jika Anda menjalankan aplikasi dan pergi ke /posts, Anda dapat melihat daftar posting yang dihasilkan.

Periksa aplikasi, daftarkan pengguna dan buat beberapa pesan. Ini adalah aplikasi yang sangat sederhana, tetapi sangat bagus untuk demonstrasi.

Berlangganan Pengguna


Kami ingin memberikan kesempatan kepada pengguna untuk berlangganan satu sama lain, jadi kami harus membuat hubungan banyak-ke-banyak di antara pengguna untuk mewujudkan hal ini.

Mari kita buat tabel pivot yang menautkan pengguna ke pengguna. Mari lakukan migrasi baru followers:

php artisan make:migration create_followers_table --create=followers

Kami perlu menambahkan beberapa bidang ke migrasi ini: user_iduntuk mewakili pengguna yang berlangganan, dan bidang follows_iduntuk mewakili pengguna yang mereka berlangganan.

Perbarui migrasi sebagai berikut:

public function up()
{
    Schema::create('followers', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id')->index();
        $table->integer('follows_id')->index();
        $table->timestamps();
    });
}

Sekarang mari kita beralih ke membuat tabel:

php artisan migrate

Mari menambahkan metode hubungan ke model 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();
    }
}

Sekarang setelah model Usermemiliki hubungan yang diperlukan, ia followersmengembalikan semua pelanggan pengguna, dan followsmengembalikan semua orang kepada siapa pengguna berlangganan.

Kami akan memerlukan beberapa fungsi tambahan yang memungkinkan pengguna untuk berlangganan ke pengguna lain - follow, dan untuk memeriksa apakah pengguna berlangganan ke pengguna tertentu - 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']);
    }

}

Baik. Setelah menyiapkan model, Anda perlu membuat daftar pengguna.

daftar pengguna


Mari kita mulai dengan mengidentifikasi rute yang diperlukan.

/...
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');
});

Maka sudah saatnya membuat pengontrol baru untuk pengguna:

php artisan make:controller UsersController

Kami akan menambahkan metode untuk itu 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'));
    }
}

Metode ini perlu diperkenalkan. Mari kita buat tampilan users.indexdan letakkan markup berikut di dalamnya:

@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

Sekarang Anda dapat mengunjungi halaman /usersuntuk melihat daftar pengguna.

Ikuti dan Berhenti Ikuti


Dalam UsersControllermetode yang hilang followdan unfollow. Mari kita implementasikan untuk menyelesaikan bagian ini.

//...
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}");
    }
}

Kami selesai dengan fungsi ini. Sekarang kita dapat berlangganan pengguna dan berhenti berlangganan dari mereka di halaman /users.

Notifikasi


Laravel menyediakan API untuk mengirim pemberitahuan di berbagai saluran. Email, SMS, pemberitahuan web, dan jenis pemberitahuan lainnya dapat dikirim menggunakan kelas Pemberitahuan .

Kami akan memiliki dua jenis pemberitahuan:

  • Pemberitahuan berlangganan: dikirim ke pengguna saat pengguna lain berlangganan
  • Posting pemberitahuan: dikirim ke pelanggan pengguna ini ketika posting baru diterbitkan.

Pemberitahuan Berlangganan


Menggunakan perintah artisan, kami dapat membuat migrasi untuk pemberitahuan:

php artisan notifications:table

Ayo lakukan migrasi dan buat tabel ini.

php artisan migrate

Kami akan mulai dengan pemberitahuan berlangganan. Mari kita jalankan perintah ini untuk membuat kelas notifikasi:

php artisan make:notification UserFollowed

Kemudian kami memodifikasi file kelas notifikasi yang baru saja kami buat:

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

Dengan beberapa baris kode ini, kita sudah dapat mencapai banyak hal. Pertama, kami mengharuskan instance $followerditerapkan ketika pemberitahuan ini dihasilkan.

Dengan menggunakan metode ini via, kami memberi tahu Laravel untuk mengirim pemberitahuan ini melalui saluran database. Ketika Laravel menemukan ini, itu membuat entri baru di tabel notifikasi.

user_iddan typenotifikasi diatur secara otomatis, ditambah kami dapat memperluas notifikasi dengan data tambahan. Itu untuk apa toDatabase. Array yang dikembalikan akan ditambahkan ke bidang datanotifikasi.

Dan akhirnya, berkat implementasinyaShouldQueue, Laravel akan secara otomatis menempatkan notifikasi ini dalam antrian yang akan berjalan di latar belakang, yang akan mempercepat respons. Ini masuk akal karena kami akan menambahkan panggilan HTTP saat kami menggunakan Pusher nanti.

Mari menerapkan pemberitahuan berlangganan pengguna.

// ...
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}");
    }

    //...
}

Kami dapat memanggil metode notifikasi untuk model Pengguna karena sudah menggunakan fitur Notifiable.

Model apa pun yang ingin Anda beri tahu harus menggunakannya untuk mendapatkan akses ke metode pemberitahuan.

Kami menandai pemberitahuan saat dibaca.

Pemberitahuan akan berisi beberapa informasi dan tautan ke sumber. Misalnya: ketika pengguna menerima pemberitahuan tentang pesan baru, pemberitahuan tersebut harus berisi teks informatif, mengarahkan pengguna ke pesan ketika ditekan dan ditandai sebagai sudah dibaca.

Kami akan membuat layer yang akan memeriksa apakah ada kejadian dalam permintaan ?read=notification_iddan menandainya sebagai sudah dibaca.

Mari kita buat layer ini dengan perintah berikut:

php artisan make:middleware MarkNotificationAsRead

Lalu mari kita letakkan kode ini dalam metode handleinterlayer:

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);
    }
}

Agar lapisan kami dieksekusi untuk setiap permintaan, kami akan menambahkannya ke $middlewareGroups.

//...
class Kernel extends HttpKernel
{
    //...
    protected $middlewareGroups = [
        'web' => [
            //...
            \App\Http\Middleware\MarkNotificationAsRead::class,
        ],
        // ...
    ];
    //...
}

Setelah itu, mari kita tampilkan notifikasi.

Tampilkan pemberitahuan


Kita perlu menunjukkan daftar notifikasi menggunakan AJAX, dan kemudian memperbaruinya secara real time menggunakan Pusher. Pertama, mari kita tambahkan metode notificationske controller:

// ...
class UsersController extends Controller
{
    // ...
    public function notifications()
    {
        return auth()->user()->unreadNotifications()->limit(5)->get()->toArray();
    }
}

Kode ini akan mengembalikan 5 notifikasi terakhir yang belum dibaca. Kami hanya perlu menambahkan rute agar dapat diakses.

//...
Route::group([ 'middleware' => 'auth' ], function () {
    // ...
    Route::get('/notifications', 'UsersController@notifications');
});

Sekarang tambahkan daftar drop-down untuk notifikasi di header.

<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
        <!-- // add this dropdown // -->
        <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>
<!-- // ... // -->

Kami juga menambahkan variabel global window.Laravel.userIdke skrip untuk mendapatkan ID pengguna saat ini.

JavaScript dan SASS


Kita akan menggunakan Laravel Mix untuk mengkompilasi JavaScript dan SASS. Pertama, kita perlu menginstal paket npm.

npm install

Sekarang mari kita tambahkan kode ini ke app.js:

window._ = require('lodash');
window.$ = window.jQuery = require('jquery');
require('bootstrap-sass');
var notifications = [];
const NOTIFICATION_TYPES = {
    follow: 'App\\Notifications\\UserFollowed'
};

Ini hanya inisialisasi. Kami akan menggunakan notifikasi untuk menyimpan semua objek notifikasi, apakah itu diambil melalui AJAX atau Pusher.

Seperti yang mungkin sudah Anda tebak, ini NOTIFICATION_TYPESberisi jenis pemberitahuan.

Sekarang, mari kita dapatkan ("GET") pemberitahuan melalui AJAX.

//...
$(document).ready(function() {
    // ,      
    if(Laravel.userId) {
        $.get('/notifications', function (data) {
            addNotifications(data, "#notifications");
        });
    }
});
function addNotifications(newNotifications, target) {
    notifications = _.concat(notifications, newNotifications);
    //    5 
    notifications.slice(0, 5);
    showNotifications(notifications, target);
}

Berkat kode ini, kami menerima pemberitahuan terbaru dari API kami dan memasukkannya ke daftar drop-down.

Di dalam, addNotificationskami menggabungkan pemberitahuan yang ada dengan yang baru menggunakan Lodash , dan hanya mengambil 5 yang terakhir, yang akan ditampilkan.

Kami membutuhkan beberapa fungsi lagi untuk menyelesaikan pekerjaan.

//...
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');
    }
}

Fungsi ini membuat garis semua pemberitahuan dan menempatkannya di daftar turun bawah.
Jika tidak ada pemberitahuan yang diterima, cukup "Tidak ada pemberitahuan" ditampilkan.

Itu juga menambahkan kelas ke tombol drop-down, yang hanya berubah warna ketika ada notifikasi. Agak seperti notifikasi Github.

Akhirnya, beberapa fungsi pembantu untuk membuat string notifikasi.

//...
//   
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;
}

Sekarang kita tambahkan ini ke file kita app.scss:

//... 
#notifications.has-notifications {
  color: #bf5329
}

Mari kompilasi aset:

npm run dev

Sekarang jika Anda mencoba berlangganan ke pengguna, ia akan menerima pemberitahuan. Ketika dia mengkliknya, dia akan dialihkan ke /users, dan pemberitahuan itu sendiri menghilang.

Pemberitahuan Posting Baru


Kami akan memberi tahu pelanggan ketika pengguna menerbitkan posting baru.

Mari kita mulai dengan membuat kelas notifikasi.

php artisan make:notification NewPost

Mari kita modifikasi kelas yang dihasilkan sebagai berikut:

// ..
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,
        ];
    }
}

Selanjutnya kita perlu mengirim pemberitahuan. Ada beberapa cara untuk melakukan ini.

Saya suka menggunakan pengamat Eloquent.

Mari kita buat pengamat untuk Post dan dengarkan acara-acaranya. Kami akan membuat kelas baru: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));
        }
    }
}

Kemudian daftarkan pengamat di AppServiceProvider:

//...
use App\Observers\PostObserver;
use App\Post;
class AppServiceProvider extends ServiceProvider
{
    //...
    public function boot()
    {
        Post::observe(PostObserver::class);
    }
    //...
}

Sekarang kita hanya perlu memformat pesan untuk ditampilkan di 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;
}

Dan voila! Pengguna diberitahu tentang langganan dan posting baru! Cobalah sendiri!

Keluar secara real time dengan Pusher


Saatnya menggunakan Pusher untuk menerima notifikasi waktu nyata melalui soket web.

Daftarkan akun Pusher gratis di pusher.com dan buat aplikasi baru.

...
BROADCAST_DRIVER=pusher
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_ID=

Tetapkan parameter untuk akun Anda di file konfigurasi broadcasting:

//...
    'connections' => [
            'pusher' => [
                //...
                'options' => [
                    'cluster' => 'eu',
                    'encrypted' => true
                ],
            ],
    //...

Kemudian kita akan mendaftar App\Providers\BroadcastServiceProviderdi array providers.

// ...
'providers' => [
    // ...
    App\Providers\BroadcastServiceProvider
    //...
],
//...

Sekarang kita perlu menginstal SDK PHP dan Laravel Echo dari Pusher:

composer require pusher/pusher-php-server
npm install --save laravel-echo pusher-js

Kita perlu mengatur data notifikasi untuk disiarkan. Mari modifikasi notifikasi 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,
            ],
        ];
    }
}

Dan 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,
            ],
        ];
    }
}

Hal terakhir yang perlu kita lakukan adalah memperbarui JS kita. Buka app.jsdan tambahkan kode berikut

// ...
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');
            });
    }
});

Dan itu saja. Pemberitahuan ditambahkan secara real time. Sekarang Anda dapat bermain dengan aplikasi dan melihat bagaimana notifikasi diperbarui.

Kesimpulan


Pusher memiliki API yang sangat user-friendly yang membuat implementasi acara real-time menjadi sangat sederhana. Dalam kombinasi dengan pemberitahuan Laravel, kami dapat mengirim pemberitahuan melalui beberapa saluran (email, SMS, Slack, dll.) Dari satu tempat. Dalam panduan ini, kami menambahkan fungsionalitas untuk melacak aktivitas pengguna di blog sederhana dan memperbaikinya menggunakan alat-alat di atas untuk mendapatkan fungsionalitas real-time yang lancar.

Pusher dan Laravel memiliki lebih banyak notifikasi: bersama-sama, layanan memungkinkan Anda untuk mengirim pesan pub / sub secara real time ke browser, ponsel, dan perangkat IOT. Ada juga API untuk mendapatkan status pengguna online / offline.

Silakan lihat dokumentasinya (dokumen Pusher, Tutorial Pusher , Laravel docs ) untuk informasi lebih lanjut tentang penggunaannya dan potensi sebenarnya.

Jika Anda memiliki komentar, pertanyaan atau rekomendasi, jangan ragu untuk membagikannya di komentar di bawah!



Pelajari lebih lanjut tentang kursus.



All Articles