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

Creating a Policy0:00

So let's take a look at refactoring this logic here, which is authorization logic, in order to ensure that we can use it across the backend, the frontend, and anywhere that makes sense in our application. And as I mentioned at the end of the last episode, Laravel has tools that allow us to perform this refactor very easily. They're known as policies. Let's go ahead and create one using php artisan make:policy. This will open up our little selector, and we'll call this the CommentPolicy, seen as that's what it links to under the hood. A policy relates to a specific model, and you'll see that in more detail in just a moment. We have to select the model that the policy should apply to. Obviously that will be Comment. And once that's done, well, why don't we go and take a look at our newly created CommentPolicy? As you take a look through the methods that Laravel has already

will be Comment. And once that's done, well, why don't we go and take a look at our newly created CommentPolicy? As you take a look through the methods that Laravel has already created on the policy, you'll note that they sort of correlate to our CRUD methods. So view any would kind of be like the index page. Can you see any Comment at all? view is sort of like our show page. Could you see one individual Comment? This specific Comment passed to the method here. Can you create a Comment? Can you update a Comment? Can you delete a Comment? Et cetera, et cetera. You see how they correlate and how we could easily then match those up to specific CRUD actions that we'll take in our application? Note that they all return booleans as well. It's very easy to write the logic in order to check that a User can execute a given action. So in the case of delete, well, let's go back to our Comment

Authorizing in Controller1:35

booleans as well. It's very easy to write the logic in order to check that a User can execute a given action. So in the case of delete, well, let's go back to our CommentController. Here was our original logic, right? So let's take that and let's paste it in. Of course, we could get rid of the if statement and we could return directly. But rather than doing a negative check, so not equal to, we'll make it a positive check. And we can use the User object that's been passed into the method like so. And this becomes our final authorization check. So you can delete this comment if your User ID is equal to the comment's user_id. That's how simple the check is. Now, how do we then update our CommentController to make use of that policy? Well, I'm going to remove this existing code and I'm going to use the authorize method that ships with the Controller class. So authorize

CommentController to make use of that policy? Well, I'm going to remove this existing code and I'm going to use the authorize method that ships with the Controller class. So authorize is available on any controller. I'll say, are you authorized to delete the given Comment? Now because I'm using Laravel IDEA, I can actually then jump into this specific implementation. You can see it has been wired up correctly. How do we actually register this policy? Well, you can register it from the AuthServiceProvider. So here we have this policies array and you could say that the Comment fully qualified class name is linked to the CommentPolicy. But here's the really cool thing. You don't have to manually register any policies because Laravel will auto register them for you as long as you've chosen sensible naming conventions. So if we take a look at our file tree, we've named this policy CommentPolicy and the model.

Laravel will auto register them for you as long as you've chosen sensible naming conventions. So if we take a look at our file tree, we've named this policy CommentPolicy and the model is called Comment. So as long as your policies are the model name followed by the word policy, Laravel will be able to automatically figure out which one it actually relates to and it will do all of the registration for you. So cool. We can actually see if our updated code has worked by going to our destroy test and running these tests again. And if it's a successful refactor, well, all of our tests will still pass. Just to show you that that is the case, if we jump back into our CommentController and comment this authorized line out and run this once more, well, now we have a failing test. So proof that this refactor was successful and already this controller is looking much cleaner. But of course, at

Exposing Permissions via Resource3:53

out and run this once more, well, now we have a failing test. So proof that this refactor was successful and already this controller is looking much cleaner. But of course, at the moment, we've basically just moved an if statement from one location to another. The real power of this is being able to call that policy method from anywhere in our code base. Now, one of the things we said we need to do is pass down that information from the back end to the front end. How do we do that? Let's jump into the CommentResource, which is that final translation layer between a Comment model and the JSON that will be sent to the front end. What if we put the permissions for a Comment inside here, which would mean that any time we access them from the front end, we know whether we can perform certain actions on this resource or not. We could call an array can, so the final functionality

that any time we access them from the front end, we know whether we can perform certain actions on this resource or not. We could call an array can, so the final functionality in JavaScript would be something like comment.can.delete. That would be really nice. So inside this array, we'll have the delete key. And how do we actually check? Well, we need some way to call the commentPolicy method here. And thankfully, Laravel's authorization tool set comes into play again. You can perform any of these checks on any User model, and we can grab a User model from the request. So we'll set this to request()->user(), and then we'll use an optional because you could technically be a guest viewing a comment. Then we'll call can, and we can check if you can delete this resource. This resource is the underlying Comment model, and that will be the case for any resource that you are making use of in

can, and we can check if you can delete this resource. This resource is the underlying Comment model, and that will be the case for any resource that you are making use of in the application. Let's get rid of this little piece of code here, and let's see if this works. I'll jump into the browser itself. Let's refresh, and I'll open up my view dev tools. So here's view. Here we have our comments data, and let's open up the first item where we have our new can array. We have delete, and it's set to true. And that makes sense because, well, here we have a Comment written by me. But if we go into the second Comment, we go to the can object. delete is set to false because it was created by someone else. So very, very simple to be able to easily pass down authorization logic from the back end to the front end, and now all of our authorization logic is stored in one simple place. If I

Using Permissions in Frontend6:14

So very, very simple to be able to easily pass down authorization logic from the back end to the front end, and now all of our authorization logic is stored in one simple place. If I return true from here all of the time, well, if we go back to view dev tools and we take a look at the data, I'll go to the second item in our array. Now you can delete that one as well. Okay. Now that we have our can array in place, why don't we make use of it from the front end. Let's open our Comment view component. We can get rid of this canDelete method here, and instead we'll reference the comment directly on this v-if. So comment.can, accessing that array, and then optionally we'll check for a delete property. And I say optionally because if down the line we decide to remove that item or optionally load in that item for performance reasons, then we're not going to break all of our front end at

optionally because if down the line we decide to remove that item or optionally load in that item for performance reasons, then we're not going to break all of our front end at the same time. Just trying to think ahead a bit in case we make that change down the line and there's no real downside to adding this now rather than later. Hopefully if we go to our front end again and I refresh, you can see sure enough we have delete available on my Comment from two hours ago, but delete is not available on any of the comments that I didn't create. So refactor complete. Now our front end and our back end is completely reliant on a single piece of logic. And if we ever need to change that, we jump into the CommentPolicy and we make the updates here. So one thing I mentioned in the previous episode is that you shouldn't be allowed to delete a comment if it's over an hour old.

Adding Time-Based Deletion Rule7:44

the comment policy and we make the updates here. So one thing I mentioned in the previous episode is that you shouldn't be allowed to delete a comment if it's over an hour old. Well, let's go to our destroyed test and let's create a test that would allow that to be the case. We can actually copy this previous test seen as most of the logic is the same. We'll change the name. It prevents deleting a comment posted over an hour ago. And then we create our comment. We want to act as the comment's user so that in every other case it's correct. And then we want to assert forbidden, but we need to change when the comment was actually created. There are a few ways to do this, but here's my favorite. The first thing I'm going to do is freeze time. That is to say I will lock Carbon in place so that it's easier to deal with timestamps reliably inside our test. Then I'm going to travel

thing I'm going to do is freeze time. That is to say I will lock Carbon in place so that it's easier to deal with timestamps reliably inside our test. Then I'm going to travel forward an hour after creating the comment. So we create the comment and we travel forward in time by one hour. That should be everything I need to make sure that this works. Let's run the test. Obviously it fails because currently there is no problem. But if we go back to that comment policy and again, just one place to update this code, I can add a new check-in. So not only do you have to have created the comment, but the comment's created_at timestamp has to be after, so isAfter is a method on Carbon, now sub an hour. And that should be the whole check. Let's refresh. The test is now passing and let's run all of those tests again and yeah, they're all passing. And now that we have those tests passing, I just want

the whole check. Let's refresh. The test is now passing and let's run all of those tests again and yeah, they're all passing. And now that we have those tests passing, I just want to refactor this code slightly because I hate using inline checks. I think it makes the code difficult to read and my little brain doesn't cope with it very well. So I'm going to do an early return here. Let's say if, and we'll make this a negative check. So if $userId is not equal to the $comment->userId, we'll return false. And then we're going to return this statement here, whether the $comment->createdAt is after now()->subHour(). All of our tests still pass. So we have a successful refactor on our hands. Let's see if this actually works. So I've not refreshed the page yet, but you can see that this hello world comment here was posted two hours ago. So when I refresh that delete button should

Verifying Behavior and Next Steps9:59

if this actually works. So I've not refreshed the page yet, but you can see that this hello world comment here was posted two hours ago. So when I refresh that delete button should disappear and you can see that it does. But if I add a comment, hello world again, and we post that, I can delete this comment because it posted less than an hour ago. And I am the author of that comment. How cool was that? Policies are so powerful and you can check for those particular policy methods anywhere in the application. Last thing we want to do when it comes to comments, at least for now, is some styling. So for example, this delete button doesn't look like a button at all. We wouldn't even know that it was an action at the moment. We'll go ahead and clean that up in the next episode and we'll talk about a little UI bug with deleting a comment and pagination that's currently

an action at the moment. We'll go ahead and clean that up in the next episode and we'll talk about a little UI bug with deleting a Comment and pagination that's currently present in our application. See you in the next episode.

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