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

Handle Video End Event0:40

maybe sprinkle in a bit of jQuery. In the next video, though, if you prefer using Vue, then we will review that workflow. Okay, let's get started. Now, if we scroll down, let's clean up just a bit. We can get rid of that. And if we uncomment this, we're going to say when the video has ended, then trigger this callback function, and we'll say it is over. Okay, let's switch back to Chrome, give it a refresh. I'm going to turn off the volume here. And if I click at the very bottom, we should see an alert, and we do. Okay, so we are successfully listening for when you finish a video. Next, we're going to submit an AJAX request. And we'll just use jQuery for this, makes it nice and easy. So why don't we say jQuery.ajax, and then we can set the type of request. So like if it's a POST, or a PUT, or a PATCH, or a DELETE, you could reference that here.

Plan AJAX Completion Post1:20

makes it nice and easy. So why don't we say jQuery.ajax, and then we can set the type of request. So like if it's a POST, or a PUT, or a PATCH, or a DELETE, you could reference that here. Maybe just POST for now. The URL we are posting to, we don't know yet, maybe API/completions, or maybe just /completions. So many different ways you could construct these URLs. Finally, if there is any data, and I assume there will be, we can send that through here. Okay, we'll get to that in a bit. So now, I'm using a Laravel app here. If you don't use Laravel, that's okay, just come along for the ride. So we're going to say route when we POST to this new completions resource, I'm going to hit a CompletionsController at store for now. So let's set that up, php artisan make:controller CompletionsController. All right, let's switch over. And then down here, I'll have a store method.

Create Completions Backend2:03

completions controller at store for now. So let's set that up, php artisan make:controller, CompletionsController. All right, let's switch over. And then down here, I'll have a store method where we are storing a new completion. So we can see this is a new term for our business. The fact that a User can complete a video, well, we have a CompletionsController because that's an important thing to our business in this case. Now, this will accept the request. And presumably, we can fetch, for example, the ID of the lesson or the content that we are completing through here. Now, next on that note, we need some kind of table, right? So let's say php artisan make:migration called create_completions_table. And we are creating a table called completions. Okay, so if I switch over there, here we go. Here's our schema. And what are we going to have here, we're going to have an integer for the userID who completed the lesson.

Okay, so if I switch over there, here we go. Here's our schema. And what are we going to have here, we're going to have an integer for the user_id who completed the lesson. And then next, we're going to have an integer for the lesson that was completed. Or the content that was completed. Maybe we'll call it video in this case. So yeah, it's a simple pivot or a simple lookup table. If you want to figure out if the User with an ID of five completed the video with an ID of 10, then this is the table you would refer to. Okay, so if I switch to my migrations folder, yeah, we have a completions table and a users table. There's also a passwords table, but we don't care about that in this case. And in fact, I will delete that entirely. Alright, so I can't run php artisan migrate just yet, because it's a fresh and solid layer of hell. And we don't even have our database settings configured. So I'm going to go

Configure Database and Migrate3:38

I will delete that entirely. Alright, so I can't run php artisan migrate just yet, because it's a fresh and solid layer of hell. And we don't even have our database settings configured. So I'm going to go to my .env file, we're going to use SQLite, which means I can get rid of all of this junk here. Next, I'm going to touch database. Basically creating an empty file. And yeah, now if I php artisan migrate, it should work. Cool. So at this point, we have a users table and a new completions table. Why don't we whip up a quick User, I'll use database factories here. App\User, the Learicast namespace is Learicast, but everything else will default to App. Okay, so Ashley Anderson is now in our system. And if we fetch that, there she is. So next, if we've decided that we have maybe videos here, that's our content type, why don't we create that as well. php artisan make:model Video --migration. That's what

if we've decided that we have maybe videos here, that's our content type, why don't we create that as well. php artisan make:model Video -m. And I want the migration as well. That's what -m will do for you, it will create a migration in addition to the model. So if you take a look, here's our Video model. And then we should have a create_videos_table as well. Okay, so we have the video_id. And we're just going to cheat for now we're going to say a string for the title. And then maybe a string for the source. Maybe we'll just keep it like that. Yeah. Okay, php artisan migrate. And now we have a videos table. So imagine this, once again, we're going to whip up a Video, new App\Video(). And I don't have a factory for this. So we'll just set it to myFirstVideo. videoSource will be some URL, usually the URL to the video, but I don't have one yet. And that's okay. Finally, I will save the video.

to my first video. Video source will be some URL, usually the URL to the video, but I don't have one yet. And that's okay. Finally, I will save the video. Okay, so now, yeah, we have one User, and we have one video. So at this point, I'd like to say that the User actually has completed this lesson. Well, if we do that manually, we would just do something like this, DB::table('completions')->insert([]);, and then we would say the user_id is 1. And then the video_id is 1. And there we go. So now if we were to fetch all completions, we have our lookup, right? The User with an ID of 1 completed the video with an ID of 1. And then if they completed the next video, you would add a new record in here that says the User with an ID of 1 completed the video with an ID of 2. That's basic relational databases. Now let's do one more thing before we switch back to our JavaScript, which is what we really care.

Add User Completion Relationship6:13

User with an ID of one completed the Video with an ID of two. That's basic relational databases. Now let's do one more thing before we switch back to our JavaScript, which is what we really care about for this series. I'm going to truncate this. Okay, so I've just cleared out the table. Rather than doing this, I'd rather put that on the model or really anywhere you want. Let's do it right here. User, we're going to complete a Video. Now I figure you have two options. One, yeah, if you want to, you can always do stuff like this, insert, and then you just manually reference it. You don't always have to use the eloquent relationships if you don't want to. So you could do something like this. videoID is videoID. Yeah, I think that's fine in a lot of situations. And honestly, when you come back to the project a year from now, I think this is easier to read versus sometimes those eloquent, I don't know about you, but sometimes those eloquent

of situations. And honestly, when you come back to the project a year from now, I think this is easier to read versus sometimes those Eloquent, I don't know about you, but sometimes those Eloquent relationships, especially some of the more polymorphic ones, they're pretty confusing when you're trying to figure out like, all right, which key does this argument refer to? And what is the fourth argument? And it just gets kind of confusing to me. But anyways, you could do this. Or if you did want to reference the relationship, well, for a pivot table, this would be a mini to mini. So for example, a User can have many Completions. So let's do this. Completions would be return this belongsToMany. And I think we may have to add the second argument. We'll see if we hit an error, but I think we do because the name would be different. Anyways, here you would say this completions attach the videoId, or you can always just

We'll see if we hit an error, but I think we do because the name would be different. Anyways, here you would say this completions attach the video ID, or you can always just pass the model itself. Okay, so let's try it out. Let's reboot Tinker up, find the User. And now I will say $user complete any video, we'll just grab the first one. And yeah, we will have to add another argument here, like I thought. So here's how it works. The convention for a pivot table in Laravel is the singular form of each table, and then an alphabetical order. So if we have users and videos, well, then we would have user_video, and then alphabetical order u comes before v. That's how that works. But in our case, we call the table completions. Sometimes I like to override that. I think it's more clear what that table refers to. So if we take a look at the belongsToMany relationship,

we call the table completions. Sometimes I like to override that. I think it's more clear what that table refers to. So if we take a look at the belongsToMany relationship, the second argument is the table name. And that's what we want completions. Okay, so if I were to boot this up again, find the User, we complete the video. And that should do it. So let's try it. Let's grab all completions. And there we go, we get the same thing as we had before. But now yeah, our API is that much nicer to work with User complete the video. Now the back end is mostly there. The only remaining thing would be to update our CompletionsController and say here, first, let's find the video. So you could do something like $video = Video::findOrFail(using the video ID from the request). Right, then you could say $this->user->complete the video. And then finally, if we want to lock this down, so in order to interact

like this video, find or fail using the video ID from the request. Right, then you could say this User complete the video. And then finally, if we want to lock this down, so in order to interact with any of these endpoints, you have to be authenticated. So we could say this middleware auth, and that will apply to every single route here. All right, does this all make sense? So when you submit an AJAX request to this endpoint, it's going to hit a method called store. The store method requires you to be authenticated. So if you're a guest, it's going to redirect you to a login page that you would have in real life. Otherwise, we will accept the request data, we will find the video in question, and we will complete the video. Now in reality, we might want to do a couple extra things just to make sure that we don't end up with duplicates. One option would be, and we're not going to go into this, but one option would be to make this

we might want to do a couple extra things just to make sure that we don't end up with duplicates. One option would be, and we're not going to go into this, but one option would be to make this a dual primary key. So you could do something like user_id and the video_id. And that way, you can never have a User linked to the same Video more than once. That would be an option. Another option would be to check to see if we have a Completion. And if so, maybe you want to toggle it instead. For example, this is kind of what we do at Laracasts. So for example, if you click this, we register, I think I call this a vote in the system. But if you click it again, it just toggles it. So it exists in the votes table. So that means you want to undo it. So we toggle the existence of that record. But yeah, we've spent too much time on the back end. We really want to get back to video.js and our JavaScript. So this is fine to start. The only

Send AJAX and Debug10:58

toggle the existence of that record. But yeah, we've spent too much time on the back end. We really want to get back to video.js and our JavaScript. So this is fine to start. The only thing I will do is just sneak an authenticated user. So log in using ID. This is just so we don't have to manually create a login form and register a user. I will artificially do that. All right. So if we go back to main.js, we're back to our JavaScript. When the video has ended, submit an AJAX request to /completions and the video ID. To start, I'm just going to hardcode it. Set it to 1. Then don't forget in your CompletionsController, we find that video, track it down, and then complete it. Why don't we try it out? And real quick, if we fetch all of our completions, we still have 1. Let's truncate the table so that I can start from scratch. Cool. So nothing in that table. Now, why don't we try this out? But first, really quick, don't

our completions, we still have one. Let's truncate the table so that I can start from scratch. Cool. So nothing in that table. Now, why don't we try this out? But first, really quick, don't forget that on our main.js page, we are using jQuery here. So let's make sure that we import that. And like I said, later, maybe in the next lesson, if you prefer to use NPM, then we can review that approach. So anyways, we're going to give this a refresh and see how it turns out. So if we go to the network tab, you should see the AJAX request here. But if you didn't, just give it a refresh and then try it again. So like, if we play it again, I'm going to go to the end. We should see it now. Yeah. So we can see a 500 error was returned, and we can see that the AJAX request has ended. Cool. So let's go back to our main.js page, and let's I'm going to go to the end. We should see it now. Yeah. So we can see a 500 error was returned,

see that the AJAX request has ended. Cool. So let's go back to our main.js page, and let's I'm going to go to the end. We should see it now. Yeah. So we can see a 500 error was returned, which usually means you made a mistake. So sure enough, yeah, we forgot to import the model. Okay. CompletionsController. And we're referencing video, but we didn't import it. Use app\Video. Next, you can see I'm referencing this user. You could put that, that's a little trick, you could put that on even the constructor of your MainController. So you could assign the user equal to auth()->user(). That can be useful in a lot of cases. You could even do things like signedIn. That way you have maybe a nicer way to interact in your controllers. You could even share them. So you could say signedIn equals auth()->user(). And that will either be the signed in user or null. Lots of cool stuff you can do there. Or if you want, yeah, as always, just say

share them. So you could say $signedIn equals auth()->user(). And that will either be the signed in user or null. Lots of cool stuff you can do there. Or if you want, yeah, as always, just say either the facade or the little global helper function. Like so. Anyways, if we come back to Chrome, let's give it another shot. So refresh, play the video, and then we'll go to the very end. Okay. Let's see what happened. So we got a 200 response. And in this case, we didn't return anything from the request, but that's okay. So if I fetch completions, sure enough, you can see it. So what exactly did we accomplish there? Oh, and actually real quick, before we get to that, notice how the timestamps aren't populated. You can add, if we go to our User model, you can say belongsToMany with timestamps. And that's because by default, yeah, it just includes the model keys. So if you want timestamps, you have to opt into that.

you can say belongsToMany with timestamps. And that's because by default, yeah, it just includes the model keys. So if you want timestamps, you have to opt into that. Anyways, let me just show you this real quick to confirm. Okay. Back to Chrome, give it a refresh. Down at the bottom, complete the video. We submit the AJAX request. And now if we fetch it, you can see the timestamps have been added there. So that's a good thing to know. Anyways, so what was the question? What did we accomplish here? Well, we pulled in Video.js, we learned how to listen for the ended event. So when the video has completed, we want to submit an AJAX request. So that's exactly what we did. In main.js, very simple. Once the video has completed, submit an AJAX request to this endpoint and send through the following data. Now, as for the videoID, yeah, once again, depending upon the library or framework

Once the video has completed, submit an AJAX request to this endpoint and send through the following data. Now, as for the video ID, yeah, once again, depending upon the library or framework you use, you can fetch that in a number of ways. If you're using simple jQuery, maybe that's stored on the video itself. And you want to find something like this on a data attribute. Or maybe it's in the address bar and you want to fetch it from the URI. That's all just kind of down to how you have built your application. If you use Vue.js, maybe you have it as a property on a component. Tons of different options there. And I will let you figure that part out on your own. Anyways, once you submit the AJAX request, yeah, on your server, just receive it. So when we hit completions, we're going to fetch the authenticated User and ask them to complete the video associated with the request. So some homework for you. Right now, don't forget, this user has

completions, we're going to fetch the authenticated User and ask them to complete the video associated with the request. So some homework for you. Right now, don't forget, this User has completed the video with an ID of one. However, currently, if they watch it again, you're going to end up with a second row. All right, so that AJAX request was submitted. And now you have two. And this isn't what you want. You don't want two instances of the same User completing the same video. So your job, if you accept it, is to figure out how can you prevent this from happening. Do you want a composite primary key? Do you want to check for the record's existence first? You decide for yourself and let us know what you come up with.

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