00:02
- So just like our modules, we're also going to want to be able to do CRUD operations for our lessons too. So let's follow a very similar approach
00:11
that we just took for our modules controller with a brand new lessons controller as well. So within our terminal, let's do node ace make controller,
00:19
and we will put this inside of our API version one folder and call it lesson hyphen A, so that we signal this as an API resource for controller,
00:28
hit enter to create that, and we go and clear that out and jump back into our text editor and into that brand new controller. In addition to the index store show update and destroy,
00:38
we also want our tag operation for this as well. So let's add in a tag method that uses the HTTP context here as well. And from this, we're going to want our programs,
00:48
request and organization. Just like everything else, the very first thing that we're gonna wanna do is authorize the token for an update operation using our organization.
00:58
Then we're going to wanna validate the data. So we'll do const data equals await request validate using to validate against the body of the request
01:07
using our lesson, and then we should have a patch tag validator there, just like we did with our module. Then we should have our with organization metadata. There we go.
01:16
We can use that method there to validate that the status and access level ID that we're passing in to this patch validator actually exists on the organization
01:26
that we're working with. Then we can go ahead and return the response of our update lesson tag action, call its handle method, pass in the ID from our programs ID,
01:36
the organization, and then that valid data. Just like with our module, this should be returning back a promise of our lesson. So by returning that out of the controller itself,
01:46
we're responding with that for the request. Cool. So to switch things up a little bit here, we can work our way up from the bottom. So let's do destroy next. In addition to our programs,
01:56
we also want our response and the organization. And then of course, we're gonna want to authorize that token for a delete operation with our organization.
02:04
We can go ahead and destroy the lesson, call the handle method, pass in the ID from the programs ID,
02:12
and then the organization itself. Once we have that, we can return our response.status to 04 for no content. All right, there's our destroy.
02:22
Let's go up to update next. In addition to our programs and requests, of course, we're also going to want that organization that we're working with, and then we'll authorize our update operation
02:32
against that organization. And then let's grab our data as await, request, validate using, and then we should have a lesson validator here as well.
02:42
And then we'll also want to use our with organization metadata as just like our tag that also works with the status and access level IDs.
02:52
So we want to make sure that those exist in the organization there. Let me go and return our update lesson action, call the handle method, and pass in the ID for the lesson
03:02
that we want to update from our programs ID. Then we also need the organization as well as that validated data. And this too will return back a promise of type lesson.
03:11
So that's our response there. For our show, we are going to go ahead and get rid of that. If you would, however, like to take this a step further,
03:19
there is a notes field on the lesson model and table that we currently aren't using anywhere inside of this application. So you could always add a lesson show page
03:27
or the ability to alter that notes field, adding maybe a markdown or WYSIWYG editor to it so that the user can plan out what their lesson actually looks like
03:37
inside of the series and module. For now though, let's go ahead and move on to our store method. And let's also grab our params and the organization out of our HTTP context,
03:47
authorize the token for a create operation with the organization, const data equals await request validate using.
03:56
And again, we want to use that lesson validator with our organization metadata, passing the organization ID into there. And then we can go ahead and return
04:05
and we should have a store lesson action we can call the handle method for and pass the organization and data into it. And if we take a look at the lesson validator type
04:15
real quick, we do see that it does take in the module ID that the lesson should be bound to. For our modules, we're using that from the route parameter, but for our lesson,
04:25
the module that the lesson belongs to comes directly from its body rather than the params. So let's go ahead and get rid of the params there and let's bring up the route next.
04:35
So let's jump into our route to V1 and we'll talk about why we have it set up this way here after we write these routes out. So let's first do our lessons resource.
04:44
So we'll do router resource and we'll just do lessons and we will use our lessons controller
04:50
from our API version one directory, mark this as API only. And then we'll also do accept show as we currently are making use of it.
04:59
Then let's go ahead and add in our patch for our tag. So patch there slash, and we'll just do lessons/id/tag, treating this as a top level resource,
05:09
the exact same as we did for our courses. And so we'll use our lessons controller there and it's tag method is lessons.tag.
05:17
Okay, so why are we treating our lessons as a top level resource, but our modules as a nested resource within our courses? Whenever I was planning this series out
05:27
and actually making the plotmycourse.app website that I actually use to plan these lessons out, I pivoted. So originally I had the module ID
05:36
as we have it inside of this application as a required field on the lesson. So the lesson must belong to a module in this context.
05:45
So if we jump into our lessons model, you'll see module ID is required as a type of number. In the real application though, this is optional. So it's number or null,
05:55
but that was a pivot that I had decided to make after we started our inertia series, which used this code base. And the reason for that pivot, if we actually jump into the application is because I decided it would be very nice
06:05
to be able to have lessons planned outside the context of a course. So we can create a lesson not bound to any course just within our organization.
06:15
And then we also have this lessons index page that just has a paginated list of all of the lessons. So that is the real reason why we're not doing this
06:23
as a nested resource within our courses, modules, lessons, but rather just having a top level lessons is because it's a little bit more flexible and it allows an approach like this,
06:33
where we can have lessons outside the confine of a course or module as a whole, just kind of freeing them up, giving you more expandability in that sense as well.
06:42
Modules though, keeping it confined to the course makes sense because the module in itself doesn't make any sense outside of a course like lessons might.
06:51
Now, if you'd like to see a refactor series where we take this code base and just kind of refactor it into being able to do this page here, let me know down in the comments. I would be happy to do so.
07:01
So, okay, back to the task at hand though, back inside of where were we? Our route definitions here. All right, cool. So we finished our route definitions up.
07:10
Everything should be all ready to go now. We should be able to jump back into Hopscotch here. It looks like my environment got deselected. So let me go ahead and select that. And what we're gonna wanna do is,
07:19
let me just do a little bit of cleanup work here and let's right click our modules, duplicate them and rename it to lessons.
07:26
So edit it and rename that to lessons. And then I'm gonna go through just as we did with the modules and rename each of these individually to lessons instead of module.
07:35
Cool, so we'll go ahead and leave get in here. We'll take care of that in the next lesson. But for now, let's go ahead and jump down to our store lessons and let's update our URL.
07:44
So all we need to do is target slash lessons as we have this as a top level entity within our route definitions. For our body, let's actually jump back
07:54
into our text editor here within our lessons controller. And let's go take a look at our lesson validator itself. So we have a name, publish at, which is tool tips out of the way here,
08:03
a UTC date time, and then a module ID, an access level ID and a status ID. So those are what we need inside of our body.
08:11
So for the name, I'm gonna do test lesson 01. I'm gonna go and just leave the status ID of three. That's fine.
08:18
Access level ID, I'm gonna take a guess that we have one called one there. And then a module ID, let's assume that we also have a one there.
08:28
And our publish at is optional. So let's just test to make sure that we have our IDs right and everything working thus far. So let's go and create that lesson. Whoops, of course, make sure that you have your server
08:37
running before you try and send that off. Okay, and there we go. Cool. So we get back our name of test lesson 01 for our lesson with a module ID of one,
08:47
access level ID of one, status ID of three. It's second within its module, went into our organization with an ID of one, and it itself has an ID of four. Then for our publish at,
08:57
if we go ahead and add that field in here, we should be able to provide any valid ISO string with a date and time on it, and that should work a okay.
09:05
So let's do 20250728 with a T of maybe 0900 hours. Let's send that off and we should get back
09:14
an additional field with a publish at there of 7/28/2025 with a time of 0900 hours. Perfect. That one has an ID of five.
09:24
So if we jump down, let's go and save that one first, then jump down to our update lesson. Again, we can get rid of the courses IDs and let's just target a specific lesson ID there.
09:34
Let's get our body. So I'm gonna go ahead and jump back over to here and just copy this body back over to our update lesson and paste that in. Let's do the good old faithful hyphen edit
09:44
to make sure that that actually works. And it does not, we get back a 404. And that's because I forgot my S again. So we wanna do lessons, not lesson.
09:54
Let's try that one more time. Okay, there we go. Now we got back our test lesson 01 hyphen edit working a okay. Let's give that a save. Now down to our delete.
10:04
So let's delete the lesson with an ID of four. That's the one that we created prior to the one with the publish ID. So lessons with an S, send that off. Perfect.
10:13
That got back a 204, so that worked a okay. And then we have our patch lesson tag. So this should just be slash lessons
10:21
slash the ID that we want to target, which would be five, which is the one with the publish ID that we specified. And let's remind ourselves what that status ID was.
10:31
That was three. So if we go ahead and try to send that off with two, it should work a okay. And now it has a status ID of two. Perfect. All right, so all of that's working.
10:40
That leaves us then with our get lessons, which we'll take care of in the next lesson.