You are on page 1of 12

IMPLEMENTING DESKTOP NOTIFICATIONS IN YOUR

LARAVEL APP
Olayinka Omole May 10th, 2018

You will need PHP and Laravel (5.4 or above) installed on your machine. You should have a working
knowledge of PHP and JavaScript.

Realtime noti cations are now very common in modern web applications, as site owners want to keep
users engaged and informed of happenings on their platform. Noti cations are also a great way to build
addiction, and make sure users keep coming back to your platform to see "what's new".

With Laravel and some Pusher magic, I will be demonstrating how to build a realtime app, with desktop
noti cations, utilising the Noti cation API.

Our application
We will build a simple News noti cation module which will alert everyone on a website when a new post
is published. Ideally, this would be part of a larger app, but we will build it in isolation here to showcase
our desktop noti cations.

At the end of this tutorial, you will have learned how to:

1. Broadcast events in Laravel, using Pusher


2. Listen for events on channels, using Laravel Echo and Vue.js
3. Use the Noti cation API to display desktop noti cations

To follow along, you will need a working knowledge of PHP and JavaScript. Basic knowledge of Laravel
and Vue.js are also needed. Laravel 5.4 and Vue.js 2.3 are used.

Introduction to Pusher
Pusher is a service that makes it super easy to add realtime functionality to web and mobile applications.
We will be using it in our application, so sign up to a free account here, create an app, and copy out the
app credentials (App ID, Key and Secret) from the “App Keys” section.
Setup and con guration
For starters, let us set up our app, and do the necessary con guration. We will call the app news-talk .

To create a new app with the Laravel installer, run this command:

laravel new news-talk

The next set of commands should be run in the app's root directory.

Installing the Pusher PHP library:

composer require pusher/pusher-php-server

Installing the Laravel Frontend Dependencies (these include Bootstrap, Axios, Vue.js and a couple of
other things which are nice to have):

npm install

Installing Laravel Echo and Pusher-js which we will use to listen for broadcast events:

npm install -S laravel-echo pusher-js

Next, we will do some more minor con guration to let Laravel know we will be using Pusher to manage
our broadcasts.

Editing the .env :

# ./.env

BROADCAST_DRIVER=pusher

PUSHER_APP_ID=your_pusher_add_id
PUSHER_APP_KEY=your_pusher_app_key
PUSHER_APP_SECRET=your_pusher_app_secret

You can edit some more optional con guration for Pusher in the ./config/broadcasting.php le
generated by Laravel. You can see other options here.

Note: If you created your app in a different cluster to the default us-east-1 , you must
con gure the cluster option. It is optional if you chose the default option.

Finally, we will con gure Echo to use Pusher. We do that by uncommenting and editing the values at the
bottom of resources/assets/js/bootstrap.js :

// ./resources/assets/js/bootstrap.js

import Echo from "laravel-echo"

window.Echo = new Echo({


broadcaster: 'pusher',
key: 'your_pusher_key'
});

Building the backend


Let's create a table for our posts. This is where data we create via our app will be persisted to. We will
use a Laravel migration le, and an Eloquent model for communication with the database.

To create a Post model run this command:

php artisan make:model Post -m -c

The -m and c ags are for automatically generating the migration and controller les respectively.

Next, we edit the generated migration le located in the ./database/migrations folder. We adjust the up

method to look like this:

public function up() {


Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('description');
$table->timestamps();
});
}

Then, after editing the .env with your database details, you can create the table with this command:

php artisan migrate

Tip: You can read more on the Laravel .env le here


We should also edit the mass-assignable properties on the model:

# ./app/Post.php

class Post extends Model


{

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['title', 'description'];
}

Saving a post

Next, we will add a route and controller method to save a new post.

We will be making an API call from the front-end to save our posts, so we can add the new route to the
API routes at ./routes/api.php . Whatever route de ned here will be pre xed by api , and belongs to the
api middleware group.

# ./routes/api

Route::post('/post', 'PostController@store');

Adding the corresponding controller method:

# ./app/Http/Controllers/PostController.php

use App\Post;

class PostController extends Controller {

/**
* Saves a new post to the database
*/
public function store(Request $request) {
// ...
// validation can be done here before saving
// with $this->validate($request, $rules)
// ...

// get data to be saved in an associative array using $request->only()


$data = $request->only(['title', 'description']);
// save post and assign return value of created post to $post array
$post = Post::create($data);

// return post as response, Laravel automatically serializes this to JSON


return response($post, 201);
}
}

Working with events


Events are a great way to separate out application logic. We can de ne events to be triggered in our
application when an action occurs, and we can de ne listeners, to listen for such events and carry out
other activities.

Laravel allows for easy de nition of events and listeners out of the box. It also includes helper functions
and classes to allow us easily trigger and broadcast events.

We can create a new event with this command:

php artisan make:event PostPublished

The event class le is created at ./app/Events .

We can then edit it to suit our needs:

# ./app/Events/PostPublished.php

class PostPublished implements ShouldBroadcast {


use Dispatchable, InteractsWithSockets, SerializesModels;

public $post;

public function __construct($post) {


$this->post = $post;
}

/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn() {
return new Channel('posts');
}

public function broadcastWith() {


return [
'title' => $this->post->title,
];
}
}

The Illuminate\Contracts\Broadcasting\ShouldBroadcast interface on the event class is used to inform


Laravel that this event should be broadcast.

The broadcastOn method returns the channel that we want to broadcast our event on. The Channel class
is used for broadcasting on public channels. PrivateChannel and PresenceChannel are for private
channels (these require authentication for access). You can read more about the various Pusher
channels here.

By default, Laravel broadcasts all of an event class’ public properties as its payload… broadcastWith
helps us override that behaviour and choose what we want to send.

Dispatching events

In our app, we want to dispatch the PostPublished event after a post has been saved. In Laravel, we can
dispatch events using the Event Facade, or the event() helper function.

To dispatch our PostPublished event, we can edit the store method in the PostController , and place
the event call right after the post is saved:

# ./app/Http/Controllers/PostController.php

use App\Events\PostPublished;

// save post and assign return value of created post to $post array
$post = Post::create($data);

// fire PostPublished event after post is successfully added to database


event(new PostPublished($post));
// or
// \Event::fire(new PostPublished($post))

The nal PostController le will look like this:

# ./app/Http/Controllers/PostController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Events\PostPublished;
use App\Post;

class PostController extends Controller {

/**
/
* Saves a new post to the database
*/
public function store(Request $request) {
// ...
// validation can be done here before saving
// with $this->validate($request, $rules)
// ...

// get data to save in an associative array using $request->only()


$data = $request->only(['title', 'description']);

// save post and assign return value of created post to $post array
$post = Post::create($data);

// fire PostPublished event after post is successfully added to database


event(new PostPublished($post));
// or
// \Event::fire(new PostPublished($post))

// return post as response, Laravel automatically serializes this to JSON


return response($post, 201);
}
}

Now that we are done with building the backend, we can proceed to create our view and event listener
on the Frontend.

Building the frontend


To create a basic page view for our app, we can edit the default welcome.blade.php le created by
Laravel. We can replace its contents with the following:

<!-- ./resources/views/welcome.blade.php -->

<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- CSRF Token -->


<meta name="csrf-token" content="{{ csrf_token() }}">

<title>News Talk</title>

<!-- Styles -->


<link href="{{ asset('css/app.css') }}" rel="stylesheet">

<style>
.container {
.container {
padding-top: 100px;
}
</style>

<!-- Scripts -->


<script>
window.Laravel = {!! json_encode([
'csrfToken' => csrf_token(),
]) !!};
</script>
</head>
<body>

<div id="app">
<!-- home Vue component -->
<home></home>
</div>

<!-- Scripts -->


<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

Most of the code above is boilerplate Laravel HTML content with relevant scripts and CSS les attached.
We will generate them later on.

We also included a Vue component ( home ) which hasn't been de ned yet. Let us go ahead to create and
de ne it.

Creating the home Vue component:

<!-- ./resources/assets/js/components/Home.vue -->


<template>
<div class="container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="form-group">
<label for="title">Post Title</label>
<input v-model="newPostTitle" id="title" type="text" class="form-control">
</div>
<div class="form-group">
<label for="description">Post Description</label>
<textarea v-model="newPostDesc" id="description" rows="8" class="form-control"></textare
</div>
<button @click="addPost(newPostTitle, newPostDesc)"
:class="{disabled: (!newPostTitle || !newPostDesc)}"
class="btn btn-block btn-primary">Submit</button>
</div>
</div>
</div>
</template>

i t
<script>
export default {
data() {
return {
newPostTitle: "",
newPostDesc: ""
}
},
created() {
this.listenForChanges();
},
methods: {
addPost(postName, postDesc) {
// check if entries are not empty
if(!postName || !postDesc)
return;

// make API to save post


axios.post('/api/post', {
title: postName, description: postDesc
}).then( response => {
if(response.data) {
this.newPostTitle = this.newPostDesc = "";
}
})
},
listenForChanges() {
Echo.channel('posts')
.listen('PostPublished', post => {
if (! ('Notification' in window)) {
alert('Web Notification is not supported');
return;
}

Notification.requestPermission( permission => {


let notification = new Notification('New post alert!', {
body: post.title, // content for the alert
icon: "https://pusher.com/static_logos/320x320.png" // optional image url
});

// link to page on clicking the notification


notification.onclick = () => {
window.open(window.location.href);
};
});
})
}
}
}
</script>

In the above code, we de ne two methods. addPost() and listenForChanges . The addPost method
makes a post request to our API with the required payload when a user adds a new post.

In the listenForChanges method we use Echo to subscribe to the posts channel which is the channel
In the listenForChanges method, we use Echo to subscribe to the posts channel, which is the channel
we are broadcasting to, from our backend. We also listen for PostPublished events, and de ne a callback
that activates our desktop noti cation whenever an event is red.

We are using the noti cations API for desktop noti cations. We rst request permission to send desktop
noti cations, then notify the user once permission is granted.

We can also check if a browser supports desktop noti cations this way:

if (window.Notification) {
console.log('Notifications are supported!');
} else {
alert('Notifications aren\'t supported on your browser! :(');
}

We create an instance of the Noti cation object with our post title as the body. An optional icon

parameter can also be set, as we did.

Finally, we de ne the component as a global component in app.js :

// ./resources/assets/js/app.js

Vue.component('home', require('./components/Home.vue'));

Bringing it all together


We can compile our assets easily using Laravel Mix!:

npm run dev

Now, we can navigate to the app's homepage to see it in action. If you use Laravel Valet, you can also
share the app, and visit it via another device to test the desktop noti cations.
Conclusion
We have learned how to build an event-driven basic realtime app enabled with desktop noti cations,
thanks to Laravel and Pusher. As a next step, you could also learn how to build a chat application with
Pusher, and integrate desktop noti cations whenever a user receives a message... awesome, right?

If you’ve thought of any other great ways to use Pusher and Laravel, let us know in the comments!

The entire code for this tutorial is hosted on Github. You can look through and ask questions if you need
more information.

LARAVEL PHP VUE.JS

CHANNELS

Products Developers
Channels Docs

Beams Tutorials

Status

Support

Sessions

Company Connect
Contact Sales Twitter
Customer stories Medium

Terms & Conditions YouTube

Security LinkedIn

Careers GitHub

Blog

© 2020 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered of ce is at 160 Old Street, London, EC1V
9BW.

You might also like