Refactoring Follow Logic 0:00Alright, things are looking pretty good. I'm now ready to implement the follow me functionality. And then at the end of the video, if you'd like to stick around, we'll do a little bit more CSS work. Alright, let's get started. Now if I go to my user model, we already have a lot of the functionality for following. So grab all the people the current user follows. Follow a user. We also would surely need another one to check if the current user is following. So we could do isFollowing or following, whatever you prefer.We also would surely need another one to check if the current user is following. So we could do isFollowing or following, whatever you prefer. So we'll likely need that as well. So whenever I have roughly three or more related methods on a model like this, I usually extract it to a trait. So why don't we do that now? I'm going to scroll up, and I'm going to add a trait called followable. A user is followable. So let's go ahead and create that trait in my app directory. Alright, and then if I switch back, yeah, we're going to pull some of these membersSo let's go ahead and create that trait in my app directory. Alright, and then if I switch back, yeah, we're going to pull some of these members into that trait. So I can refactor and go down to pull members up if you're working along in phpStorm. And we're going to move these three. And there we go. So now you'll see our user model has nothing related to following. That's all contained in the followable trait. Okay, so that just cleans things up a little bit. Next, a little tip. Route Model Binding Keys 1:14Okay, so that just cleans things up a little bit. Next, a little tip. Right down here, getRouteKeyName. I forgot to mention this. So you'll remember in our routes file, if we want to access a user by something other than the primary key, other than the ID, well, in Laravel 6 and below, you can add this method here. And this specifies the attribute that we should perform the query under. And I showed you this in the last episode, as you see here. However, in Laravel 7 and above, you can remove this.And I showed you this in the last episode, as you see here. However, in Laravel 7 and above, you can remove this. And just real quick, if I refresh, it's not going to work. But we can instead add it directly to your routes file, like this. When you declare your wildcard, add a colon and then the name of the attribute. Now give it a refresh, and this works again. And I think that's a lot cleaner. So we'll stick with that. And it comes with the bonus of cleaning up the user model, which is really important. Because for any application, the user model is the first thing that becomes a God object Building Follow Form 2:05And it comes with the bonus of cleaning up the user model, which is really important. Because for any application, the user model is the first thing that becomes a God object almost every single time. And that's because, think of this, any of this functionality, it always sort of makes sense on the user model. And that's okay, but just be careful because it grows and grows over the years. All right, so let's get started. We'll go to our profile page. And yeah, we're going to get started on the follow me functionality. And at the moment, it is an anchor tag.And yeah, we're going to get started on the follow me functionality. And at the moment, it is an anchor tag. But of course, we know this is going to be a form. So let's update this to a submit button. And then I'll wrap the whole thing within a form. Like so. All right, so now think about it. We're going to submit a POST request to a new endpoint. And at the moment, we have profiles slash a user, right? So we could do something like this.And at the moment, we have profiles slash a user, right? So we could do something like this. That might be okay. Or we could make follow a top level endpoint, and then pass the user through as part of the request. There's so many different ways to do this. But why don't we try this way first? All right. So if I come back and give it a refresh, now our formatting is messed up. And that's because we no longer have an anchor.So if I come back and give it a refresh, now our formatting is messed up. And that's because we no longer have an anchor. We have a different display type. So I'm going to take the parent and tell it to flex, left to right. Okay, so now if I click follow me, it's going to fail, right? We get a 404, because that endpoint doesn't exist. So we'll get started on that. And we'll use some of this. So I'll copy that. And we'll say if you make a POST request to this endpoint, we're going to hit a brand Creating Follows Controller 3:35So I'll copy that. And we'll say if you make a POST request to this endpoint, we're going to hit a brand new controller called follows controller, and a method called store. Okay, so now give it another shot, and it fails again, because that controller doesn't exist. All right. Make a controller. And you'll see I'm moving a little bit more quickly, only because we've covered so much of this at this point. So I expect you to follow along.of this at this point. So I expect you to follow along. Plus we have a lot to cover. So in our store method, we now need to follow the given user, and that'll be part of the request. Or have the authenticated user follow the given user. All right. So we could say auth user, and we have that method follow. So let's pass that in and reformat. And then I'm going to redirect back to the previous page.So let's pass that in and reformat. And then I'm going to redirect back to the previous page. And this is what we get. All right, so I will optimize those imports. All right, so now if we switch back and try it again, actually, we do have one little issue. John Doe already follows Juliet, but we still see that follow me button. So we will have to update that. But real quick, I want to try it out. So Juliet Jenkins is an ID of five, I'm just going to start from scratch. Checking Follow Status 4:45But real quick, I want to try it out. So Juliet Jenkins is an ID of five, I'm just going to start from scratch. So I'm going to remove that record, and a start all over. So if I click it, it works. We're now following Juliet. But again, we're not providing any feedback. So that's our next step. If we come back to our profiles page, right here, I'm going to show you a couple ideas. So first, let's check if the current user is following the given user. In that case, we should say unfollow me.So first, let's check if the current user is following the given user. In that case, we should say unfollow me. Otherwise, right, follow me. So we could do it this way as a single form, or we could do two different forms. And we're going to talk about that in just a second. But first, I want to get this to work. So we will return to our trait, followable. And you'll remember we added this method, but it's empty. So you can do a couple options here. First, you could get a collection of everyone the current user follows, and check if itSo you can do a couple options here. First, you could get a collection of everyone the current user follows, and check if it contains the given user. And that would work. So if I come back and give it a refresh, sure enough, it changes to unfollow. However, the downside here is you are fetching a collection. So always be careful when you access a collection like this. People often do this. They just want to check if something contains or has that. But in doing so, they load a collection of that entire relationship.They just want to check if something contains or has that. But in doing so, they load a collection of that entire relationship. And in this case, I follow three people, so it's irrelevant. But what if I follow 3,000 people? This call right here would give me a illuminate collection of 3,000 user records. And then of that collection, we would check if they contain the given user. So that's something to be aware of. Is it a problem or not? And that will entirely depend on the relationship and how many records it can store. So if you want to change this up, I wish we could just do that.And that will entirely depend on the relationship and how many records it can store. So if you want to change this up, I wish we could just do that. And I think we should be able to do that, but it doesn't work. So instead, we could do a query where we say where the following user ID is this person, the person we are following, and check if that record exists. So now, if I were to come back and refresh, sure enough, it says unfollow me. And we don't yet have the functionality to unfollow. We'll add that in a second. But if I manually remove it, now it says follow me, run it, and now it says unfollow me. Okay, so that would be a more performant way to allow for this. Implementing Toggle Follow 6:55But if I manually remove it, now it says follow me, run it, and now it says unfollow me. Okay, so that would be a more performant way to allow for this. Okay, let's go back to that form. So yeah, like I said, depending upon how you architect your application, there's so many different ways to do this. So for example, we could do a single form here and a single endpoint. And that endpoint will toggle the follow. So if you are following the person, it'll unfollow them. If you're not following them, it will follow them. Or you could do two different forms.If you're not following them, it will follow them. Or you could do two different forms. So you could say this would be the more restful way. So you could say, well, if the authenticated user is following this person, then display one form to unfollow them. Otherwise, display a second form to follow them, right? So you have two different endpoints. One submits a POST request. The other submits a DELETE request. So that would be an option.The other submits a DELETE request. So that would be an option. Another way would be maybe this is a VIEW component, and you're submitting an AXIOS request. Or another way, you might be using a layer of live wire, in which case you have a live wire component that will handle the toggle. So there's so many different ways to do these things. With that in mind, to keep it simple and fairly pragmatic, we're going to stick with a single endpoint that will toggle the follow. Okay, so with that in mind, we're going to go to our follows controller.we're going to stick with a single endpoint that will toggle the follow. Okay, so with that in mind, we're going to go to our follows controller. And it sounds like this should now toggle it. So let me show you the long way, and then maybe we'll refactor to a toggle method. So you could say, well, if the authenticated user is following this person, then unfollow them. So you could have a method called unfollow. Otherwise, follow them. So yeah, see what I mean how this is very long form? I wouldn't recommend, especially after prettier reformats,So yeah, see what I mean how this is very long form? I wouldn't recommend, especially after prettier reformats, I wouldn't recommend that at all. But nonetheless, it would, in fact, work. So if we unfollow, actually, no, it wouldn't work, because we haven't added that method yet. So that's something we should probably provide real quick. To unfollow a user, we do the same thing, but we call detach. So anyways, if we run that again, now we're unfollowing, follow, unfollow, follow, unfollow.So anyways, if we run that again, now we're unfollowing, follow, unfollow, follow, unfollow. Uh, so that works. Great. That's always a good feeling. You get it to work, and then you have this, and we don't want that. So instead, I'm going to open up a second tab here, or section here, and we're going to add a toggle method. I will often create things like this. We're going to toggle a follow for a user.I will often create things like this. We're going to toggle a follow for a user. And this way, you can move some of this logic directly down to the model. Think about it. I could grab all of that and move it in. So now think. I can replace all of the auth user calls with the current instance and reformat. And then we have an if-else, but we could just return early, and then do something like this. So that ends up being a little more palatable, don't you think? Extracting Follow Button Component 9:53and then do something like this. So that ends up being a little more palatable, don't you think? Now you could just say auth user toggle a follow, and we end up with that again. So now, one more time, follow, unfollow. It works. It's something to consider. Now, let's clean things up a little bit. So I'm going to go back to that profile page. And we could do a couple of things here. But why don't we just start by extracting that to a component,And we could do a couple of things here. But why don't we just start by extracting that to a component, like you learned in the last episode? And why don't we just call it a follow button or form? Maybe button. All right. So we go to our components directory, a follow button. So you're starting to see how useful these can be. And that's what we get. The only thing is we will need access to the user.And that's what we get. The only thing is we will need access to the user. All right. So, for example, if I come back and refresh, I don't think it'll work. Yep. It doesn't know what user is. So we're going to pass that through. And we can use this colon syntax here. So like so. So real quick, if I didn't have the colon, it's going to interpret that as the stringSo like so. So real quick, if I didn't have the colon, it's going to interpret that as the string dollar sign user, in which case it's going to fail, because you're trying to access a name property off of a string. So when you add the colon here, very similar to view components, if you're familiar with that. Now we're actually saying, well, I want to interpret this as the user variable, not the user string. Anyways, if I come back and refresh, now that works again. And that would be an option.Anyways, if I come back and refresh, now that works again. And that would be an option. You can refactor this to a component. Now, if you ever need to dig down and tweak that, you have a dedicated place to do that. And again, if you want to research this on your own, on the documentation, or through layer casts, this is called an anonymous blade component. So let's switch back, see how we're doing. Let's go ahead and give Juliet a follow. So she'll now show up in our timeline. And yeah, we're feeling pretty good here. Revisiting Path Helpers 11:43So she'll now show up in our timeline. And yeah, we're feeling pretty good here. So if you'd like to stick around this, I'm going to consider any remaining CSS to be bonus, because not everyone is interested in CSS work. But if you are, oh, you know what? It's because we removed that method. I will tell you the truth. I did not know that would happen. That's from if we removed the get route key name. So I wonder, get route key name.That's from if we removed the get route key name. So I wonder, get route key name. I'm kind of learning on the fly right here. Of course, that probably wouldn't have. Yeah. Okay. So that is something to consider. You still might want to keep that if you want your routes to be simple. So take a look at this. If I go to a single tweet.So take a look at this. If I go to a single tweet. Yeah. So it's nice that you can just say tweet user, and Laravel will automatically look at the route key name to figure out which attribute to append there when building up the string. If I did not have this, it's going to assume the primary key, right? And that's how we end up with that. So I guess I would have to be specific there. And now it comes back. Still something to consider.And now it comes back. Still something to consider. So there might be a use case where you want to keep that get route key name so that your helpers here are a little easier to write. But we're going to stick to it and keep it like that. Or what you can even do, and I've often demonstrated this, I kind of like this. I will often add a path method. So this would be a path, a string that represents the path to the model, whether it's a post or a video or a user or their profile. So then here, you could have a single place where you declare route profile, and thenor a video or a user or their profile. So then here, you could have a single place where you declare route profile, and then this name. So that would be an option as well. So many different ways you can construct these things. But the benefit would be now, rather than always remembering to do this, and if you come back six months from now, you'll forget like, well, what's the wildcard? What do I need to provide to this route? It's often easier to just say, give me the path to this user, or if it helps you profile path, whatever you want. CSS Layout Improvements 13:41It's often easier to just say, give me the path to this user, or if it helps you profile path, whatever you want. So we do this tweet user path. And yeah, it's something I kind of like to do. Anyways, if we come back, that will still work. All right, so now we'll finish up with a little CSS work. I noted in the last episode, this is a little clumsy, the way we positioned it. We kind of made it arbitrary. If you take a look at the comments from the previous episode, one of the commenters had a good suggestion for how to solve this.If you take a look at the comments from the previous episode, one of the commenters had a good suggestion for how to solve this. So we will implement that now. And the basic technique is we're going to place the banner image and the avatar within the same relatively positioned div. And I'll show you what I mean by that. Let's go to the profiles page. We'll scroll up. So this is that Bugs Bunny banner image. And then way down here is the avatar.So this is that Bugs Bunny banner image. And then way down here is the avatar. So we're going to move this up. And we're going to store both of them within a single div. And I'll reformat. OK, so now I will relatively position. And now, well, first, let's just start by removing all of that. We're no longer going to calculate it. And in fact, I'll just set the width here. OK, so now if I place it right at the bottom of this relatively positioned div, and ifAnd in fact, I'll just set the width here. OK, so now if I place it right at the bottom of this relatively positioned div, and if I come back, sure enough, yep, it's right there against the bottom. And you can see it there. All right, so now take a look. What we could do is say, well, move it 50% from the left. And then I've no doubt you've run into this before. You say left 50%, but it's not quite right because you have to account for the width of the image. So you'll often do things where you say, all right, 50% minus one half the width of thisof the image. So you'll often do things where you say, all right, 50% minus one half the width of this element. So in this case, that's why we do calc. 50% minus one half of the image, which is 75 pixels. And that will give you a perfect center. But what we can do instead is use translate. So I could say transform and translate x equal to negative 50% of the width of the element. And then I can do the same thing for translate y. In this case, I want to push it downward 50% of the height of the image.And then I can do the same thing for translate y. In this case, I want to push it downward 50% of the height of the image. And that's what we want. All right, so let's get that going. And with Tailwind, in fact, I didn't even know this, but there are transform helpers. So I could say, first, what do we got? We have negative 50%. So negative translate x one half, which is 50%. And then positive translate y one half. Finally, yeah, one more.And then positive translate y one half. Finally, yeah, one more. Left 50%. So if we come back and give this a refresh, we're still going to get the same thing. But yeah, this ends up being a little more flexible when we start working on the responsive layout. Now, the only remaining thing is I want to push this down a little bit. And we can do that here. And then the last little thing for this lesson, notice right down here, that awkward border there.And then the last little thing for this lesson, notice right down here, that awkward border there. And that's because for every single tweet, we have a border bottom. So if we go to a tweet here, right there. So if I were to remove that real quick, it does solve that border issue. But now none of the tweets have a separator. So here's a little tip. Whenever you're within a loop, like a for each statement with blade, you have access to this loop variable that Laravel makes available to you. And it contains lots of little helper properties, like is this the first iteration of the loop?to this loop variable that Laravel makes available to you. And it contains lots of little helper properties, like is this the first iteration of the loop? Is it the last iteration of the loop? So you can use this for things like, well, if we are in the last item of the loop, we don't need a border. So don't don't spit out anything. Otherwise, we will spit out this. Kind of cool, right? Of course, you can do it directly with CSS using nth child. But especially for utility classes, I think this is a good way to go.Of course, you can do it directly with CSS using nth child. But especially for utility classes, I think this is a good way to go. So now if I come back and refresh, we do get proper borders, but not on the very last one. All right, so we're doing pretty good. We're moving a little quick. You can watch at half speed if you want to hear me drunkenly explain it a bit slower. We have our timeline. We can publish tweets. Oh, you know what? We might. Navigation and Ordering Fixes 17:55Oh, you know what? We might. Yeah, OK. I forgot to update this real quick. So again, this is actually as an aside why it's useful to have tests. And you'll learn about that a little later in your learning. So here we can redirect back to tweets. Or this is often why it's useful to have named routes. Notice we have a name there. So rather than hard coding home, I could instead say redirect to the route called home.Notice we have a name there. So rather than hard coding home, I could instead say redirect to the route called home. But anyways, we have our timeline. We can view our profile. Oh, you know what? That tweets at the bottom when it should be at the top. These are all little things you have to solve. So let's see. If we go to our profile page, we get the tweets there. So here's a little tip.If we go to our profile page, we get the tweets there. So here's a little tip. When you go to your user model, you can set an order by here if you always want it to apply. So you could do that anyway. So if we come back now, we see that one at the top. So what else? We can view our friends. We can unfollow them, in which case they will no longer show up in our timeline. Let's get rid of Montana as well. Now we have a tweet timeline consisting of only John Doe.Let's get rid of Montana as well. Now we have a tweet timeline consisting of only John Doe. Amazing. All right. I'll see you in the next episode.