Tạo ứng dụng thông báo realtime with laravel and pusher

Giới thiệu về Pusher
Pusher là một dịch vụ cung cấp cho người dùng một serve ảo làm trung gian xử lý các dữ liệu với thời gian thực. Vậy pusher hoạt động như thế nào? Client duyệt web –> dữ liệu sẽ được chuyến đến sever –> sever chuyển tiếp đến Pusher thông qua pusher API –> Pusher trả kết lại cho client. Pusher cung cấp cho chúng ta nhiều gói dịch vụ . Nhưng gói free của pusher cung đã cũng cấp cho chúng ta 200k tin nhắn và 100 kết nối mỗi ngày, nên nếu dùng cần dùng với số lượng tin nhắn lớn thì cần phải trả phí.

Các bước thực hiện như sau:
Bước 1 : Tạo tài khoản và tạo app pusher
Để sử dụng pusher ta phải đăng ký tại khoản ở [pusher.com]
Sau khi đăng ký thành công ta tiến hành tạo app bằng cách click vào menu Create new app
Màn hình tạo app hiện ra như sau:

Việt Nam thì chọn location Singapore để có tốc độ truy cập nhanh nhất nhé.
Tạo xong app, bạn tích sang tab App Keys và để ý mấy thông tin app_id, key, secret, cluster
Bước 2 : Tạo project laravel và tích hợp pusher
Tạo project laravel tên là realtimeblog

composer create-project --prefer-dist laravel/laravel realtimeblog "5.4.*"

Bạn nào muốn move file index.php ra ngoài root thì xem bài viết này:http://www.gacoder.info/move-index-php-in-public-folder-to-root-directory/
Cài gói thư viện pusher.
Ở đây mình dùng laravel 5.4 nên cách cài đặt như sau.Bạn có thể xem version pusher phù hợp với laravel ở document của trang chủ laravel
https://laravel.com/docs/5.4/broadcasting#configuration

composer require pusher/pusher-php-server "~2.6"

Cầu hình file .env
Mở file .env trong project Laravel, kéo xuống cuối cùng, chúng ta dùng những thông tin trong App Keys ở trên để cấu hình trong này:

PUSHER_APP_ID=YOUR_APP_ID
PUSHER_APP_KEY=YOUR_APP_KEY
PUSHER_APP_SECRET=YOUR_APP_SECRET

Và cũng trong file .env sửa dòng BROADCAST_DRIVER=log thành BROADCAST_DRIVER=pusher

BROADCAST_DRIVER=pusher

Tiếp tục mở file config/app.php, tìm đến dòng App\Providers\BroadcastServiceProvider::class, dòng này ban đầu đang được comment lại, chúng ta bỏ comment nó đi để có thể làm việc được với Broadcast.
Mở file config/broadcasting.php, thêm 1 dòng dưới đây vào option array trong mục cấu hình pusher

'cluster' => 'ap1'
'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => 'ap1',
            ],
        ],

Bước 3 : Tạo event
Để tạo event ta dùng command sau:

php artisan make:event DemoNotificationEvent

Event được tạo sẽ nằm trong folder D:\xampp\htdocs\realtimeblog\app\Events
Chỉnh event như sau:

<?php
namespace App\Events;
 
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
 
class DemoNotificationEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
 
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public $message;
 
    public function __construct($message)
    {
        $this->message = $message;
    }
 
    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('channel-demo-real-time');
    }
}

Bước 4 : Tạo controller và router
Bạn vào routes/web.php và thêm vào 1 route này:

Route::get('send-notification', 'NotificationController@sendNofication');

Tạo controller NotificationController.php:

php artisan make:controller NotificationController

Sửa content như sau:

<?php
 
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Events\DemoNotificationEvent;
 
class NotificationController extends Controller
{
    public function sendNofication(){
     // Truyền message lên server Pusher
     event(new DemoNotificationEvent("Hi,Xin chào hải đẹp trai!"));
     return "Message has been sent.";
    }
}

Bước 5 : Tạo view
Bạn sửa file D:\xampp\htdocs\realtimeblog\resources\views\welcome.blade.php thành như sau:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Demo Application</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css" href="public/css/bootstrap-notifications.min.css">
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-inverse">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-9" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Demo App</a>
        </div>
 
        <div class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="dropdown dropdown-notifications">
              <a href="#notifications-panel" class="dropdown-toggle" data-toggle="dropdown">
                <i data-count="0" class="glyphicon glyphicon-bell notification-icon"></i>
              </a>
 
              <div class="dropdown-container">
                <div class="dropdown-toolbar">
                  <div class="dropdown-toolbar-actions">
                    <a href="#">Mark all as read</a>
                  </div>
                  <h3 class="dropdown-toolbar-title">Notifications (<span class="notif-count">0</span>)</h3>
                </div>
                <ul class="dropdown-menu">
                </ul>
                <div class="dropdown-footer text-center">
                  <a href="#">View All</a>
                </div>
              </div>
            </li>
            <li><a href="#">Timeline</a></li>
            <li><a href="#">Friends</a></li>
          </ul>
        </div>
      </div>
    </nav>
 
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="//js.pusher.com/3.1/pusher.min.js"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
 
    <script type="text/javascript">
      var notificationsWrapper   = $('.dropdown-notifications');
      var notificationsToggle    = notificationsWrapper.find('a[data-toggle]');
      var notificationsCountElem = notificationsToggle.find('i[data-count]');
      var notificationsCount     = parseInt(notificationsCountElem.data('count'));
      var notifications          = notificationsWrapper.find('ul.dropdown-menu');
      if (notificationsCount <= 0) {
        notificationsWrapper.hide();
      }
      // Enable pusher logging - don't include this in production
      // Pusher.logToConsole = true;
      var pusher = new Pusher('API_KEY_HERE', {
            cluster: 'ap1',
            encrypted: true
        });
      // Subscribe to the channel we specified in our Laravel Event
      var channel = pusher.subscribe('channel-demo-real-time');
      // Bind a function to a Event (the full Laravel class)
      channel.bind('App\\Events\\DemoNotificationEvent', function(data) {
        var existingNotifications = notifications.html();
        var avatar = Math.floor(Math.random() * (71 - 20 + 1)) + 20;
        var newNotificationHtml = `
          <li class="notification active">
              <div class="media">
                <div class="media-left">
                  <div class="media-object">
                    <img src="https://api.adorable.io/avatars/71/`+avatar+`.png" class="img-circle" alt="50x50" style="width: 50px; height: 50px;">
                  </div>
                </div>
                <div class="media-body">
                  <strong class="notification-title">`+data.message+`</strong>
                  <!--p class="notification-desc">Extra description can go here</p-->
                  <div class="notification-meta">
                    <small class="timestamp">about a minute ago</small>
                  </div>
                </div>
              </div>
          </li>
        `;
        notifications.html(newNotificationHtml + existingNotifications);
        notificationsCount += 1;
        notificationsCountElem.attr('data-count', notificationsCount);
        notificationsWrapper.find('.notif-count').text(notificationsCount);
        notificationsWrapper.show();
      });
    </script>
  </body>
</html>

Bạn nhớ sửa lại API_KEY_HERE của pusher nhé
File bootstrap-notifications bạn có thể down ở đây :https://github.com/neoighodaro/laravel-pusher-web-notifications/blob/master/public/css/bootstrap-notifications.min.css
Bước 6 : Test
Bạn mở trình duyệt chạy route / sẽ thấy như sau:
http://localhost/realtimeblog/

Bạn sẽ chưa thấy gì nên bạn cần mở thêm 1 tab nữa hay một cửa sổ browser mới và chạy route send-notification

Quay lại trang chủ bạn sẽ thấy biểu tượng tin nhắn đã xuất hiện:

Bấm vào bạn sẽ thấy:

Bạn cũng có thể xem nội dung message trên server Pusher, tại app của bạn, click sang tab Debug Console