Real Time
Real Time
To make real time notification, we need to separate as two parts: backe-end and front-end part.
Because, in back-end side, we make broadcast notification by using laravel websockets to send real time
notification.
Then, from front-end side, by using laravel-echo and pusher-js(both is javascript library), listen channel
from laravel websockets server and show notifications in front-end application.
Ref Link:
https://laravel.com/docs/7.x/notifications
https://laravel.com/docs/7.x/broadcasting
https://laravel.com/docs/7.x/events
https://www.youtube.com/watch?v=7MvN0w5BW48&list=PLzz9vf6075V25O3jAzvBrU-
k4PscUWoLi&index=5
https://medium.com/@mihkelallorg/laravel-react-jwt-tokens-and-laravel-echo-with-pusher-for-
dummies-like-me-cafc5a09a1a1
Note: Before following step, you must already setup API authentication with laravel passport.
This package comes with a migration to store statistic information while running your WebSocket server.
You can publish the migration file using:
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\
WebSocketsServiceProvider" --tag="migrations"
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=841161584
PUSHER_APP_KEY=64498465165
PUSHER_APP_SECRET=asdf4a87f65464
We just use pusher as a broadcast driver. Not really use pusher paid service. So that, we can give any
random number that we want as app_id, app_key and app_secret keys.
Step 5. In config/broadcasting.php, add host, port, schema into pusher array as shown in below.
You can copy these lines from laravel websockets line:
https://beyondco.de/docs/laravel-websockets/basic-usage/pusher#pusher-configuration.
The reason of putting those lines because we tell pusher to use our local laravel websockets server,
instead of pusher server.
If your websockets server is on separate server with specified port, you can change in that place.
As we use broadcast notifications with private channel, we need to authorize that the currently
authenticated user can actually listen on the channel.
Laravel makes it easy to define the routes to respond to channel authorization requests.
The Broadcast::routes() method will place its routes within the web middleware group; however, we
are making API, we can change web middleware to api middleware by passing parameter to
Broadcast::routes() method.
Add Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]); into boot() method of this file.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);
require base_path('routes/channels.php');
}
}
And we also add api prefix. So, /broadcasting/auth will change into /api/broadcasting/auth. You can
check that route changes with: php artisan route:list.
Step 7. Next, we need to define the logic that will actually perform the channel authorization. There’s
already an example channel defined for us in routes/channels.php file. I only changed the channel name
from App.User.{id} to App.EmployeePersonal.{id}
Broadcast::channel('App.EmployeePersonal.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Each user has its own channel and in this example only the user itself can subscribe to its channel.
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class EmployeePersonal extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The channels the user receives notification broadcasts on.
*
* @return string
*/
public function receivesBroadcastNotificationsOn()
{
return 'App.EmployeePersonal.' . $this->id;
}
}
Step 9. When sending notification, we also store that notification into database. We need to create a
table to store our notifications. Use following command to create a laravel default notifications table.
php artisan notifications:table
Now, our setup is complete for creating notifications table, broadcast channel to send notification and
broadcast channel authorization routes.
Step 10. Let assume, when a user is create a product, we will send notification to all users. To do that,
we will start create a notification class with followin command.
php artisan make:notification ProductNotify
This command will place a fresh notification class in your app/Notifications directory
[app/Notifications/ProductNotify.php]. You can give any name you want.
By creating notification class by artisan command, laravel will already place some methods:
__construct(), via(), toMail(), toArray().
<?php
namespace App\Notifications;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class ProductNotify extends Notification implements ShouldBroadcastNow
{
private $product;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($product)
{
$this->product = $product;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['database', 'broadcast'];
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'product_id' => $this->product->id,
'product_name' => $this->product->product_name,
'quantity' => $this->product->quantity,
'page' => 'product',
'message' => $this->product->message
];
}
/**
* Get the broadcastable representation of the notification.
*
* @param mixed $notifiable
* @return BroadcastMessage
*/
public function toBroadcast($notifiable)
{
return new BroadcastMessage($this->toArray($notifiable));
}
public function broadcastType()
{
return 'new-product';
}
}
__construct(): we will send product information as notification, we pass product object in constructor.
via(): as we will send broadcast notification and save into database, we need to set database and
broadcast.
toArray(): The toArray() method is also used by the broadcast channel to determine which data to
broadcast to your JavaScript client. If you would like to have two different array representations for the
database and broadcast channels, you should define a toDatabase method instead of a toArray
method. But, now, I will only use toArray() because we will send and save same notification data.
toMail(): We will remove toMail() method because we don’t need to send email, just send notification.
toBroadcast(): The broadcast channel broadcasts notifications using Laravel's event broadcasting
services, allowing your JavaScript client to catch notifications in realtime. If a notification supports
broadcasting, you can define a toBroadcast method on the notification class. This method will receive a
$notifiable entity and should return a BroadcastMessage instance.
broadcastType(): In addition to the data you specify, all broadcast notifications also have a type field
containing the full class name of the notification. If you would like to customize the notification type that
is provided to your JavaScript client, you may define a broadcastType method on the notification class.
<?php
namespace App\Http\Controllers\API\Product;
use App\Http\Controllers\Controller;
use App\Models\EmployeePersonal;
use App\Models\Product;
use App\Notifications\ProductNotify;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification as FacadesNotification;
class ProductController extends Controller
{
public function create(Request $req) {
DB::beginTransaction();
try {
$product = new Product;
$product->product_name = $req->product_name;
$product->quantity = $req->quantity;
$product->save();
// sent notification to all users
$users = EmployeePersonal::all();
// add new custom message to show as notification in front-end
$product->message = 'New product '.$product->product_name.' is creat
ed!';
FacadesNotification::send($users, new ProductNotify($product));
DB::commit();
return response()->json(['message':'New Product Created!'], 200);
} catch (\Throwable $th) {
DB::rollBack();
return response()->json((['message':$th->getMessage()], 400);
}
}
}
Inside of this class, when we create a new product, we will send notification to all users by using
notification facades FacadesNotification::send($users, new ProductNotify($product));. We also pass
our product object when calling notification class.
Step 12. This step is optional. If you want to show all notifications as a page in your web applications and
you want to set features like: Mark All As Read for all notifications or Mark As Read for a user clicked
notification, you can use following sample code.
Before start accessing notifications table, we need to create model for that table with following
commands. It will create a model in this path: app/Models/Notification.php
php artisan make:model Models\Notification
<?php
namespace App\Http\Controllers\API\Notification;
use App\Http\Controllers\Controller;
use App\Models\EmployeePersonal;
use App\Models\Notification;
use Illuminate\Http\Request;
class NotificationController extends Controller
{
/**
* Show all unread notification for requested user
*
* @param userId
*/
public function show(Request $req) {
$result = [];
$users = EmployeePersonal::find($req->user_id);
foreach($users->unreadNotifications as $noti) {
$tmp['id'] = $noti['id'];
$tmp['notifiable_id'] = $noti['notifiable_id'];
$tmp['message'] = $noti['data']['message'];
$result[] = $tmp;
}
return response()->json(['data' => $result], 200);
}
/**
* Make notification mark as read
*
* @param notifications id -> f3187479-ab6c-4ce2-8ddc-5103f28f7155
*/
public function readNotification(Request $req)
{
$read = Notification::where('id', $req->id)->update(['read_at'=>now()]);
if ($read) {
return response()->json(['message'=>'Mark as read'], 200);
} else {
return response()->json(['message'=>'Something wrong!'], 200);
}
}
/**
* Mark all notification as read
*
* @param userId
*/
public function readAllNotifications(Request $req)
{
$user = EmployeePersonal::find($req->user_id);
$user->unreadNotifications->markAsRead();
return response()->json(['message'=>'mark all as read'], 200);
}
}
show(): to show all user unread notifications. ‘unread’ means read_at column is NULL inside
notifications table for requested user.
readNotification(): make specific requested notification data to mark as read. ‘mark as read’ means
updating read_at column with current time inside notifications table for specific notification id.
readAllNotifications(): mark all as read for all notifications of requested user. It means updating read_at
column with current time inside notifications table for all of specific user id.
Route::post('login', 'API\User\EmployeePersonalController@login');
Route::post('register', 'API\User\EmployeePersonalController@register');
Route::middleware(['auth:api'])->group(function() {
Route::post('product-register', 'API\Product\ProductController@create');
Route::get('notifications', 'API\Notification\NotificationController@show');
Route::post('read', 'API\Notification\
NotificationController@readNotification');
Route::post('read-all', 'API\Notification\
NotificationController@readAllNotifications');
});
Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events
broadcast by Laravel. You may install Echo via the NPM package manager. In this example, we will also
install the pusher-js package since we will be using the Pusher Channels broadcaster:
npm install --save laravel-echo pusher-js
Step 2. Once Echo is installed, you are ready to create a fresh Echo instance in your application's
JavaScript. A good place to do this is inside of componentDidMount() method because we can update
state immediately if we got a new notification.
import Echo from "laravel-echo"
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-channels-key'
});
Inside of Echo instance you can also make authentication. I will show later.
Step 3. Once you have installed and instantiated Echo, you are ready to start listening for broadcast
notifications. You may listen for the broadcast events using Echo's notification method. Remember, the
channel name should match the class name of the entity receiving the notifications:
window.Echo.private(`App.EmployeePersonal.${userId}`)
.notification((notification) => {
console.log(notification.type);
});
In this example, all notifications sent to App\EmployeePersonal instances via the broadcast
channel would be received by the callback. A channel authorization callback for the
App.EmployeePersonal.{id} channel is included in the default BroadcastServiceProvider that
ships with the Laravel framework.
Note: Channel name that listen in Echo is the channel that we already made in laravel. See Step
7 of laravel side.
Following is the complete sample code of instainting laravel Echo and listen broadcast notification from
App.EmployeePersonal.{id} broadcast channel with channel authentication.
componentDidMount() {
const token = 'xxxxxx-xxx-xxx';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: '64498465165',
cluster: 'mt1',
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
disableStats: true,
//authEndpoint is your apiUrl + /broadcasting/auth
authEndpoint: 'http://localhost/RealTimeNotiTest/public/api/broadcasting/
auth',
// As I'm using JWT tokens, I need to manually set up the headers.
auth: {
headers: {
Authorization: 'Bearer '+token,
Accept: 'application/json',
},
}
});
window.Echo.private('App.EmployeePersonal.5')
.notification((notify) => {
console.log(notify);
});
}
key and cluster must be same that we already set in .env file inside laravel project. See Step 4 of laravel
side.
authEnpoint is the api url for broadcast authentication. This is related to Step 6 of laravel side.
In auth, we can set Authorization header for access token key and other necessary header.
Note: There is one important thing to notice that, token key that we set as authorization headers must
be user id’s token key that we listen in App.EmployeePersonal.id.
Now, in your front-end, you are ready to get notifications from laravel websockets server.
Following is the sample result to show you how it works.