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

Creating Post model0:00

Alright, so you made it this far. It's time to tackle the final relationship type. Many might say this is the most scary, the most confusing one. It's not too bad, you just need to understand the basics. So let's get started. I'm going to show you this in two forms. First, we'll set up a standard mini-to-mini relationship, and then we'll add a new constraint that forces us to switch to a mini-to-mini polymorphic relationship. So imagine we have a model for a standard Post. Alright, make a model for a Post. I'm going to need a migration for that, as well as a factory. Now, if we switch over there, we're going to do this fairly quick. This should all be a standard recap of Laravel Basics. So we'll have a string for the title, and then we'll have a text field for the body, and there's your standard Post. Now while we're here, let's go to our database factory for a Post and set this up.

for the title, and then we'll have a text field for the body, and there's your standard post. Now while we're here, let's go to our database factory for a Post and set this up. The title will be a fake sentence, and the body will be a fake paragraph. Alright, that should be fine. In real life, a Post is written by a User and things like that, but let's keep it very simple. So if I now migrate my database, and then check out SQL Pro, give it a refresh, we now have our users table, of course, but we also have our posts table, and an easy way to quickly generate new posts on the fly. Now, I want to create a feature a Post can be liked. Now, since we're at the final highest level for this series, I'm going to show you how I might do this in real life. I would start with a test. So I would make a new test called, we could call it LikingTest or LikePostTest. Why don't we stick

Writing like feature test1:33

to show you how I might do this in real life. I would start with a test. So I would make a new test called, we could call it LikePostTest. Why don't we stick with that? Okay, so this will be stored within your Feature directory. Let's clean this up a little bit. Here's how I write my tests. First, describe the behavior. Well, a Post can be liked. Alright, now we create the world. So we'll say something like, well, let's quickly whip up a Post. And luckily, because we have that model factory, it's really simple. So given we have a Post, and we like that Post, well, at the very least, I expect there to be exactly one Like associated with the Post. Notice I'm writing code that doesn't yet exist, it doesn't yet work, but we are describing how we want it to work. And then we're going to write the code to make that work. Alright, so I give that a save. And once again, given

it doesn't yet work, but we are describing how we want it to work. And then we're going to write the code to make that work. Alright, so I give that a save. And once again, given we have a Post, and the User likes that Post, and we're going to expose it in this way. Well, when I then fetch the likes relationship, we should have exactly one User who likes the given Post. Let's give it a run. And it's going to fail, of course. Called undefined method post like. It sounds like that's our first step. Alright. Like, and then we also said there's a likes relationship. Okay, well, let's think about what the database structure is. Let's just do it down here. We know that we'll have a post table, right? And that's going to consist of the title as well as the body. And we also know that we have users, and users have an ID and a name, things like that. But now if we want to say a Post can

Designing pivot relationship3:11

going to consist of the title as well as the body. And we also know that we have User, and Users have an ID and a name, things like that. But now if we want to say a Post can be liked, well, it sounds like we need a pivot table. A table where we can record which User ID liked which Post. Like so. Does that make sense? Now, if we're following standard conventions, like you learned about in the mini-to-mini lesson in this series, we would call this post_user. The singular form of both tables in alphabetical order. P comes before U, so it's post_user. Alright, so in this case, we have a standard belongsToMany relationship. It's not polymorphic yet. So I can say return $this->belongsToMany. And what is the association? Well, when I say post give me the likes, what I'm saying is give me all the Users who like that Post. Like so. Alright, now if we give this a run again, it's still going to fail.

Building pivot table migration4:00

Well, when I say post give me the likes, what I'm saying is give me all the users who like that post. Like so. Alright, now if we give this a run again, it's still going to fail. At this point, it's trying to find that post_user table per the Eloquent convention, but it doesn't exist. So that's our next step. Make me a migration called create_post_user_table. And let's switch over there. CreatePostUserTable. And what do we need here? Well, we said we need the post_id, so that will be an unsigned integer. And we also need the user_id. Now, I'm going to keep the timestamps, but I also want to say, well, these two should be unique. So for example, there should only ever be one occurrence in the database of the user with an ID of one liking the post with an ID of one, right? We should never have multiple records. So what I'm going to do here is set a primary key that consists of the post_id.

with an ID of one liking the post with an ID of one, right? We should never have multiple records. So what I'm going to do here is set a primary key that consists of the post ID and the user ID together. And that will set up the necessary index and the uniqueness. So now I can migrate my database. And we're good to go. Now again, in real life, you might want to set up the necessary foreign key constraints. Let's again keep it fairly simple. So we can move on to the polymorphic portion of the lesson. All right, let's go ahead and run our test again. It's failing. So it's now found to the pivot table, but we still got a size of zero instead of one. So what's happening here is we called postLikes, we expected one item to be returned, but zero were returned. And of course, when we call that like method, nothing happens. So let's say return this likes attach, and we want to say we'll attach

item to be returned, but zero were returned. And of course, when we call that like method, nothing happens. So let's say return this likes attach, and we want to say we'll attach the associated User. Now what we could do, we have a couple options. What we really want to say is the authenticated User should like the given Post. So I often do things like this. Now a lot of people don't like this. They don't like their domain to interfere with sessions and the currently signed in User. You know what, it's up to you. From my experiences, things like this often get squawked at, but very rarely are they the bottleneck. Very rarely are they the big concern for a messy code base. But nonetheless, often a stopgap is you can accept the User, but don't require one. Then you could say, well, $user equals what you gave us or the authenticated User. This kind of gives you the best of both

a stopgap is you can accept the User, but don't require one. Then you could say, well, $user equals what you gave us or the authenticated User. This kind of gives you the best of both worlds because you can still rely on the framework and make use of it, but also for testing or for any other concerns, you can substitute a different User. So any of these options would be fine. Now though, you'll notice we're assuming an authenticated User, but if I switch over to my test, there is no authenticated User. So why don't we at the very top, we'll say this while acting as a User. Let's whip one up real quick. Factory for User, create. Okay, so whip up a new User, set that as the authenticated User. That way, right here, it will reference that signed in person. So if I give our test a run again, we now get green. We could even take it a step further if you want. Right down here, we could say,

it will reference that signed in person. So if I give our test a run again, we now get green. We could even take it a step further if you want. Right down here, we could say, well, this assertContains, or I'm sorry, this assertTrue, and let's say $post, give me all the Users who like it. Let's make sure in that resulting collection, we find a record where the ID is equal to the authenticated's ID. Just to be sure, yes, the User liked it, and yes, it exists within that pivot table, and we still get green. All right, so everything's looking good at this point, but real quick, if we switch over to SQL Pro, we see a couple of things. One, we're writing tests, but it's persisting to our local database. So we have all of these posts, we have these users, it's not being reset. Okay, I'll show you how to fix that. Let's manually delete them to start. And

our local database. So we have all of these Posts, we have these Users, it's not being reset. Okay, I'll show you how to fix that. Let's manually delete them to start. And also, actually, real quick, you'll notice here the timestamps are not being populated. We're going to fix that as well. Let's do it first. Within our Post model, I'm going to say withTimestamps to turn that feature on. Okay, next in our test, I'm going to make sure that I use refreshDatabase, and I'll import that. And this is what we end up with. Okay, so now it's going to roll back after every test. So if I give it a run, we still keep getting green, but now we're not messing up our database with old records. That will end up making future tests fail. All right, so yeah, everything's looking good at this point. You consider this feature done. So then your boss tells you, well, it's cool

Expanding likes to comments8:30

end up making future tests fail. All right, so yeah, everything's looking good at this point. You consider this feature done. So then your boss tells you, well, it's cool that you can now like a Post. You hit a thumbs up button or a heart button. But we want to introduce that functionality for Comments as well. And you think, well, crap, I'm going to have to double all of the work I did. Because in my database, once again, we set up a pivot table to associate a User with a Post. A Post can have many Users, and a User can be associated with many Posts, right? But now, if we want to include Comments, do we do a Comment User pivot table? Like, do we have to double up? Is there any way we can merge this logic? And the answer is yes. If you want to, you can use a polymorphic many-to-many relationship. So in the last episode on standard polymorphic relations, think of that as the polymorphic

And the answer is yes. If you want to, you can use a polymorphic many-to-many relationship. So in the last episode on standard polymorphic relations, think of that as the polymorphic form of a belongsTo relationship. In this episode, it's the polymorphic form of a many-to-many relationship. And you know what? Even as I say, I know it's a little confusing. You just need to dig in and see it for yourself. So let's switch back and think, well, what do we need to do here? Well, the first step is, I'm going to rename this, and I'll say like test, or likingTest. This is the feature that we can like things in our system. Okay, so we still get green if we run this. But now we're going to say, all right, well, a Comment can be liked. And it'll be fairly similar, so I'm going to take some of this here and reuse it. Okay, so given we have a User who has signed in, and given we have

comment can be liked. And it'll be fairly similar, so I'm going to take some of this here and reuse it. Okay, so given we have a User who has signed in, and given we have a Comment, and we don't have a Comment model yet, so we'll need to whip one up. But anyways, if we like the Comment, then the number of likes associated with the Comment should be one. Okay, so I'm going to give that a run, and it should blow up. We don't have a Comment. All right, php artisan, once again, make me a model for Comment, and give me a migration for it, as well as a database factory. Okay, I'll run it again. Still fails because we have an imported Comment. All right, we'll do that now, and run it again. Okay, there's no method like. All right, Comment, add a method like, run it again. Aha, okay, assertCount is not working. Also, if we go to our create_comments_table, we need to populate

no method like. All right, Comment, add a method like, run it again. Aha, okay, assertCount is not working. Also, if we go to our create_comments_table, we need to populate this. So what does a Comment consist of? Well, a Comment is associated with a Post, at the very least, so an unsigned or a positive integer. It needs to be associated with the Post, and we'll add an index to that. Or, if we were to add a standard port and key constraint, the index, of course, would automatically be added. We're just skipping that step to be brief. Next, we need a body for the Comment. We also need things like the Comment belongs to a User. Once again, let's skip some steps here. You can fill those in on your own if you're working along. Next, in my CommentFactory, we'll whip this up. So a Comment consists of the body, and that'll be a paragraph again. And what I'll do here is make it equal

you're working along. Next, in my CommentFactory, we'll whip this up. So a Comment consists of the body, and that'll be a paragraph again. And what I'll do here is make it equal to a function, where I can then return a new Post. So we'll say factory for Post, pull that in, and then give me a persisted, and then return to me the ID, and that's what will be assigned here. All right, so that should do it for our Comment model, our CommentFactory, and the migration to whip up the comments table. But still, if I give this a run, I think it's still going to fail where it did before. Yeah, okay, let's get to work. If we like a Comment, well, it's kind of the same thing, right? It's basically what we had here. But, hmm, let's give it a shot. We're going to duplicate some code, so already we're filing that copy and paste away. We don't have to act upon it yet, but the fact

Switching to polymorphic likes13:05

relationships for liking, and, you know, that might be okay, but what if you take that approach and then your boss says, okay, great, we can now like posts and comments, but we also have this other type of feature or component called a video, and we want users to be able to like the video. Well, now you think, well, I don't want to have three different pivot tables for all the same concept. So again, this is where we can consolidate to a polymorphic many-to-many. Here's what we do. Rather than belongsToMany, I'm going to upgrade that to a polymorphic belongsToMany. All we do is we say morphToMany. Okay, let's give that another run. It shouldn't work, though. All right, so we called morphToMany, and it expected at least two arguments, but we only gave it one. So when we call morphToMany, we need to provide the naming convention we will use in the lookup table. Often we will use the blankable syntax.

least two arguments, but we only gave it one. So when we call morphToMany, we need to provide the naming convention we will use in the lookup table. Often we will use the blankable syntax. So in this case, we might say likeable. In other cases, it might be countable. In other cases, it might be, if you're dealing with tags, maybe tagable. So just often follow that convention, likeable. Okay, let's give it another shot, and it will guide us to the next step. Okay, hey, I was trying to find a likeables table. So notice it took this syntax here, and it's looking for a table that will be the plural form of that. Okay, so it sounds like we need to update our migration. We'll go right here. PostUser is no longer a standard many-to-many. It is a polymorphic many-to-many. So I'm going to change this to create likeables table, and we'll update this in a couple places. Okay, but now notice that we've changed the table name, but there's still

many-to-many. So I'm going to change this to create likeables table, and we'll update this in a couple places. Okay, but now notice that we've changed the table name, but there's still things pointing to a Post and a User. We can no longer do it. Remember, polymorphic means to have many forms. So we can't have specifics here about a Post because, well, what about the Comment? Or what about the Video that could be liked? We need to make this more abstract and generic. So again, this is where this term likeable comes into play. So yes, we have a userId, but we also have not the postId, but the thing to which the User can like. We're going to call that likeableId. Again, notice this matches up with what we provided here. Okay, next, it's not just enough to provide the ID, because we'll think Eloquent still needs to know, all right, well, the ID is for, but what is the thing? What is the type that we're looking up? Is it a Post? Is it a Comment?

just enough to provide the id, because we'll think Eloquent still needs to know, all right, well, the id is for, but what is the thing? What is the type that we're looking up? Is it a post? Is it a comment? Is it a video? So let's provide another one for the likeable type. And traditionally, it doesn't have to be this way, but this would be the Eloquent model. So it would be something like app/Post or app/Comment. Or we could do the morph mini technique like I showed you in the last episode. Finally, what makes this unique? Well, we're going to set the primary key to be, well, it's the combination of the userId, the likeableId, and the likeableType. And this is what we get. All right, let's give it another run. Now remember, because, and it does pass, yay, but remember, because we're using refreshDatabase, even though you're not seeing me manually migrate the database, it's still going to work. And that's because it's doing it in memory behind the scenes. For local

because we're using refreshDatabase, even though you're not seeing me manually migrate the database, it's still going to work. And that's because it's doing it in memory behind the scenes. For local testing, when you're actually opening a browser, of course, you would want to rerun your migrations. But yeah, we give our test another run, and it passes. However, let's go back to this one, and if we run that if host can be liked, it's going to fail. And that's because it's still whipped up for a mini to mini relationship. So we're going to update that one. Now I'm noticing though, can I just copy again? Well, let's see. We'll take that, move it over. I'm filing this one away. Run it again. And now that passes, which means if I just run the entire class, let's remove the filter, both of those are now passing. So take a look at this. Let me turn off refreshDatabase so you can see the records that are being created. Okay, let's go to SQL Pro and

let's remove the filter, both of those are now passing. So take a look at this. Let me turn off refreshDatabase so you can see the records that are being created. Okay, let's go to SQL Pro and refresh. So you'll notice that it creates a Post and the necessary Users and the necessary Comments. But now look at the likables table. We now have a way to say, all right, the User with an ID of three liked the Post with an ID of three. Or if we were to change this, the User with an ID of three liked the Comment with an ID of two. Notice this is where the polymorphic aspect comes into play. No longer are we creating a one-to-one connection between a User and Posts that the User likes. Instead, it's abstract. It can have many forms. A User can like a Comment. A User can like a Post. A User can like anything that we introduce into our system six months from now, like a video. We are now future-proof when it comes to liking things. So now the only remaining step, if we want

Extracting reusable likeable trait18:08

A user can like anything that we introduce into our system six months from now, like a video. We are now future-proof when it comes to liking things. So now the only remaining step, if we want to, is to take a look at the similar code. A Comment can like things, so we accept all of this. A Post can like things. And you know what? It's the same thing. So what if we instead deleted this? Our tests are all going to fail, of course. And actually, real quick, let's go back to the liking test and turn refreshDatabase back on. Anyways, it's all going to fail. I'm now going to extract it to a reusable trait. You can use any convention you want. If you want to stick with likable, that's fine. You could use canLikeThings, if you want. Whatever you want. I'll stick with likable. Now, if I give this another run, it's still, of course, going to fail because likable doesn't exist. All right, app/likable.php.

things, if you want. Whatever you want. I'll stick with likable. Now, if I give this another run, it's still, of course, going to fail because likable doesn't exist. All right, app/likable.php. Within the app namespace, we have a trait called likable, and I will paste those in. So now, if I give it another run, we get green, but we've now extracted this code to a reusable trait. So let's do the same thing to Comment. I'm going to delete this, and this is how I often do the refactor. I make a change, I run a test to see red, and then I reference the new code. In this case, the trait. And then I run it again to ensure that I'm brought back to green. Run it. Yes, we now get green, but now that duplicated code is isolated here. Many people don't love traits. They think it's a way to sweep code under the rug and pat yourself on the back, even though it's still there. It's just hidden under the rug. You know what? I think it's quite nice, actually. I don't have an

it's a way to sweep code under the rug and pat yourself on the back, even though it's still there. It's just hidden under the rug. You know what? I think it's quite nice, actually. I don't have an issue with it, and it makes my post a lot more readable. A post is likable, or a comment is likable. I hope you get the idea. All right, so that will do it. Let's go back to our tasks, check off the final one, and with that, our series is complete. So you now understand and can leverage every relationship type that Eloquent provides.

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