00:00
[MUSIC]
00:05
So at this point, we know most of the big picture,
00:06
but there is one final piece of the puzzle to put together,
00:09
and that's how we combine our relationships together
00:12
with our models to actually create data with our models.
00:15
So I'm going to hit "Control tilde" here inside of
00:17
our text editor and this will bring up the terminal.
00:20
Now, I just have it taking up full screen,
00:22
you can adjust this right here.
00:23
This is going to give us a little bit more room to work with
00:26
compared to working directly inside of our terminal application.
00:30
I'm going to run node.ace.repl to jump into a REPL session.
00:34
Await.loadModels to load up our models.
00:37
So let's start with one-to-one relationships.
00:39
So let's create a brand new user.
00:40
So let's do const user equals awaitModels.user.read.
00:46
We'll pass in an object here.
00:48
I think this takes a full name.
00:50
I'll put my name here,
00:51
email, we'll just do [email protected] ,
00:54
and then a password of password.
00:57
Let's go ahead and run that.
00:59
Cool. So now we should have our user.
01:01
If we go ahead and serialize that,
01:03
we'll see my details printed out.
01:04
However, at present, if we go ahead and try to await
01:07
user.loadProfile and we do user.profile,
01:11
we're going to get null because we haven't
01:13
actually created a profile for this user.
01:15
With our factory, we have this creating
01:16
all of our profiles along with our users,
01:18
which is why we went ahead and created
01:19
a brand new user here so that we can walk through this.
01:21
So there's a couple of ways that we can actually add
01:23
data with a relationship for our user.
01:25
So we could do await user.related as we've
01:29
done prior to reach through the relationship,
01:31
and then provide the relationship name.
01:32
So that would be our profile.
01:34
So far, we've just used this to
01:35
access the relation query builder,
01:37
but we can also call create directly off of this as
01:40
well for relationships pointing to a single record.
01:43
We just provided an object
01:44
for the record that we want to create.
01:46
So from our user model,
01:48
we're grabbing the relationship for our profile,
01:50
and then we're going to create a new profile
01:52
specifically for the user that we started this call from.
01:55
Lucid will automatically bind this user's ID
01:58
onto the profile that ends up getting created.
02:00
So it will automatically set that user ID that's on
02:02
our profile model just via this relationship.
02:05
Meaning, all that we have to do is define the,
02:07
I think we call it the biography column,
02:09
and everything else will be automatically
02:11
taken care of for our current profile.
02:13
So this is my bio.
02:15
Let's hit "Enter" there.
02:17
Cannot define biography on profile model since
02:20
it's not defined as a model property.
02:22
So I got the name wrong, my bad.
02:24
Let's go check our profile model here.
02:26
Oh, it's called description.
02:27
We'll elevate our panel size back up for our terminal.
02:30
I'll go ahead and hit "Up",
02:31
and I'll switch our biography column to description.
02:35
Run that, and there we go.
02:37
So now if we await user.loadProfile,
02:40
and then user.profile,
02:42
and we go ahead and serialize that,
02:44
we're going to see that we actually get back
02:45
that newly created profile instance.
02:48
Additionally, whenever we called create,
02:49
it immediately returned the model
02:51
that was created as a result as well.
02:53
So you could do cost profile equals await
02:56
user related profile.create and get
02:58
back that brand new profile that way as well.
03:00
So let's go ahead and await user.profile.delete
03:04
to delete that user's profile.
03:06
So now if we do user.profile.isDeleted,
03:10
we're going to get back true.
03:12
Remember that this is a flag that holds whether or not
03:14
the current record has been
03:15
deleted out of the database using the model.
03:17
This paves way for us to create a brand new profile.
03:20
So I've cleared out our terminal,
03:21
reloaded our rep session to give us more working room here.
03:24
Let's go ahead and grab that user again.
03:25
So cost user equals awaitModelsUser.findByOrFail,
03:32
we'll find by e-mail,
03:33
and we've provided that with [email protected] .
03:36
So now we should have our user, and if we type out user,
03:38
we could see in the hint down here that we do indeed have that user.
03:41
We can also create a brand new record by
03:43
instantiating the model itself as well.
03:46
So we can do const profile equals newModels.profile.
03:51
Within our code, I'm going to scroll down to the bottom here.
03:53
This would be the same as doing const profile
03:56
equals new profile directly with our model class.
03:59
So we'll run that on the profile.
04:02
We'll set the description value equal to,
04:04
we'll just do test right now to give it something.
04:06
So at this point, we have a profile instance of
04:08
our model and a user instance of our model,
04:11
but our profile instance is not persisted into the database,
04:14
nor is it bound to our user.
04:16
Well, we can do both of those with one call by doing
04:18
awaitUser.relatedProfile to reach through with the relationship again,
04:24
and calling .save and providing it
04:27
the entire model that we want to relate with,
04:30
which would be our profile.
04:32
This will both persist our profile into the database
04:35
and relate it to our user record as well.
04:38
So we've run this, and then awaitUser.loadProfile,
04:42
user.profile, let's go ahead and serialize that.
04:45
There is our brand new profile with our test description.
04:49
Once more, let's go ahead and awaitUserProfile.delete.
04:54
Back with a fresh slate here,
04:56
we have our user once more.
04:58
Another way that we can join these relationships together,
05:00
and this one is unique to belongs to relationships.
05:03
So this will be coming at it from our profile side,
05:05
is to use associate and disassociate.
05:07
So const profile equals new,
05:11
and let's do models.profile once more,
05:13
instantiating a new instance of our profile class.
05:16
Then we'll do profile.description equals,
05:19
I probably spelled that wrong,
05:20
but we'll just do associated there.
05:22
Now from our profile side,
05:23
we can awaitProfile.related,
05:26
reach for the user relationship,
05:29
and we can associate our profile
05:31
with the particular user instance that we have.
05:34
Again, this will save our profile into the database,
05:37
and set its user ID with the user's ID that we've provided in.
05:41
We'll go ahead and run that.
05:43
Now if we await,
05:44
and let's do this from our user side,
05:45
user.loadProfile, user.profile.serialize once more,
05:50
we'll see that our profile was successfully associated with this user.
05:54
Now, the user ID relationship for our profile is required.
05:58
But if this relationship were nullable,
06:00
we could then dissociate these two from one another as well,
06:04
by doing something relatively similar to our associate call.
06:07
We could awaitProfile.related user,
06:11
and then call.dissociate to pluck the two apart from one another,
06:16
essentially removing the user ID off our profile and setting it to null.
06:20
Again, our profile's user ID is required,
06:23
it's not nullable in the database.
06:25
So if I were to run this,
06:26
we're going to get an error due to that violation of the not null constraint.
06:30
But if that relationship were nullable,
06:33
that's one way that you could dissociate the two from one another.
06:36
So we're back into a fresh REPL session with everything cleared out.
06:39
Similarly to how we created a record through
06:41
the relationship for our one-to-one relationship between our user and our profile,
06:45
we can do the same using a create many call for one-to-many relationships as well.
06:49
So if we create a new Sinist here,
06:50
I'm going to do const me because I find Sinist rather hard to spell correctly,
06:54
equals awaitModels.sinist.create,
07:00
providing a first name of Tom and a last name of Govij.
07:05
So we'll just create a record here for myself.
07:07
I believe headshot URL has an empty string default,
07:10
so we should be good to just create a record with that.
07:12
Cool. Everything looks good there.
07:13
If we do await me.related,
07:17
and I'm going to collapse this back down and take
07:19
a look to see what we call this relationship.
07:21
We have moviesDirected and moviesWritten as our hasMany relationships here.
07:25
So we can do either one of those for our example,
07:27
moviesWritten.createMany here to create many in one go.
07:33
So we can provide in an array,
07:34
and then inside of our array,
07:35
we just need to create objects for however many movies we
07:38
want to add and fill them out with each movie's details.
07:41
So let's see, we have our status ID.
07:43
I'm just going to default that to one.
07:45
Our director ID,
07:47
we'll default that to one.
07:48
Our writer ID will be automatically set via the relationship because we're reaching
07:52
through this user via the movie's written relationship,
07:55
which is bound by the written ID.
07:57
So we can pass over the written ID,
07:59
go to title, do written by me or something like that.
08:02
I believe everything else there is optional.
08:04
So let's move on to our second one, status ID.
08:07
We'll just do one here, director ID.
08:09
We'll do one again, title,
08:11
written by me too.
08:13
Let's go ahead and run that.
08:15
As you can see, just like create,
08:16
it's going to return back each of the movies that was created with this relationship.
08:22
Now we should be able to do await me.loadMoviesWritten,
08:26
and then me.moviesWritten,
08:29
and we'll map over those,
08:31
movie.serialize like so,
08:34
and we get back both movies written by me and written by me too.
08:38
So we're back with a fresh REPL session with me defined once more.
08:41
Again, just like with our user profile example,
08:44
whenever we called save through the relationship,
08:46
we took a model instance that wasn't yet persisted into the database,
08:49
saved it, and bound it to the user all in one blow.
08:52
We can do that exact same thing here using save many for our one-to-many relationships.
08:57
So let's do const movie1 equals new models movie,
09:02
and we'll create a new instance of that.
09:04
Now, rather than doing movie1.statusId,
09:07
so on and so forth, taking up multiple lines here,
09:09
what we could alternatively do is do movie1.merge,
09:13
and this will merge whatever object data we provide into our current movie data.
09:17
So for example, we could just set just as we did with our create call,
09:21
our status, id of one.
09:24
How about for this one, we set these movies as directed.
09:27
So we'll set our writer ID rather than our director ID,
09:30
and then our title directed by me1,
09:34
and we'll merge that data in.
09:36
So now if we do movie1.title,
09:39
we see it was directed by me1.
09:41
Cool. So let's do const movie2 equals new models movie,
09:46
create an instance of that,
09:48
and we can do movie2.merge,
09:50
statusId of one,
09:52
writerId of two,
09:54
and title of directed by me2,
09:57
and we'll merge that in.
09:59
So now for movie2.title,
10:01
we have directed by me.
10:03
So let's take await me.related,
10:07
we'll reach for our movies directed relationship,
10:10
and we'll .saveMany and provide an array of all of
10:14
the movies that we want to save with this movies directed relationship to me.
10:19
So we'll do movie1 and movie2.
10:22
We'll run that, and now we should be able to await me.loadMoviesDirected,
10:27
me.moviesDirected.mapMovie, movie.serialize,
10:33
and we should see directed by me1 and directed by me2.
10:37
Awesome. So now we know how to create one-to-one and one-to-many
10:39
relationships through our models via their helper methods.
10:42
What about many-to-many?
10:43
So let's go ahead and get a movie.
10:45
So the const movie equals awaitModels.movie.create,
10:50
we'll set the status ID to one,
10:52
writer ID to one,
10:54
and director ID to one with a title of attached.
10:58
Create that movie here, type it out,
11:00
there we are, we see it.
11:01
So thanks to our seeder, we know that we have plenty of
11:03
synapse and we know that we have somebody with an ID of 1,
11:06
2, 3, and so on and so forth.
11:07
So if we took those IDs and we wanted to bind them to
11:11
this movie as either cast or crew members,
11:13
what we could do is await movie.related to reach for
11:18
the relationship applicable for the action that we want to take.
11:20
So we do cast members to bind in a cast member here,
11:24
.attach, and then provide in an array of IDs for
11:28
synapse that we want to attach as cast members to this movie.
11:33
So if we run that and we do await movie,
11:36
load cast members,
11:38
movie.castmembers.map, cast, cast.serialize,
11:44
we're going to see three synapse records bound to
11:46
this movie as cast members.
11:48
But hold that thought.
11:49
What about our pivot table data?
11:51
Well, that information isn't defined on the model itself.
11:55
Anytime that we're working with the models,
11:57
they will always return back instances of
12:00
those models unless we call Poyo as we have prior.
12:03
Anything extra not defined on the model itself is bound onto
12:07
an extras object and by default extras will not
12:10
serialize with the model unless you explicitly tell it to.
12:13
So first, let's check and verify that this was
12:15
the relationship that we had eerily
12:17
loading our pivot table data.
12:18
Right there, yes, it is.
12:20
So let's jump over to our synapse model.
12:23
So on our model, all that we need to do is add
12:25
serialize extras and set that to true.
12:28
Now, it will include those extras
12:30
anytime that we serialize our models results.
12:32
So for example, if we take a look at
12:34
movie.castmembers as a whole and we take a look at those raw data,
12:38
within one of our synapse models as we see right here,
12:40
we have a $extras object.
12:43
This is where our pivot table data is.
12:46
This is also where any aggregates go.
12:48
So if we reach for movie.castmembers and we go for the first index,
12:53
.extras, we'll see our pivot movie ID,
12:56
pivot synapse ID, character name,
12:58
sort order, created at and updated at columns.
13:00
So all are accessible.
13:02
However, we've bound these cast members
13:05
without specifying a sort nor a character name.
13:08
So let's go ahead and remove these synapse from this movie.
13:11
To do that, we can either remove
13:12
everybody by doing await movie.related castmembers.detach.
13:19
If we don't provide anything to detach,
13:22
it will remove all cast members from this movie.
13:25
However, we can scope this to just specific IDs by providing those in as well.
13:30
So we've attached one, two, and three.
13:32
They are the only synapse bound to this movie.
13:34
But if we only supply one and two,
13:36
three will remain as a cast member.
13:39
Right now though, we want to go ahead and delete everybody.
13:41
So we'll just go ahead and detach altogether.
13:43
I'm going to go ahead and reload our model so that we get
13:45
that serialize extra applied and we can see exactly what it does.
13:48
So as a reminder,
13:49
here's the serialized results for
13:51
our cast members without the serialize extra applied.
13:55
So we're back with a fresh travel session in our movie re-queried.
13:57
Let's go ahead and do await movie related castmembers.attach.
14:04
For right now, let's just do the IDs one and two.
14:06
We'll run that. We'll do await movie.load
14:09
the cast members and then movie.castmembers.map,
14:15
cast, cast.serialize.
14:18
Now, whenever we run that,
14:19
we're going to see meta applied onto these results as
14:22
well with that extra information that
14:25
was on the raw model instance whenever we inspected it.
14:27
Now, we need to take care of including
14:29
pivot data information whenever we're attaching.
14:31
So how can we do that?
14:32
Well, let's go ahead and detach once more.
14:35
So await movie related cast members detach,
14:40
and let's get a fresh instance.
14:42
So let's get back to where we're attaching.
14:43
So we'll do await movie related cast members attach.
14:48
Instead of providing an array of IDs,
14:50
since we need to provide additional information
14:53
with each one of the IDs that we're attaching,
14:55
which is what character they played and what sort we want them to display in,
14:58
we'll want to provide an object.
15:00
I'm going to go ahead and get rid of our ending here and
15:02
hit "Enter" so that we do multi-lines.
15:04
The key for the object is going to be the ID of the record that we want to attach.
15:09
So if we want to attach a sentence with an ID of one,
15:11
the key would be one.
15:12
Then for the value of this ID of one,
15:14
we would provide an object with the additional information
15:17
that should be attached onto the pivot table.
15:20
So for our cast, this would consist of the character name,
15:24
maybe that's Linus, and then the sort order,
15:27
and we'll do a zero-based index here.
15:29
So we can do comma, and then attach in a sentence with an ID of two,
15:33
give them a character name of Snoopy,
15:36
and a sort order of one.
15:38
Go ahead and end our attachment there and run that.
15:42
My bad, I missed the curly brace there,
15:43
so I retyped it here.
15:44
We'll do curly brace and then end our parentheses,
15:47
and then we can call that, and there we go.
15:49
So now we can await movie.load our cast members,
15:54
and then movie.castmembers.map,
15:58
cast, cast.serialize, so that we can actually
16:01
see the results here in our terminal and run that.
16:04
And there we go, we have our synapse with an ID of one,
16:07
and then their meta pivot table data
16:09
includes a character name of Linus,
16:11
and a sort order of zero,
16:13
and then our synapse with an ID of two here
16:15
has a character name of Snoopy,
16:17
and a sort order of one.
16:19
So that successfully got applied onto our pivot table data
16:21
for those two relationships.
16:23
So we know attach will take an array of IDs,
16:25
or an object with a key where the key is the ID
16:29
and a value where the value is pivot table data
16:31
as an argument.
16:32
We know detach will take an array of IDs or nothing at all.
16:36
If we provide nothing at all, it will detach everybody.
16:39
If we provide an array of IDs,
16:40
it will only attach those with the IDs that we've provided.
16:43
And there's actually a third argument as well.
16:46
So if we do movie.related, stick with our cast members here,
16:51
and there is a sync method that we can call
16:53
that accepts the same arguments as attach does.
16:56
So we could provide an array of IDs,
16:58
or we could provide a key value pair
17:01
where the key is our attached ID,
17:03
and the value for that key is the pivot table data.
17:06
To keep things simple here, just to show what this does,
17:08
we'll just provide an array of IDs.
17:11
So we have an ID of one and two attached onto this movie.
17:14
If we sync a synapse with an ID of three, run that.
17:18
Oh, I forgot to await it,
17:19
but it will have completed successfully by now.
17:22
So we can move forward and do await movie.load,
17:26
and we'll just reload our cast members.
17:29
So reload them, and movie.castmembers.mapcastcast.serialize,
17:34
we'll see that all that we get back
17:39
is the synapse with an ID of three.
17:42
The other two were removed,
17:43
and that's because sync will detach everybody
17:46
and then reattach only those with the information
17:49
that we've provided inside of sync,
17:50
essentially syncing the data with whatever we have provided.
17:53
Anything not provided is considered outdated and removed.
17:56
And there we go.
17:57
That's how you can manage your relationships
17:59
with your Lucid models for one-to-one,
18:01
one-to-many, many-to-one, and many-to-many relationships.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!