00:00
[MUSIC]
00:05
For our admin movie actions,
00:06
let's start by adding the ability to create a new movie inside of our application.
00:09
So we'll start by adding in a create movie button right over there.
00:11
So scroll on up to where we have our H1,
00:14
wrap this inside of a div class flex justify
00:17
between and add a gap of four there for safety sake.
00:20
For our H1, and then we'll do a div and add a button into it that goes to an href,
00:25
and we'll put a pound there for right now because we don't have the route created,
00:28
and our button, and we'll do create movie,
00:31
and we'll probably also want this to be items center there as well.
00:34
Next, let's go ahead and create the route for our create movie page.
00:37
So let's jump into our routes file,
00:39
and then let's do router.get/movies create admin movies controller,
00:45
and we have our create method there as movies create.
00:48
Now, let's pause here for a second.
00:50
So if you recall to when we created this controller,
00:52
we created it as a resourceful controller,
00:54
and there's also resource routes.
00:55
It's my personal preference to list out the routes individually,
00:58
but I know that may not be everybody's preference.
01:00
So an alternative that we could do to listing them out
01:03
individually is router.define this as a resource route group.
01:07
You define the resource name,
01:09
so that's our movies,
01:10
and then provide in the controller that handles the resource,
01:12
which is our admin movies controller,
01:14
and that takes care of the rest.
01:16
So we can remove those,
01:17
and now we have a route defined for each of our resource actions.
01:21
For example, if we stop our server,
01:23
clear that out, and do node ace list,
01:25
there is an actual list command called list routes.
01:28
I don't think we've taken a look at this quite yet.
01:30
Maybe we did early on, but let's clear this out.
01:32
Node ace list routes,
01:34
hit enter on that, and we're going to get a list of all of our defined application routes.
01:39
Right down here are all of the routes being defined via
01:42
that one line where we're doing router.resource movies.
01:45
It's prefixing all of the defined routes with movies.
01:48
For our create route, it's doing /create.
01:50
For our show, edit, update,
01:52
delete, and destroy routes,
01:53
it's appending on an ID route parameter,
01:56
and on edit, it's making it unique by doing /edit there as well.
01:59
Additionally, you can see each of these get a name too.
02:01
So we have our admin movies index,
02:03
the same as we had it before,
02:04
admin movies create, the same as we had just manually defined it.
02:07
We also have our store, show, edit,
02:09
update, and destroy names as well.
02:12
You can see put and patch get the same route name as they're discerned via their HTTP action.
02:17
So we can clear that out, boot our server back up,
02:18
so npm run dev.
02:19
So we should be done defining our routes for our movies resource actions.
02:23
Meaning that we can now jump into our movies controller.
02:25
For our admin section,
02:26
scroll down to where we have the create method,
02:28
grab the view, return view,
02:31
render, pages, admin, movies,
02:33
and we'll create a create page.
02:35
We're going to want to provide some things in.
02:37
So first, let's go ahead and take a look at our movies model
02:39
to remind ourselves what all we have on here.
02:41
So we're going to need to provide the status options as a select,
02:44
so that we can select the status to apply to a movie.
02:46
In a real-world application,
02:47
it would be great to just pull this writer and director in as an autocomplete.
02:51
So as you type out, it will fetch and provide options in.
02:55
That behavior is not quite manually supported by the browser,
02:58
and we would need to pull in a third-party package to support that on the browser side.
03:02
So we're going to bypass that approach here today,
03:05
and just provide our sinister as a select list,
03:08
so that we can select our writer and director IDs there.
03:10
Then we'll have a title, our slug will be auto-generated from the title,
03:13
summary, abstract, poster URL,
03:16
release that, and that looks like it.
03:17
So that we don't have to remember that,
03:19
what I'm going to do is drag this off to the right-hand side,
03:21
and we can nudge it in there a little bit to make it smaller,
03:24
just so that we have the property names here to
03:26
reference and remind ourselves what we need.
03:28
So we're going to need the statuses.
03:29
So const statuses equals await movie status.
03:34
We can import that model query,
03:36
order by, let's just order it by the movie status name.
03:38
Then we'll also want to get our sinist.
03:40
So sinist equals awaits,
03:43
import our sinist controller there,
03:45
query, and let's order them by,
03:47
we'll just do their last name.
03:48
That should be all that we need to provide in for our form.
03:51
So we have our statuses and our sinists.
03:54
So let's go create our movies create page.
03:56
So let's scroll on down to where we have our admin movies directories,
03:59
right-click that movie directory,
04:01
new file, add in create.edge@layoutadmin,
04:05
and that layout, do an h1,
04:07
create a new movie,
04:09
add a form in, method of post,
04:12
and an action reaching for a route admin movies store method,
04:18
and that form out.
04:19
We're going to want to add in our CSRF field,
04:22
and let's start with a form,
04:24
input label will be title,
04:27
name will be title,
04:28
the type will be text, which is our default input type.
04:30
So we'll leave that out.
04:31
So that should be all that we need for our title input.
04:33
Can actually go ahead and just collapse that down to a single line there too.
04:36
So then we have our form.input,
04:39
the type for this one is going to be select,
04:41
label will be status,
04:43
and the name we'll do as movie status ID,
04:46
matching how we have it defined on the model.
04:48
We'll end our input and add in our option slot.
04:51
So do at each status in statuses,
04:55
go ahead and end our each,
04:56
add in an option value,
04:58
status ID, and then the status.name as the display value.
05:03
Next, we have our writer and director.
05:04
So we can go ahead and give this select for our status copy,
05:07
paste it in once,
05:08
switch status to writer,
05:10
switch the name to writer ID,
05:13
and rather than looping over our statuses,
05:14
we're going to be looping over our synists with a synist there.
05:18
Give that a copy because it's hard to type and paste that in for the ID,
05:21
and rather the name that should be named there.
05:24
Once we have that applied, we can give that a copy once more,
05:26
paste it in there, and switch writer to our director.
05:29
So we'll have a director ID,
05:31
and then the options should remain the same for that.
05:33
Then we have our summary and abstract.
05:35
So we can do @forminput type,
05:38
and I believe we added support for a text area.
05:40
If not, we'll add that in here momentarily,
05:42
but I believe we did.
05:43
We'll have our label of summary and name of summary, just like so.
05:47
Then we can give that one there a copy as well,
05:49
paste it in, and switch summary to our abstract.
05:53
Now, an abstract is something that you would probably want to
05:55
add a WYSIWYG into your application for,
05:58
what you see is what you get editor.
05:59
Those can take time to actually add into an application.
06:01
We're going to cover how you can go about that in a separate series later on,
06:04
but for now, we're just going to leave this here as a text area.
06:07
Then it looks like we have our release that as the last field that we'll need.
06:10
Let's go and scroll up and put that underneath the title.
06:13
So we can use the native browser input for that.
06:15
So we can do @forminput type,
06:18
and it looks like we have this as a date time,
06:20
but let's just worry about the date in terms of actually setting up a value.
06:24
So name, give that a name of released at.
06:27
Again, we're just naming all of these fields to
06:28
match how we have them defined in the model,
06:30
just to simplify taking our form data,
06:32
validating it, and passing it directly into our movie model as the property values.
06:36
We need a label on this one here as well.
06:38
So label and we'll do release date there.
06:41
Let's scroll on down to the end and give ourselves a submit button.
06:44
So @button, type, submit,
06:46
end our button, and create movie there as the action.
06:49
We do also have that poster URL.
06:51
Take care of that here in the next lesson.
06:52
Let's first just see what we get.
06:53
So let's jump into our create movie page.
06:55
Oops, forgot to break up the route.
06:57
Let's go break that up real quick.
06:58
Jump into our movies index page right there,
07:00
route, admin, movies, create.
07:03
There we go. So now we should be able to jump into our browser and hit create movie.
07:06
There we go. Could use some spacing,
07:08
but we do indeed get our form here.
07:10
We have our date input right here.
07:12
For browsers, you can still click on the little calendar icon
07:15
and get a nice little date selector right there.
07:17
So that's nice.
07:18
For our statuses, we have casting, post-production,
07:20
production, released, and writing.
07:22
For our writers and directors,
07:23
we have a list of all of our cinests, just like so.
07:26
So everything there seems to be working A-okay.
07:28
Let's go ahead and minimize that back away.
07:29
Scroll up to our form.
07:31
And to add some spacing in here,
07:32
I think what we'll do is we'll just do class, flex, flex, call, and gap of form.
07:38
So we'll just use flex gap there to add spacing
07:40
in between each of our little form groups there.
07:42
Okay, cool.
07:42
So next we need to do our validator
07:44
and then actually create our movie.
07:46
So we do already have a movie validator file.
07:49
We could reuse this here if we wanted to,
07:51
or we could create a brand new file
07:52
specifically for our admin movie section.
07:55
What would probably make the most sense
07:56
is to rename this validator file to movie filter validator,
08:01
since this is specifically for our movie filters.
08:04
So I think what we'll do is we'll right-click this file,
08:06
rename, and append in filter to the end of that file name.
08:10
And Visual Studio Code's gonna ask us
08:11
whether or not we want to update the existing imports
08:14
for this file to match what we've renamed this file to.
08:17
Go ahead and hit yes for that
08:18
so that we don't have to worry about it.
08:20
The only thing it doesn't do that I wish it would do
08:22
is automatically save for us.
08:23
So you can see movie service right up here,
08:25
just switch to needing to be saved.
08:27
So we can jump into here.
08:28
This may be the only file where we were using it
08:30
since we normalized everything into this service.
08:32
So we can go ahead and give that a save, close that back out,
08:34
which should now give us room to do command control P,
08:37
make validator, and we'll create a brand new movie validator.
08:39
So now we have a validator for our movie filters
08:42
and a validator specifically for our movie itself.
08:45
So let's export const movie store validator
08:49
equals vine compile, and we'll do vine object.
08:52
We're gonna have a title of type vine string,
08:55
and let's just roll through and put the types in first,
08:57
and then we'll add in additional checks.
08:59
So let's scroll on up to make sure we get everything here.
09:01
We'll leave slug out because that's gonna be auto-generated
09:04
by our model, have our summary type vine string,
09:07
our abstract as type vine string,
09:11
our writer ID as type vine number,
09:14
director ID of type vine number,
09:17
and let's see, status ID vine number.
09:20
And let's see, here we have our poster URL,
09:23
which we'll take care of here in the next lesson,
09:25
and our released at.
09:26
So released at vine, and that will be a date.
09:30
Okay, so for each of these,
09:31
we're gonna have varying additional validations to do.
09:35
And for that, it would be great to reference
09:36
what we've defined on our database as requirements.
09:39
So we'll scroll down to our database, migrations,
09:42
and let's go find our movies table migration
09:45
and open that up here on our right-hand side.
09:47
Clips that back down, scroll back up there,
09:49
and minimize that back down too.
09:50
Okay, so for our status writer and director IDs,
09:53
these all reference their relationships,
09:55
and all three are not nullable,
09:57
so they're not gonna be optional.
09:59
So we're gonna wanna add exists checks to those three.
10:02
And if you recall, Mac, we created an is unique utility
10:05
so that we didn't actually have to write a unique query
10:07
over and over again for our validation.
10:09
So let's go ahead and do that for unique as well real quick.
10:11
So we'll scroll on down.
10:12
We had that within our start directory, rules.
10:15
We have this unique file here.
10:16
Rather than squeezing an additional rule
10:18
inside of this file, let's go ahead and create a new file.
10:21
So let's right-click our rules directory, create a new file,
10:23
and add in exists.ts,
10:26
and paste what we had for our unique
10:28
within here for our exists check.
10:30
For this one, I think we only care about numbers for it,
10:32
so we'll go ahead and remove strings.
10:34
And we're gonna want to update the name
10:35
from is unique to is exists,
10:38
or whatever you feel is applicable there.
10:39
My goal with the naming here is just to not overwrite
10:42
the currently existing unique and exist validator methods
10:45
in case we should ever need them.
10:46
Our query here remains the same,
10:48
but if we don't get a result,
10:50
that's when we wanna report an error.
10:51
So we'll flip our result check there
10:54
and switch our error to something like
10:55
value for field does not exist,
10:59
and switch the reported error name to is exists.
11:02
Scroll down a little bit.
11:03
And now we have our is exists rule,
11:06
and create that with our is exists method.
11:09
Scroll down a little bit further.
11:10
We're not gonna apply this to strings,
11:12
but rather just numbers, so we'll remove the bind string
11:14
and switch our bind number to is exists.
11:17
Scroll down a little bit further.
11:18
Again, we'll remove the string,
11:19
so we'll get rid of that macro,
11:20
and switch this macro to is exists,
11:23
and use our is exists rule.
11:26
Awesome, so we manually created this exists preload file,
11:30
so we also need to manually register it
11:32
inside of our javasrc.ts file.
11:33
So within here, we'll scroll down
11:35
to where we have our preloads,
11:37
and add in an additional item,
11:39
port import start rules exists.
11:42
Up till now, we've used ACE to create these files for us,
11:45
and it automatically registered it
11:46
within here for us as well.
11:48
So that's why we need to manually do it here,
11:50
because we manually created that file.
11:51
Awesome, so now we can jump back
11:52
into our movie validator, and do .is exists here,
11:56
provide in our options of our table.
11:58
This will be our Cineasts table,
12:00
and the column will be ID,
12:02
and give that a copy,
12:03
and paste it on our writer there as well.
12:06
And then for our status, this will be is exists table
12:09
movie_statuses, and column of ID.
12:14
All right, cool, so both of those should be good.
12:16
Then we come down to our title,
12:17
which is not nullable,
12:19
and has a max length of 100 characters.
12:21
So we're gonna want to validate against that as well.
12:23
So we'll do .max_length 100 there
12:25
to enforce that limitation.
12:27
Our slug is auto-generated, so we'll skip over that.
12:29
Our summary is not nullable,
12:30
but it does default to an empty string,
12:32
meaning that we can make it optional
12:33
here inside of our validator.
12:35
Our summary is also a type of string here,
12:37
so it's going to have a default max length
12:39
of whatever your database's max length for string is.
12:42
I believe in most cases,
12:44
a max length of 255 should be a safe bet there,
12:47
or our summary, and then our abstract is a type of text.
12:51
So we don't really need to worry
12:53
about adding a max length in there,
12:54
as it can go as long as needed.
12:56
We're omitting our poster URL for now.
12:58
We have that as not nullable
12:59
and defaulting to an empty string,
13:00
so that is technically optional as well.
13:03
Then we have our release_stat, which is nullable.
13:05
So we'll wanna make sure that that is optional as well.
13:08
And we're going to want to get this back
13:10
as a lux in the time.
13:11
So we'll also want to transform the value here.
13:14
This transformer, we can provide a callback to
13:16
that takes in our value as a JavaScript date,
13:19
which we can return date time,
13:21
dot from JS date, provide our value in, and there we go.
13:25
So now we'll have a lux and date time returned
13:28
after we validate our release_stat, if it has a value.
13:31
Now, since we're using a native date input,
13:33
the format should come up as year, year, year,
13:36
month, month, day, day, separated by hyphens,
13:39
which should be accepted by default from Vine here.
13:42
But if you have any issues,
13:43
you can specify additional formats into here as well.
13:47
You can specify that as an array as needed.
13:49
So you could do something like year, year, year,
13:51
month, month, day, day, which is I think supported by default
13:55
and I believe that's how our dates will be sent up.
13:57
So we should be able to omit that altogether.
13:59
Then we'll jump up into our movies controller
14:01
or our admin.
14:02
So right here, scroll down to our store method.
14:05
We'll do const data equals await request validate
14:09
using our movie store validator.
14:12
Then we'll do const movie equals await movie,
14:15
import that if we need to,
14:16
it looks like it's already imported, dot create
14:18
and provide our data in.
14:20
So with that in place,
14:21
we should be good to go ahead and just response
14:23
and return response redirect.
14:26
And let's go to route admin movies index.
14:30
And of course, since we're not using our movie,
14:32
we can go ahead and get rid of that variable there.
14:34
All right, let's go ahead and jump into our browser
14:36
and test it out.
14:37
So first and foremost,
14:38
let's just try to hit create without specifying anything.
14:40
And you can see, we get the title must be defined
14:42
and our abstract must be defined.
14:44
Apart from that though,
14:45
it looks like everything's working a-okay.
14:46
So let's go ahead and try my cool movie one.
14:51
Let's provide in a release date
14:52
to make sure that that's working okay.
14:53
Let's say maybe the 26th of June.
14:56
Let's say it is in post-production.
14:59
Let's say the writer is Leila Abernathy
15:01
and the director is Trudy Berner.
15:03
We'll leave our summary out,
15:05
but we will provide an abstract of,
15:06
this is a cool movie right here.
15:09
And let's go ahead and hit create.
15:11
And it looks like something failed,
15:12
but I'm not quite sure what.
15:14
We got redirected back here with our old values,
15:16
but no error was displayed.
15:18
All right, so let's jump back into our create page here
15:22
and let's see what's going wrong.
15:23
So let's add an inspect/messages.all
15:27
and let's try this all again.
15:29
So we'll do test movie,
15:31
do everything the exact same as we had it before,
15:32
not necessarily with the exact same values.
15:34
And let's hit create to see what's going wrong.
15:37
All right, so here's all of our old values
15:39
and then here's our error status ID.
15:40
The status ID must be the,
15:42
oh, in our validator, we called it status ID,
15:44
but on our model and here in our field,
15:46
it is movie status ID.
15:48
Okay, so let's hide that back away,
15:49
jump back into our movie validator.
15:51
Right here, it's status ID,
15:52
but we need to call this movie status ID.
15:55
Or did we call it just status ID on our model?
15:57
Oh, we sure did.
15:58
Okay, so let's leave that as status ID then.
16:01
And it's our form that needs to change.
16:02
So let's go down to our movie status select
16:05
and switch that to just status ID.
16:08
All right, now let's jump back into our browser.
16:11
My cool movie one, select the date here.
16:14
So we'll do the 26th again,
16:16
say that this is post-production,
16:18
Leila Abernathy, Trudy Bernard,
16:20
and this is a cool movie right here.
16:23
Create our movie and there we go.
16:25
So now we see the most recent movie
16:26
that we have inside of our movies list
16:28
is now my cool movie one,
16:30
it's in post-production,
16:31
the writer is Leila Abernathy
16:32
and the director is Trudy Bernard.
16:34
And we're not worrying about our cast or crew members
16:36
for this particular format at this point.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!