This episode is for members only

Sign up to access "Eloquent Relationships By Example" right now.

Get started
Already a member? Sign in to continue
Playing
14. Creating a postable timeline app

Episodes

0%
Your progress
  • Total: 4h 18m
  • Played: 0m
  • Remaining: 4h 18m
Join or sign in to track your progress

Transcript

00:00
So we're now going to turn everything we know about one-to-many relationships into a working app. This is just going to be a very simple page that looks a little bit like what we've already done, but it's going to have a form to allow us to post these and display them underneath,
00:16
as well as the count. What we will also do, because I think it's incredibly important, is touch on authorization for when we delete posts. So we'll have the ability to delete any of the posts that belong to us that we have in here. So the first thing that we're going to do is just go ahead and comment out all of the routes that we already have in here. So we're starting
00:36
nice and fresh. And we're going to come over to the post index file that we have here. And I'm going to go ahead and delete this out as well, just so we're starting completely from scratch. And of course, now we just end up with nothing here. So we're going to build out a home controller, which is going to show our posts, show the form, and then we'll have a controller to create a post,
00:56
and we'll have a controller to delete a post as well. This is where things start to get really interesting if you're just learning Laravel. This will build up, again, a huge amount of just what you do in everyday applications, posting things, deleting things, listing things. So let's go ahead and start by creating out a controller for our home page. So we're going to go ahead and use
01:18
artisan to make out a controller here. And this is going to be our post index controller, which is going to list our posts, but also show that form as well. So we're going to create out a new route. We'll just put this over on, let's just do this on slash posts. And we're going to have our post index controller in here, the full namespace to this. Again, just pulling this in at
01:39
the top manually if your editor doesn't do that for you. So we can go and head over to our app section under HTTP controllers and post index controller. And we can just start to list through the post that a user has. So let's create out an invoke method in here, which will be called when we hit this page. And again, remember, we don't have any authentication built in here. So I'm
02:05
going to head over to our auth service provider under app and providers. And just down the bottom here, when we boot this, we're going to go ahead and use our auth facade, which again, you need to pull in just here, illuminate support facades and auth. And we're going to say login using ID one, just so we mimic that user being authenticated throughout our entire app. Okay, so we're going
02:30
to go ahead and return a view from here. And this is going to be posts and index. And we need to pass down the user's posts, we can get the user in a couple of different ways. But I tend to always pull this in through the request. So we're going to go ahead and pass in our posts here. And from the request, grab the user, which will give us back a user model, and then access just the posts
02:54
directly from this. Or we can use that latest posts method that we created a little bit earlier. If you were doing this manually, you would go ahead and build up your posts from the user. So you'd say request user posts, latest. And yeah, I'm gonna leave it that actually, because that's pretty much what you would normally do. And then we can just switch this out for posts. Okay, so
03:17
overall, we've got an authenticated user, which we've done manually, we're extracting out the latest posts from the latest at the top, and we're passing them down to this post index view. Let's head over to the slash post page. And of course, at the moment, we don't see anything. So we're going to head down to that index page. And we're going to start to iterate this out.
03:38
So we're going to have a form at the top here. So we'll define that out first of all. And then just underneath it, we're going to have a list of our posts. So we can just say for each posts as post, and that for each there. And then inside of here, we're going to have each of them posts. Again, I'll output the ID just so we can reference this. But of course, in reality,
04:01
you probably wouldn't do that. And we're going to go ahead and output the post body in here as well. So if we head over and give that a refresh, we have exactly what we had before. But of course, we've moved this over to controller and we are assuming the user is always signed in. Okay, let's build out this form that is going to allow us to actually create a post. So inside of here,
04:21
we don't quite know the action yet. So I'm going to go ahead and create out a text area in here with the name of body, an ID of body. And we can set the columns and rows or just leave that as it is. And let's go over and check this out. There we go. Doesn't look great at the moment. Let's maybe just bring back our rows and set them to six. Yeah, that will do. Okay, so the next thing
04:44
is a button with a type of submit in here. And we'll just call this post. There we go. Okay, so again, does not look great, but let's roll with this. Okay, so we need somewhere to post this through to. So we're going to go ahead and create out another controller. Of course, you can put that in the same controller if you're using restful controllers, but I prefer to
05:05
separate these out. And we're going to call this post store controller. And let's go over to our routes to define this out, first of all. So we're going to copy this one down. And we want to post through to posts. And of course, reference the post store controller. Again, just putting this in at the top if your editor doesn't do that. So over in the post store controller now, we're going
05:27
to go ahead and create our invoke method in here. And this is going to, from the request, when we submit that form, take that data and store it for the currently authenticated user. So this is now creating a post. So let's just dive down here and say store, just so we know that this actually hooks through. And let's head over to our index here and hook this up. So we know that the action
05:49
is posts. And the method here is post. And we need to include our cross-site request forgery token for security. So now when I post this, we go through to that route and eventually end up storing this post. So we've got the name here of body. So that will be included in the request through to here. So now we can just start to access the currently authenticated user
06:14
using request user. Or you could even use auth user if you wanted to. It's entirely up to you. And then we're going to go into posts. And then we're going to say create. So remember, we looked at two different ways to create posts or create relationships through this earlier. But we'll use this now. So again, what we can do here is say body and request body if we wanted to. That's
06:38
perfectly acceptable. Or what you can do is just say request only body. And that's going to give you back an array, which will be placed into here with just the body in it. If you have anything more complex that you need to pass through to here, so if you have a very specific field that needs to be calculated or calls another method, you're not going to be able to use this just here.
07:03
It's up to you what you do. So once we do that, we're going to go ahead and return back. And also here, we're going to kind of just introduce the concept of validation if you haven't already touched on this. This is, again, really important because we need this body to actually be filled. So I'm going to go ahead and say this validate past the request
07:21
in which, of course, contains the data from that form. And then we're just going to give out a really basic rule here for the body. And we're going to say that the body is required. So although we're not going to be showing the validation rule just yet, that is going to stop this from happening if the body is empty. So let's go over and just see this in action. So I'm going to say
07:43
a new post, hit post, and there we go. The new post is added in here. Now, the reason that we're seeing that at the bottom, even though we have our latest post scope in there, is just to do the fact that I've been inserting dates manually into here. If I were to get rid of all of these, so let's save this out and completely clear it, we don't see anything here. I'm going to say first
08:07
post. And we'll post that. Let's say second post. And there we go. You can see the ordering is correct. It's just because the timestamps didn't quite match up from me manually adding the date to the database. OK, so we can post now. We can't post if it's not valid. So we would see a validation error here. But let's now focus on deleting the posts that we already have. And we'll
08:33
touch on authorization. So I think this hooks into Eloquent really nicely. OK, so we'll go ahead and create the ability to delete a post. And then we will add in the authorization after. So again, we're going to go ahead and create out a controller in here. And that's going to be post destroy controller, which is just a Laravel convention. But you can, of course, call this anything you
08:56
want. And let's go ahead and just duplicate this down because it's very similar. And we're going to use the delete verb. And we're going to say post slash and then again use root model binding to pull in that post by its ID. So we're going to reference the post destroy controller again, just pulling that in at the top. And we should be good to post this through. So let's go over
09:17
to our post destroy controller, create out our invoke magic method, and just die and dump on the post just to make sure that this is working. So let's die dump on our post. And again, just make sure we pull in the post model at the top here. And let's have a look. So we're going to hook this up to our form here. So now, now that we're iterating for every single post, we want to
09:40
output a button or a link that's going to be clickable to actually delete this. Now remember that when we delete things, we don't want this to be a get type request, which we saw here. We want this to be a delete. So we need to implement a form here for security. So we know that the person who is clicking this button is the person who actually is intending to do this. So we're going
10:02
to create out another form inside of here that is going to go through to post slash and then the post ID. We've not given any of these roots names, but that's fine. And the method here is going to be post. But like we saw a little bit earlier, we're going to add in our cross-site request forgery token, but we're also going to add in the method helper here. And we're going to say that we want
10:24
to spoof the delete method. So now what we can do is add in a button and we can have that as a type of submit. And we're going to say delete. So for every single post now, we are going to have a delete button. And of course, you could style that up as a link or if you're using JavaScript, have a confirmation. It doesn't really matter at the moment, but I can click through here and
10:48
that's the second post that's deleting the right one. And sure enough, that's the first post. So now we can click through to actually delete this. Now, the key thing is here, once we've implemented the ability to delete, we want to put in a policy to make sure that first of all, we can see this delete button for our posts, because remember, this is a kind of public timeline. And second of
11:07
all, we want when we click through, we want that to authorize and make sure that we can only delete this if we own that post. So how are we going to do this? Well, let's focus on the delete functionality first of all. In our case, we don't necessarily need to access this through the relationship because we're going to add authorization. So we can just say post delete and return back. That
11:27
will redirect us back to the previous page. So this will work. So I can delete the second post and it's gone. So I'm going to re-add in second post here so we can have another look. So now we want to authorize this. Remember, what you can do is you could say request. Let's just delete this request user posts and find post first and delete if you wanted to. And now technically, if we just
12:02
bring in our request here, even if we didn't own this post, it would not delete it because we're accessing the currently authenticated user to delete this. So this will still work. But if I were to try and delete a post that didn't belong to me, it would not work. So, for example, if our second post belonged to someone else, let's switch the user over here and save this out and come back
12:25
over. And yeah, we're just seeing our posts at the moment. So we can actually tweak this. So if we head over to our post index controller, let's maybe just switch this out to show all posts. That kind of makes sense, doesn't it? Because this is like a public timeline. So in this case, let's leave this in here for reference. But we're just going to say post and get because we just want to grab all posts
12:47
from everyone that's posted. So we can also say latest get as well to order them. So there we go. We've got all posts in here. And if we delete this one, sure enough, it doesn't work. Remember, we saw that earlier. If we delete the first post, it does work because it belongs to us. Let's leave it like this for now, just so we can see the authorization in action. But then we can always
13:09
switch over just to show our own posts. OK, so now that we have our form and we're going through to this, it's time to go ahead and authorize this. I'm actually going to return this back to post delete. That makes a little bit more sense. And let's add in a little bit of authorization. So to do this, we're going to go ahead and use Artisan to make out a policy. And we're going to call this
13:35
post policy. What this policy will allow us to do is define who can do what to different resources. So our policies live if we just head up here over in the policies directory. And if we open this up, we can add any methods on here that we want. We can say, can we create? Can we delete? Can we update? Whatever we want to do. So we're going to go ahead and create out a method inside of this
14:01
post policy called delete. And this will implicitly take in a user. So we're always assuming that a user is passed into here. And it will also, when we invoke this and try and use this policy, it will bring in the post. So now we've got two things that we can use to check, does this post belong to this user or the other way around? Does this user own this post? So the easiest way to do
14:28
this and the most common way to do this is grab the user's ID and compare that to the user ID that we stored on the post instance or the post model or database record. So now if the user IDs match, we know that they can delete it. So we're just returning here because this method needs to return a true or a false value. So does this match? If it does, we're going to return true,
14:55
which means we can delete it. If this is one and this is two, they don't match. So we're going to return false and we can't delete it. OK, so we're going to register this policy now and then we can add it to our controller and see it in action. So we're going to go over to our auth service provider under app and providers, much like we saw here, and we're going to go ahead and add in
15:16
our policy. Now to add these in, what we need to do is first of all, choose the model that we want to apply this to, the fully qualified namespace to that model. Again, once again, just pulling the namespace in at the top here and then we're going to assign to this either a single value or an array of policies that we have attached to this model. So again, we're going to pull in our post policy
15:38
and the fully qualified namespace to that policy. So if we head up here, we just make sure we pull the policy in. That is all we need to do. So now that we have our policy defined with a condition of whether we can delete this or not, we've registered it inside of our post destroy controller, we can just use this authorize. We can pass in the action. So in this case, it's delete
16:04
and then the model itself. So can the currently authenticated user, which is implicit, remember, delete this post? Well, we have the condition in there. Let's see if this works. So we're going to head over. I'm going to hit delete on my second post, and it looks like we've got an error here. Let's just head over to our policy. In fact, head over to our auth service provider. I don't
16:30
actually think we can put that in an array. So let's just leave that outside of an array and let's just head back here. And yeah, there we go. OK, so we now get this action is unauthorized because the post that we're trying to delete doesn't belong to the currently authenticated user. So that is our authorization, which is incredibly important. Once we pass this,
16:52
we don't need to do anything fancy. We just delete the post because we know this will throw that exception. It will go ahead and stop this user at this point, and it will render out that 403 page for us. So let's switch this back over to our user. And let's see the difference here. So let's head back over to our posts page, hit delete, and sure enough, it disappears.
17:16
OK, so I'm going to create a few more posts just to demonstrate one more thing before we go. So second post, third post. And let's go ahead and assign the second post that we have here to that other user like so. Now, if we look at this as that currently authenticated user with the user ID of one, should they even be able to see this delete button? Probably not.
17:44
So what we're going to do is we're going to use what we've done since we've spoken about authorization, which is incredibly important, to hide this form if the user does not have the ability to delete it. So we can do this very easily if you're working within Blade. We can use the can directive. So we can say can delete post. So it looks exactly the same as our authorization
18:05
within our controller. And we just end that can there. We just wrap that directly within there. So that will check this at the point of rendering, and it will not show this form if we can't delete this post. So sure enough, although the formatting is a little bit off, the delete button has been removed for the second post. Even if we were to click that, it's not going to work because we have
18:26
authorization in place. But we can delete the third post, the first post, the second post is just by that other author. Now, we started this out by just having a timeline of our own posts, which wouldn't be a problem if we wanted to delete them in terms of visually showing the delete button. But whenever you're creating a controller like this that deletes something, it's incredibly
18:51
important to always authorize this. So regardless of whether your posts are directly from that user, you have that endpoint open to delete posts by any ID. So it's really important that you add authorization. It's really difficult to build out example applications without discussing authorization because it is incredibly important. So whether you're just showing the posts on this
19:15
timeline that belong to that authenticated user, or you're showing all posts, always make sure you authorize your controllers. But there we go. We've built a really simple app where we can post. Anyone can log in here. Technically, when we have authentication, anyone can log in and post something. And of course, we have given the ability to delete these, but also authorized as well.
33 episodes4 hrs 18 mins

Overview

Eloquent is Laravel's ORM (Object Relational Mapper). In simple terms, it's how your models work with the database.

The good news? There's a bunch of powerful relationship types available. Our task is to learn when and where to use each one.

In this course, we'll cover each basic relationship type, how to access related models, and then insert, sync, update and delete related data. Oh, and we'll build a practical example for each relationship type, to really make it stick.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Comments

No comments, yet. Be the first to leave a comment.