تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Push Subscription Overview0:00

The next step in this whole push notification process is for the client to subscribe to our application's push notifications. There's a lot involved, so let's just get started. Push notifications are, well, they're really cool, but of course there's a whole lot of things going on behind the scenes that make it work. There's a whole specification involved, and we're not going to go into the detail of the specification, but it's useful to understand how at least some of it works. So our layer of all application is of course going to send push notifications, but it doesn't send it straight to the devices or the browsers. That would be impossible. Instead, there is a push service that works as an intermediary. It's not ours. We don't control it, but every browser that supports push notifications has its own push service. So our Laravel application is going to send push notifications to the push service,

Generating VAPID Keys1:00

We don't control it, but every browser that supports push notifications has its own push service. So our Laravel application is going to send push notifications to the push service, which will then send those notifications to the browsers. And in order for our Laravel application to know where to send those notifications, the browser is going to subscribe to our notifications. And in doing so, it's going to tell our Laravel application everything that it needs to know to send push notifications. As I said, there's a lot involved, so we're just going to dive right in. So the very first thing that we need is to set up some keys for our Laravel application, because our Laravel application just can't send push notifications willy-nilly to the push service. Our application has to be authenticated. So we need what are called VAPID keys. Now this stands for voluntary application server identification.

willy-nilly to the push service. Our application has to be authenticated. So we need what are called VAPID keys. Now this stands for voluntary application server identification. Voluntary application. There is no S here. I think it's funny. VAPID, or at least voluntary application identification, or I'm getting confused. Voluntary application server identification is not VAPID. There's no S here. But we need to create some VAPID keys, some VAPID keys. And we could do this in a variety of different ways, but there are some, you know, very specific things that these keys need. And there are many tools out there that are specifically for creating VAPID keys. But what we are going to use is a package called minishlink slash web push. This is going to give us everything that we need to create these VAPID keys. But it's also going to do all of the hard stuff when it comes to actually sending push

minishlink slash web push. This is going to give us everything that we need to create these VAPID keys. But it's also going to do all of the hard stuff when it comes to actually sending push notifications, because there's encryption and all that stuff. This is just going to make it easy. So we want to install this package. And then once we have it installed, we can run Tynker. So that we can say minishlink web push slash VAPID. And there's a create VAPID keys method. And this is going to give us a public and a private key. Now, if you're familiar with public keys, you are going to see that this isn't a valid Base64 string. For one, a valid Base64 string needs to be in multiples of four. And if we counted this out, it's probably not going to. But there's also underscores, which shouldn't be there. There's also dashes. So this is what we are essentially going to call a URL encoded Base64 string, which is important because we are going

Configuring Push Keys3:58

But there's also underscores, which shouldn't be there. There's also dashes. So this is what we are essentially going to call a URL encoded Base64 string, which is important because we are going to have to convert that into a valid Base64 string so that we can then convert it into a byte array. Fun. As I said, there's a lot that we got to do. So we want these two keys, and these are for our Laravel application. So if we open up our .env file, then we can just add these right here. Because really, especially a private key, we want to keep this as private as possible. So let's call this VapidPublicKey, and we will also have a VapidPrivateKey. But we also need a third value, which we'll call VapidSubject, which we'll talk about here in a moment. So our public key is going to be this right here. So let's copy that. We'll paste it in. And voila. Now we need our private key, which we can just copy that and paste it in. And then it comes time for our subject. Now,

to be this right here. So let's copy that. We'll paste it in. And voila. Now we need our private key, which we can just copy that and paste it in. And then it comes time for our subject. Now, when it comes to actually sending the notifications, we need to include a subject. And basically, it's just contact information. It could be a URL, so that it could go to a URL on our application. Or it could be a mail to, which, let's do that. And of course, this doesn't really exist. But for a real application that was going to be published, this would definitely need to exist. So what was this? This is fieldslogger.test. Yes, I believe so. So that's going to be our subject. Now, just because we have these environment variables, it would be useful to have these in our config. So let's terminate that batch job, so that we can use Artisan to make a config. And we'll just call it push, so that we can open up the push.php file. And here,

in our config. So let's terminate that batch job, so that we can use Artisan to make a config. And we'll just call it push, so that we can open up the push.php file. And here, we'll have our Vapid configuration. So that the first thing will be our public key, which we will get from our environment called Vapid public key. Then we want the same thing for the private key, except that this needs to be the private key environment variable. And then we have our subject, which will be our Vapid subject. And then once we have this in our config, we can reference this wherever we need to. And in fact, we want to essentially smuggle our public key into our page, because when it comes time to subscribing, we need to do that through JavaScript, which means this needs to be available there. So let's go to our layout page. And I think let's just add a meta tag, where the name is going to be Vapid public key.

Creating Subscription Model7:01

do that through JavaScript, which means this needs to be available there. So let's go to our layout page. And I think let's just add a meta tag, where the name is going to be Vapid public key. The content will be from our config. So we'll just call config, and we'll push Vapid public key. Then we will have that value. Since we're here, we are also going to need the CSRF token, because whenever we subscribe, we're going to store that information in our database, which means we're going to need to make a POST request. So let's have the CSRF token there, and all we need to do is call CSRF token function. So we have our Vapid key, we have our CSRF token, so that we can refer to those inside of our JavaScript, which is great. That comes here in a little bit. For right now, we need to create a model, because when the browser subscribes, it's going to tell our Laravel application where it needs to send those push notifications,

For right now, we need to create a model, because when the browser subscribes, it's going to tell our Laravel application where it needs to send those push notifications, which means that we need to store it in the database, and it's going to be bound to a user. But a user can have as many devices as they need, so we're going to have a one-to-many relationship here. But we're going to use Artisan to make a model, let's call it push subscription. We want the migration so that we can open up the create push subscriptions table. All right, of course, we want the ID and the timestamps, but we do want the foreign ID of user ID, but we want this to be constrained. And let's also just cascade on delete, because if we delete a user, we just need to delete all of the subscriptions for that user, just makes perfect sense. So we have our foreign ID, the next thing that we need

because if we delete a user, we just need to delete all of the subscriptions for that user, just makes perfect sense. So we have our foreign ID, the next thing that we need is a string for the endpoint. And this is going to be unique, because every device is going to have its own subscription. Well, not every device can have its own subscription, which means it has its own unique endpoint. But that's not everything that we need. We also need two keys that are going to be used to encrypt the messages. And this is coming from the browser. The browser is going to provide our Laravel application everything that it needs. So this is going to be a key, it's the browser's public key. And then we will have a private key coming from the browser for encrypting that information. And that's really all that we need. So we have the user ID, we have the device, I'm going to call it device, it's the browser,

coming from the browser for encrypting that information. And that's really all that we need. So we have the user ID, we have the device, I'm going to call it device, it's the browser, but the browser is on individual devices. So I'm going to call it device. And then we have the keys for encrypting the information. So we are good to go there. So right now we can go ahead and let's migrate this. I'm just going to run fresh and seed because, you know, why not? Let's start completely over from scratch. And that's it as far as our migration is concerned. But we do need to open up our user model as well as the push subscription model, because here we need the protected fillable array because we want these things to be fillable. So we will have the user ID, we will have the endpoint, then we will have the P256DH, and then we will have the auth. Now, I'm not, you know, just making these things up. There is a whole specification

the user ID, we will have the endpoint, then we will have the P256DH, and then we will have the auth. Now, I'm not, you know, just making these things up. There is a whole specification involved with this. So when the browser sends its subscription information, it's going to have P256DH. And in fact, it's going to be keys.P256DH, and then keys.auth. But this is going to be fine here. Then we want to set up the relationship so that we will have our user, which will be a belongs to relationship, so that we will return this belongs to and user class. So there is our push subscription model. So now for our user model, we just need to add the public function push subscriptions, which is a has many relationship. And we will simply return this has many push subscription class. Okay. So that's our model. We are good to go there. So now we just need, well, I say just, we need a lot of things. We need a controller.

Building Subscription Controller12:02

has many push subscription class. Okay. So that's our model. We are good to go there. So now we just need, well, I say just, we need a lot of things. We need a controller. So let's make a controller called push subscription controller. And this is going to have two methods. This is what the browser is going to send the subscription information to. So let's have push subscription controller. And we want a method called store because we want to store the subscription information. We can also go ahead and define the destroy method so that we can destroy any subscription information that we would want. So when it comes to storing our subscription information, we need to first of all, validate and to make sure that we have the information that we need, which is going to be the end point. It's going to be the keys that we just talked about whenever we created our migration in our model. So let's do this.

the information that we need, which is going to be the end point. It's going to be the keys that we just talked about whenever we created our migration in our model. So let's do this. We will say data equals request, and we are going to validate all of these things. So we want to validate in points because, well, first of all, it's required. Every one of these are required. Every one of these are a string. So let's just copy that and paste it a couple of times. The next thing that we need is keys.p256.dh. Once again, that's required and it's a string. Then we have keys.auth. Once again, required and a string. And once we have that, then it's just a matter of storing this inside of the database. So we could say push subscription, and we will update or create our stuff. But our end point, that is unique. So we will get the end point from our data, store that there. But then we, of course,

and we will update or create our stuff. But our end point, that is unique. So we will get the end point from our data, store that there. But then we, of course, need to store the other things such as the user ID, which is going to come from our user, and we will have the ID there. But then we have the p256.dh, which is going to come from our data, keys, and then p256.dh. And then finally, we have the auth, which is a lot like the other thing, except we just need to make the necessary changes. Okay. So we are updating or creating a new record. The end point is unique, the user ID and everything there. So we're good. And then we just need to return something. So if everything goes okay, then let's just return response, and it'll be a JSON structure that's, let's say, status. Okay. I mean, because really, we aren't hitting this to return HTML or anything like that. The browser is not the browser. Our

response, and it'll be a JSON structure that's, let's say, status. Okay. I mean, because really, we aren't hitting this to return HTML or anything like that. The browser is not the browser. Our JavaScript is going to do this part, but the browser is going to get the subscription information so that we can send this to the server. In which case, yeah, that's going to be fine. So when it comes to destroying, we essentially want to kind of do the same thing, at least as far as validation is concerned. And really the only thing that we need to validate is the end point, because that's the only thing that matters here, because the end point is unique. So we will validate the end point, and then we will get the user so that we can get to the push subscriptions where the end point is data end point. And then we just want to delete that. And then once again, we'll just return a response that is a JSON structure that will say status.

push subscriptions where the end point is data end point. And then we just want to delete that. And then once again, we'll just return a response that is a JSON structure that will say status. Okay. I think I typed everything okay. There's a lot involved in this. And if we get to the end of this and we test it and it works, I'm going to be happy. So before we do anything else, let's go ahead and let's open up our routes so that we can define our routes here. And we want to only do this for authenticated users. So we're going to do it inside of where our routes are protected with the auth middleware. We will have a post request. We'll call this push subscriptions. And that is what? The push subscription controller class. The method is store. And let's give this a name of push subscriptions store. Then let's just copy paste it for the delete. Of course, this is going to be a little bit different. The name will be destroy.

Implementing Client Subscription16:54

is store. And let's give this a name of push subscriptions store. Then let's just copy paste it for the delete. Of course, this is going to be a little bit different. The name will be destroy. If you type destroy correctly, and then the method will be destroy. So we have our routes set up. We have our controller set up fingers crossed. So now the rest of the work is going to be inside of what do we call it? Our notification banner, Alpine components. We also need to open up the blade component because we're going to make some changes here. They aren't going to be very much, but there are some changes. So the first thing that we need to do, we are tracking the state of permission. We also need to track the state as to whether or not we are subscribed. So we're going to initialize that as false, which means that when it comes to our notification, you know, we are only showing the banner if the state is not granted, but we also want to show it

going to initialize that as false, which means that when it comes to our notification, you know, we are only showing the banner if the state is not granted, but we also want to show it if we are not subscribed, because if we have permission, but we aren't subscribed, we still need to show the banner so that we can try to subscribe. So that's going to fix that there. And then it's just a matter of doing things that are specific to our subscription. So let's see. We are checking permission right now. So we get the permission. If it's denied, then you blocked notifications and all of that stuff. Let's say that if the state is not equal to granted, then there's really nothing that we need to do as far as checking the subscription is concerned, because our service worker is going to know if it is subscribed or not. So if the permission is not granted, then we just need to return because otherwise we don't

is concerned, because our service worker is going to know if it is subscribed or not. So if the permission is not granted, then we just need to return because otherwise we don't need to check to see if the service worker is subscribed. So now we need to check on our service worker because our service worker knows if we are subscribed, but we can only check if we are subscribed if the service worker is ready. So we're going to do this. We want the registration of our service worker and we will await navigator service worker ready. This is basically going to pause. And whenever the service worker is ready, then we get the registration for that service worker. This is asynchronous. So we get the registration for our service worker and then we check to see if there is an existing subscription, in which case we once again, await registration, push manager, get subscription. And then all we need to do right

we check to see if there is an existing subscription, in which case we once again, await registration, push manager, get subscription. And then all we need to do right now is set our subscribed property equal to existing. We want a Boolean value here because existing is an object. That's not going to give us what we need. We want a Boolean for subscribed. So we are kind of converting that. And okay, now we need to actually subscribe. So if we have granted access, we subscribe, which is going to be done with the subscribe method. And there are a lot of things that can go wrong. So we're going to wrap this with a try and let's do this. If we catch an error, then we will write it out to the console. Then we will set our message equal to unable to subscribe to push. When it comes to subscribing, we essentially want to start off in the same way that we did right here. We want to wait for the service worker to be ready

unable to subscribe to push. When it comes to subscribing, we essentially want to start off in the same way that we did right here. We want to wait for the service worker to be ready so that we can get any existing subscriptions. Because if we are already subscribed, there's literally nothing that we need to do. So if we have an existing subscription, then this subscribed is going to be true. And then we return because there is nothing that we need to do. However, if we make it past this, we of course want to subscribe. And you know what, let's also do this. Even though the message shouldn't be displayed, I would like to have a message here. And we could say already subscribed, just so that if we ever decide to show the banner still, we could see our message and that could give us a hint as to what's going on if something is going on. Okay. So if we have a subscription, then great, we exit. Otherwise we need to subscribe.

we could see our message and that could give us a hint as to what's going on if something is going on. Okay. So if we have a subscription, then great, we exit. Otherwise we need to subscribe. But the first thing we need to get is our vapid key. So we will query selector meta, and we want it to where the attribute was, what name equals vapid public key. I think that's what we called it. If it doesn't work, we'll find out. And you know, we should always have that, but let's just be careful and then get attributes content. So this is going to get that URL encoded public key that we need to convert into a valid base 64 string, which we then need to convert into a byte array. So how do we do that? Very, very carefully. We're just going to call this application key. And there's just nothing built in. And I wish that there was, because we have this whole push notification thing. You know, this is a specification that's built into the browsers,

application key. And there's just nothing built in. And I wish that there was, because we have this whole push notification thing. You know, this is a specification that's built into the browsers, give us a built-in way of converting this vapid public key into a byte array, but unfortunately it doesn't exist. We have to write it or we can copy and paste it because it's something that is used often. You can find this anywhere. Basically I'm pasting this in. There's no sense in typing this out because as I said, it's available everywhere. So we're going to have this function called URL base 64 to uint8 array. It's a mouthful, but it at least, you know, tells you what's going on here. So it's going to accept our URL encoded base 64 string. It's going to pad it so that the amount of characters has, you know, a multiple of four. It's going to replace all of the dashes with plus signs and the underscores with slashes. Then it's going to convert everything into an

amount of characters has, you know, a multiple of four. It's going to replace all of the dashes with plus signs and the underscores with slashes. Then it's going to convert everything into an array of bytes, which we can then send to the subscription. And the reason why this is important is because this is essentially going to set up the subscription so that our Laravel application can send push requests. So we call URL base 64, blah, blah, blah. We pass in the vapid key, and then that gives us the byte array that we will then use to subscribe. So here is where we create our subscription. We await registration, push manager, then we call the subscribe method, and we pass in two values. The first is called user visible only. This is part of the specification. And then we include, and then we include the application key. And that's, that's going to be it. This is going to be our subscription so that we should then have an object that we will use to

And then we include, and then we include the application key. And that's, that's going to be it. This is going to be our subscription so that we should then have an object that we will use to store our subscription in our database. So this subscription object is going to have the endpoint. It's going to have those two keys that we needed. And we are going to store that in the database, link it to the currently signed in user so that then if we make it this far, we can say subscribed is true. And then we can set a message that says push subscription saved. So that is the subscribe method. So now we just need to write this store subscription method, which I'm going to do up here. So we have our subscription here that we want to store. And the first thing we need is the CSRF token, because we want to make a post request. So we are going to query selector. Once again, we want a meta tag, but the name in this case is CSRF token. And let's be good

is the CSRF token, because we want to make a post request. So we are going to query selector. Once again, we want a meta tag, but the name in this case is CSRF token. And let's be good programmers. And, you know, maybe that doesn't exist so that we will then get attributes. We'll get the content so that we have our CSRF token so that now we can fetch. It was push subscriptions. And then our method is going to be post. Then we're going to have three headers. The first is going to be content type. And that is going to be application slash Jason. Then we have the except header, which is going to be application slash Jason. And then we want the X CSRF token header. And that's going to be our CSRF token value. OK, so we have our method. We have our headers. Next thing we need, let's include the credentials. And then we have our body, which we're just going to stringify the subscription information. So we want to await

We have our headers. Next thing we need, let's include the credentials. And then we have our body, which we're just going to stringify the subscription information. So we want to await that. And we could check, you know, to see if that works. We could wrap it in a try and catch. But whenever we call this, this is going to be inside of a try and catch anyway. So let's just leave it like this. Everything else should be OK, except that when it comes to pressing our button, we don't want to just request. We want to set up push notification, although let's make it plural. Set up push notifications because we want to request, first of all. And if that is granted, then we want to subscribe. So let's define that async set up push notifications, in which case we await this request. If the this state is equal to granted, then we call the subscribe method. I think that's it.

Debugging Subscription Flow27:24

async set up push notifications, in which case we await this request. If the this state is equal to granted, then we call the subscribe method. I think that's it. So we've we've got the Laravel side done. We have our routes. We have the JavaScript side done. We have our changes to our blade component. So now's the moment of truth. If I have typed everything correctly, then naturally everything is going to work. And and we see try again, which is what we want because, you know, in the previous episode, we allowed notifications. So we're OK there. So we should just try again. Unable to subscribe to push. Of course, there's an issue. So let's open up the developer tools. Let's take a look at the console. Registration failed. Missing application server key. Oh. This needs to be called application server key.

the console. Registration failed. Missing application server key. Oh. This needs to be called application server key. Details. Details are important. Let's try again. OK, so it looks like that worked, but we got an internal server error. So if we take a look at the network tab, those are a lot of health. Let's click on push subscriptions. Can we hide that? OK, so this is our payload. And I can't make that any taller. Lovely. So this is our payload whenever we send the subscription. So we see the endpoint here. We have the expiration time, which we don't care about. But there we have the keys. But let's take a look at the response. So call to undefined method, push subscription updated or create.

care about. But there we have the keys. But let's take a look at the response. So call to undefined method, push subscription updated or create. You know, in the grand scheme of things, with as much typing as this was, with as many pieces involved, I'm rather proud of myself that I didn't really just royally screw it up. OK, so this is update or create. OK, so let's do this. We want a clean slate. Let's unregister the current service worker so that we can control F5 so that we can try again. Registration failed. Push service error. So it's a query issue, which means that when it comes to updating or creating. Oh. Did I already say details? Details. Let's go to the application tab. Let's unregister the service worker so that we can control F5 and we want to try again. So we'll run through

Did I already say details? Details. Let's go to the application tab. Let's unregister the service worker so that we can control F5 and we want to try again. So we'll run through another error. What? OK, so let's take a look at the network. We need to find that right there. And it's the same error. Oh, or no, it's not. It's a SQL error. Push subscriptions has no column named P256DH. Are you sure about that? Of course, I could have done. Just wipe everything out so that we can start over so that after signing back in, let's unregister the service worker, restart, try again. OK, finally, that looked like it worked. There's no errors, but really, the moment of truth is going to be what is inside of the database. Sure enough, we have our push subscriptions.

OK, finally, that looked like it worked. There's no errors, but really, the moment of truth is going to be what is inside of the database. Sure enough, we have our push subscriptions. We have a record for our user, and that worked just fine. So now that we have an actual subscription, we can send push notifications, and we will implement that in the next step. So now that we have our push notifications, we can send push notifications, and we will implement that in the next step.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟