Playing Next Lesson In
seconds

Transcript

  1. So at this point, we know most of the big picture, but there is one final piece of the puzzle to put together, and that's how we combine our relationships together

  2. with our models to actually create data with our models. So I'm going to hit "Control tilde" here inside of our text editor and this will bring up the terminal. Now, I just have it taking up full screen,

  3. you can adjust this right here. This is going to give us a little bit more room to work with compared to working directly inside of our terminal application.

  4. I'm going to run node.ace.repl to jump into a REPL session. Await.loadModels to load up our models. So let's start with one-to-one relationships.

  5. So let's create a brand new user. So let's do const user equals awaitModels.user.read. We'll pass in an object here.

  6. I think this takes a full name. I'll put my name here, email, we'll just do test@test.com, and then a password of password.

  7. Let's go ahead and run that. Cool. So now we should have our user. If we go ahead and serialize that, we'll see my details printed out. However, at present, if we go ahead and try to await

  8. user.loadProfile and we do user.profile, we're going to get null because we haven't actually created a profile for this user. With our factory, we have this creating

  9. all of our profiles along with our users, which is why we went ahead and created a brand new user here so that we can walk through this. So there's a couple of ways that we can actually add data with a relationship for our user.

  10. So we could do await user.related as we've done prior to reach through the relationship, and then provide the relationship name. So that would be our profile. So far, we've just used this to

  11. access the relation query builder, but we can also call create directly off of this as well for relationships pointing to a single record. We just provided an object

  12. for the record that we want to create. So from our user model, we're grabbing the relationship for our profile, and then we're going to create a new profile

  13. specifically for the user that we started this call from. Lucid will automatically bind this user's ID onto the profile that ends up getting created.

  14. So it will automatically set that user ID that's on our profile model just via this relationship. Meaning, all that we have to do is define the, I think we call it the biography column,

  15. and everything else will be automatically taken care of for our current profile. So this is my bio. Let's hit "Enter" there.

  16. Cannot define biography on profile model since it's not defined as a model property. So I got the name wrong, my bad. Let's go check our profile model here.

  17. Oh, it's called description. We'll elevate our panel size back up for our terminal. I'll go ahead and hit "Up", and I'll switch our biography column to description.

  18. Run that, and there we go. So now if we await user.loadProfile, and then user.profile, and we go ahead and serialize that,

  19. we're going to see that we actually get back that newly created profile instance. Additionally, whenever we called create, it immediately returned the model that was created as a result as well.

  20. So you could do cost profile equals await user related profile.create and get back that brand new profile that way as well.

  21. So let's go ahead and await user.profile.delete to delete that user's profile.

  22. So now if we do user.profile.isDeleted, we're going to get back true. Remember that this is a flag that holds whether or not the current record has been

  23. deleted out of the database using the model. This paves way for us to create a brand new profile. So I've cleared out our terminal, reloaded our rep session to give us more working room here. Let's go ahead and grab that user again.

  24. So cost user equals awaitModelsUser.findByOrFail, we'll find by e-mail,

  25. and we've provided that with test@test.com. So now we should have our user, and if we type out user, we could see in the hint down here that we do indeed have that user. We can also create a brand new record by

  26. instantiating the model itself as well. So we can do const profile equals newModels.profile. Within our code, I'm going to scroll down to the bottom here.

  27. This would be the same as doing const profile equals new profile directly with our model class. So we'll run that on the profile.

  28. We'll set the description value equal to, we'll just do test right now to give it something. So at this point, we have a profile instance of our model and a user instance of our model,

  29. but our profile instance is not persisted into the database, nor is it bound to our user. Well, we can do both of those with one call by doing

  30. awaitUser.relatedProfile to reach through with the relationship again, and calling .save and providing it

  31. the entire model that we want to relate with, which would be our profile. This will both persist our profile into the database

  32. and relate it to our user record as well. So we've run this, and then awaitUser.loadProfile,

  33. user.profile, let's go ahead and serialize that. There is our brand new profile with our test description.

  34. Once more, let's go ahead and awaitUserProfile.delete. Back with a fresh slate here, we have our user once more.

  35. Another way that we can join these relationships together, and this one is unique to belongs to relationships. So this will be coming at it from our profile side, is to use associate and disassociate.

  36. So const profile equals new, and let's do models.profile once more, instantiating a new instance of our profile class.

  37. Then we'll do profile.description equals, I probably spelled that wrong, but we'll just do associated there. Now from our profile side,

  38. we can awaitProfile.related, reach for the user relationship, and we can associate our profile

  39. with the particular user instance that we have. Again, this will save our profile into the database, and set its user ID with the user's ID that we've provided in.

  40. We'll go ahead and run that. Now if we await, and let's do this from our user side, user.loadProfile, user.profile.serialize once more,

  41. we'll see that our profile was successfully associated with this user. Now, the user ID relationship for our profile is required. But if this relationship were nullable,

  42. we could then dissociate these two from one another as well, by doing something relatively similar to our associate call.

  43. We could awaitProfile.related user, and then call.dissociate to pluck the two apart from one another,

  44. essentially removing the user ID off our profile and setting it to null. Again, our profile's user ID is required, it's not nullable in the database.

  45. So if I were to run this, we're going to get an error due to that violation of the not null constraint. But if that relationship were nullable,

  46. that's one way that you could dissociate the two from one another. So we're back into a fresh REPL session with everything cleared out. Similarly to how we created a record through

  47. the relationship for our one-to-one relationship between our user and our profile, we can do the same using a create many call for one-to-many relationships as well. So if we create a new Sinist here,

  48. I'm going to do const me because I find Sinist rather hard to spell correctly, equals awaitModels.sinist.create,

  49. providing a first name of Tom and a last name of Govij. So we'll just create a record here for myself. I believe headshot URL has an empty string default,

  50. so we should be good to just create a record with that. Cool. Everything looks good there. If we do await me.related, and I'm going to collapse this back down and take

  51. a look to see what we call this relationship. We have moviesDirected and moviesWritten as our hasMany relationships here. So we can do either one of those for our example,

  52. moviesWritten.createMany here to create many in one go. So we can provide in an array, and then inside of our array,

  53. we just need to create objects for however many movies we want to add and fill them out with each movie's details. So let's see, we have our status ID. I'm just going to default that to one.

  54. Our director ID, we'll default that to one. Our writer ID will be automatically set via the relationship because we're reaching

  55. through this user via the movie's written relationship, which is bound by the written ID. So we can pass over the written ID,

  56. go to title, do written by me or something like that. I believe everything else there is optional. So let's move on to our second one, status ID. We'll just do one here, director ID.

  57. We'll do one again, title, written by me too. Let's go ahead and run that. As you can see, just like create,

  58. it's going to return back each of the movies that was created with this relationship. Now we should be able to do await me.loadMoviesWritten,

  59. and then me.moviesWritten, and we'll map over those, movie.serialize like so,

  60. and we get back both movies written by me and written by me too. So we're back with a fresh REPL session with me defined once more. Again, just like with our user profile example,

  61. whenever we called save through the relationship, we took a model instance that wasn't yet persisted into the database, saved it, and bound it to the user all in one blow.

  62. We can do that exact same thing here using save many for our one-to-many relationships.

  63. So let's do const movie1 equals new models movie, and we'll create a new instance of that. Now, rather than doing movie1.statusId,

  64. so on and so forth, taking up multiple lines here, what we could alternatively do is do movie1.merge,

  65. and this will merge whatever object data we provide into our current movie data. So for example, we could just set just as we did with our create call,

  66. our status, id of one. How about for this one, we set these movies as directed. So we'll set our writer ID rather than our director ID,

  67. and then our title directed by me1, and we'll merge that data in. So now if we do movie1.title,

  68. we see it was directed by me1. Cool. So let's do const movie2 equals new models movie, create an instance of that,

  69. and we can do movie2.merge, statusId of one, writerId of two, and title of directed by me2,

  70. and we'll merge that in. So now for movie2.title, we have directed by me. So let's take await me.related,

  71. we'll reach for our movies directed relationship, and we'll .saveMany and provide an array of all of

  72. the movies that we want to save with this movies directed relationship to me. So we'll do movie1 and movie2.

  73. We'll run that, and now we should be able to await me.loadMoviesDirected,

  74. me.moviesDirected.mapMovie, movie.serialize, and we should see directed by me1 and directed by me2.

  75. Awesome. So now we know how to create one-to-one and one-to-many relationships through our models via their helper methods. What about many-to-many? So let's go ahead and get a movie.

  76. So the const movie equals awaitModels.movie.create, we'll set the status ID to one, writer ID to one,

  77. and director ID to one with a title of attached. Create that movie here, type it out, there we are, we see it. So thanks to our seeder, we know that we have plenty of

  78. synapse and we know that we have somebody with an ID of 1, 2, 3, and so on and so forth. So if we took those IDs and we wanted to bind them to this movie as either cast or crew members,

  79. what we could do is await movie.related to reach for the relationship applicable for the action that we want to take.

  80. So we do cast members to bind in a cast member here, .attach, and then provide in an array of IDs for

  81. synapse that we want to attach as cast members to this movie. So if we run that and we do await movie, load cast members,

  82. movie.castmembers.map, cast, cast.serialize, we're going to see three synapse records bound to

  83. this movie as cast members. But hold that thought. What about our pivot table data? Well, that information isn't defined on the model itself.

  84. Anytime that we're working with the models, they will always return back instances of those models unless we call Poyo as we have prior.

  85. Anything extra not defined on the model itself is bound onto an extras object and by default extras will not serialize with the model unless you explicitly tell it to.

  86. So first, let's check and verify that this was the relationship that we had eerily loading our pivot table data. Right there, yes, it is. So let's jump over to our synapse model.

  87. So on our model, all that we need to do is add serialize extras and set that to true. Now, it will include those extras anytime that we serialize our models results.

  88. So for example, if we take a look at movie.castmembers as a whole and we take a look at those raw data, within one of our synapse models as we see right here,

  89. we have a $extras object. This is where our pivot table data is. This is also where any aggregates go.

  90. So if we reach for movie.castmembers and we go for the first index, .extras, we'll see our pivot movie ID,

  91. pivot synapse ID, character name, sort order, created at and updated at columns. So all are accessible. However, we've bound these cast members

  92. without specifying a sort nor a character name. So let's go ahead and remove these synapse from this movie. To do that, we can either remove

  93. everybody by doing await movie.related castmembers.detach. If we don't provide anything to detach,

  94. it will remove all cast members from this movie. However, we can scope this to just specific IDs by providing those in as well. So we've attached one, two, and three.

  95. They are the only synapse bound to this movie. But if we only supply one and two, three will remain as a cast member. Right now though, we want to go ahead and delete everybody.

  96. So we'll just go ahead and detach altogether. I'm going to go ahead and reload our model so that we get that serialize extra applied and we can see exactly what it does. So as a reminder,

  97. here's the serialized results for our cast members without the serialize extra applied. So we're back with a fresh travel session in our movie re-queried.

  98. Let's go ahead and do await movie related castmembers.attach. For right now, let's just do the IDs one and two.

  99. We'll run that. We'll do await movie.load the cast members and then movie.castmembers.map,

  100. cast, cast.serialize. Now, whenever we run that, we're going to see meta applied onto these results as well with that extra information that

  101. was on the raw model instance whenever we inspected it. Now, we need to take care of including pivot data information whenever we're attaching. So how can we do that? Well, let's go ahead and detach once more.

  102. So await movie related cast members detach, and let's get a fresh instance. So let's get back to where we're attaching.

  103. So we'll do await movie related cast members attach. Instead of providing an array of IDs, since we need to provide additional information

  104. with each one of the IDs that we're attaching, which is what character they played and what sort we want them to display in, we'll want to provide an object. I'm going to go ahead and get rid of our ending here and

  105. hit "Enter" so that we do multi-lines. The key for the object is going to be the ID of the record that we want to attach. So if we want to attach a sentence with an ID of one,

  106. the key would be one. Then for the value of this ID of one, we would provide an object with the additional information that should be attached onto the pivot table.

  107. So for our cast, this would consist of the character name, maybe that's Linus, and then the sort order, and we'll do a zero-based index here.

  108. So we can do comma, and then attach in a sentence with an ID of two, give them a character name of Snoopy, and a sort order of one.

  109. Go ahead and end our attachment there and run that. My bad, I missed the curly brace there, so I retyped it here. We'll do curly brace and then end our parentheses,

  110. and then we can call that, and there we go. So now we can await movie.load our cast members,

  111. and then movie.castmembers.map, cast, cast.serialize, so that we can actually see the results here in our terminal and run that.

  112. And there we go, we have our synapse with an ID of one, and then their meta pivot table data includes a character name of Linus, and a sort order of zero,

  113. and then our synapse with an ID of two here has a character name of Snoopy, and a sort order of one. So that successfully got applied onto our pivot table data

  114. for those two relationships. So we know attach will take an array of IDs, or an object with a key where the key is the ID and a value where the value is pivot table data

  115. as an argument. We know detach will take an array of IDs or nothing at all. If we provide nothing at all, it will detach everybody. If we provide an array of IDs,

  116. it will only attach those with the IDs that we've provided. And there's actually a third argument as well.

  117. So if we do movie.related, stick with our cast members here, and there is a sync method that we can call

  118. that accepts the same arguments as attach does. So we could provide an array of IDs, or we could provide a key value pair where the key is our attached ID,

  119. and the value for that key is the pivot table data. To keep things simple here, just to show what this does, we'll just provide an array of IDs.

  120. So we have an ID of one and two attached onto this movie. If we sync a synapse with an ID of three, run that. Oh, I forgot to await it,

  121. but it will have completed successfully by now. So we can move forward and do await movie.load,

  122. and we'll just reload our cast members. So reload them, and movie.castmembers.mapcastcast.serialize,

  123. we'll see that all that we get back is the synapse with an ID of three. The other two were removed,

  124. and that's because sync will detach everybody and then reattach only those with the information that we've provided inside of sync, essentially syncing the data with whatever we have provided.

  125. Anything not provided is considered outdated and removed. And there we go. That's how you can manage your relationships with your Lucid models for one-to-one,

  126. one-to-many, many-to-one, and many-to-many relationships.

A Deep Dive Into Relationship CRUD with Models

In This Lesson

We'll take a deep look at how we can perform CRUD operations with one-to-one, one-to-many, many-to-one, and many-to-many relationships using our Lucid Models.

Created by
@tomgobich
Published

Join the Discussion 0 comments

Create a free account to join in on the discussion
robot comment bubble

Be the first to comment!