Push Notifications with Laravel and Webpush phần 2 (ok)

https://medium.com/@sagarmaheshwary31/push-notifications-with-laravel-and-webpush-446884265aaa

Push Notifications with Laravel and Webpush

In this tutorial, we will implement Push Notifications feature in our Laravel app using WebPush. We will use Vanilla Javascript with no frameworks or libraries. Push Notification is a feature of Service Workers. A Service Worker is a script inside the web browser that runs in the background. There are a lot of features like caching , background syncing but this tutorial is only about Push Notifications. We will also implement push notifications for guest users.

Note: Service Workers use HTTPS unless you are using localhost.

Getting Started

Let’s create a new laravel project with composer:

composer create-project laravel/laravel webpush

Before migrating table add this line in the boot() method of your AppServiceProvider.php located in app\Providers :

Above code will set the default length of a string column to 191. Since Laravel comes with utf8mb4 charset as default and it’s max length of a unique key is 191. If it exceeds the limit then Laravel will generate an error and won’t run migrations. If you want to specify a different charset then you can set it in database.php config file located in config directory. Now setup the database and add the database credentials to .env file and run migrate command which will create all the necessary tables used for authentication:

php artisan migrate

and generate authentication scaffolding:

php artisan make:auth

Instead of using a virtual host with apache, we will use Laravel dev server with (for localhost):

php artisan serve

Open your chrome and paste the url you see in your terminal. (I don’t suggest using browsers other than chrome or firefox).Image for post

Image for post

Subscribing a User

To start with our notifications, we will need to subscribe a user from the browser and we can do that with javascript. There will be two steps involved in subscribing a user which are,

  • Getting the permission from the user.

  • Getting the PushSubscription from that browser and sending it to server.

Before subscribing a user, we will need to generate application server keys known as VAPID Keys. VAPID Keys are public and private keys used for recognizing a user and these keys ensure that it’s the same server triggering the push messages to users. There are a couple of ways to generate these keys, you can visit this site web-push-codelab.glitch.me to generate these keys or install web-push cli with npm to generate them from your command line:

$ npm install -g web-push
$ web-push generate-vapid-keys

Since we are using a Laravel package called Webpush, we can generate these keys with an artisan command. Webpush libraries are also available for other languages, see this link for a list all libraries. Let’s install the package:

composer require laravel-notification-channels/webpush

Laravel 5.6 or above uses package discovery feature, so we don’t need to add the package provider. Let’s add the HasPushSubscription trait to the User model:

After adding the trait we will need to publish the migration that will create the push_subscriptions table:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="migrations"

When you will run the migrate command it will generate an error because of charset saying the specified key is too long, so we will add a different charset for this table and add a different length for endpoint column. Let’s update the up() method:

You can also publish the config file (Which we won’t use):

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="config"

Now we can generate the VAPID Keys:

php artisan webpush:vapid

This command will generate VAPID public and private keys and store them to .env file.

Let’s create a Service Worker file in public directory named sw.js and a javascript file named enable-push.js in public/js directory. Add the script right before the </body> ending tag in app.blade.php layout file located in resources/views/layouts :

It will include the script only if we are autheticated.

Let’s start working on the client side by opening the enable-push.js file. We will register our service worker by defining a function named initSW() :

In above function, we are first checking if service workers and push are supported, after that we are registering our service worker which is outside one directory (you don’t have to check for push here, this will stop the registration of service worker but we are using service worker for the sole purpose of push, so we will keep it there.). Inside then() we are calling initPush() which will be responsible for subscribing a user. Don’t forget to call the initSW() in our enable-push.js. Just push this line at the top of the script:

initSW();

Let’s define initPush() after initSW() :

Inside initPush() we are requesting user permission and if the user clicks allow then we will be able to get PushSubscription. After we are granted permission, we are calling subscribeUser() which will subscribe the user and send the PushSubscrption to our server. Let’s define subscribeUser() :

navigator.serviceWorker.ready is used to check if a service worker is registered and active. Inside then() we are first creating an object subscriptionOptions which will have our VAPID public key and userVisibleOnly option. Use the public key that you have generated from the above command. Setting userVisibleOnly to true will not allow silent push that happen in background without letting users know (if not defined then you will get an error, read more). urlBase64ToUint8Array() function will convert our public key to appropriate format. After subscribing. it will return a PushSubscription which we can send it to the server. Inside the second then(), we are sending the PushSubscription to our server with storePushSubscription() function. Before defining it we should define urlBase64ToUint8Array() :

Our storePushSubscription() looks like this:

Above function is making a POST request to /push route. We are getting the CSRF Token from a meta tag which is important for our ajax request. That meta tag was added when we had created authentication scaffolding.

Storing PushSubscription to the database

Let’s start with our routes (add it in your web.php not api.php because we need to access the authenticated user):

Let’s create our PushController :

php artisan make:controller PushController

Now open the PushController and add the store method:

We can use updatePushSubscription() to create or update a PushSubscriber (because we added that trait to our User model). If it was successful, we will get a success message on console. Note that whenever you visit / route you will not see a “service worker installed” message because we have added the script in app layout file which is not used by welcome view. Now refresh the page and you will see a permission box:Image for postImage for post

When you click Allow, you will see the PushSubscription and the success message in console:Image for postImage for post

Now we need to create a Notification in Laravel to send Push Notifications :

php artisan make:notification PushDemo

Open the notification class which is located in app/Notifcations directory and add this code:

Inside toWebPush() method, we have a notification title for the title of our notification, body will be the extra text, icon is the image displayed on a notification and action will display a button with notification (you can add more options and learn more about these options). Now we can send notifications using Notification::send() from our controller. Let’s define a push method inside our controller:

add a new route in web.php:

We will need a button to send notifications so let’s add it in our home.blade.php view. Place it inside the div with a class of card-body :

Now the final step is to add an event listener that will listen to the push event in our service worker file sw.js located in public directory:

Inside the push event listener, we are first checking if Notifications are allowed and supported, after that we are accessing the notification data from e object with e.data and converting it to json with json() method. In service workers, waitUntil() is used to tell the browser that a task is running and should not be terminated. we can display a notification with serviceWorkerRegistration.showNotification(title,options). Make sure you check the update on reload option inside the application tab in dev tools.Image for postImage for post

Now we can test it by click that button.Image for postImage for post

Ever browser and operating system has it’s own way of displaying a notification. I’m using chrome on ubuntu. Chrome does not show the icon while Firefox does.Image for postImage for post

Notifications For Guest Users (New)

Please read the above tutorial before implementing guest notifications

Most of the times we want all the visitors of our website to receive push notifications so let’s work on that. First we need to update the migrations and create some new migrations. Open push_subscriptions migration and update user_id to guest_id:

Remove the foreign key constraint:

Let's create a Guest Model and it's migration that will relate to the push subscription:

php artisan make:model Guest -m

Inside the guests table migration we have a unique column endpoint that will be used to identify each push subscription:

We need another migration that will create a foreign key constraint between these tables:

php artisan make:migration add_foreign_to_guest_id_in_push_subscriptions_table --table=push_subscriptions

Open the migration and add the following foreign constraint inside the up() method:

Create all the tables with migrations command. Now open Guest model and add the following:

We are importing Notifiable trait so we can use Laravel notifications (it’s already imported in User model). Overriding pushSubscriptionBelongsToUser() method is important since we are not using authenticated user.

Last step will be to modify our PushController so open the controller and first replace all the User model usages with Guest model. After that replace the below line inside the store() method:

With:

Of course you to need to remove the @auth directive that blocks the inclusion of our enable-push.js script and we are done.

You might want to deploy it to heroku which has a free version with https ( if you want to test it on mobile devices). As far as the UX goes, it might be a good idea to create something like a button which will trigger the initPush(). It’s just not a good idea to ask for notification permission right from the start when our users don’t know what kind of notifications they are receiving.

Above was just a basic example of using Push notifications. You can dig deeper if you want at google developers docs.

Thank you for reading this tutorial and feel free to comment if you get stuck. You can find the Source Code / Github repo HERE. Branch for guest notifications can be found be HERE.

Please note that <?php in code samples is only used for syntax highlighting

Last updated