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

Define delete authorization0:00

Like I mentioned, we have a big problem. Any User can delete any puppies and we don't want that. We are going to implement some sort of authorization rules to decide who should be allowed to delete a puppy. And well, the rule is pretty simple. To be allowed to delete a puppy, you need to be the owner of the puppy. So in terms of relationships, that means that the user_id field on the puppy record should match the User that is currently logged in, expressed in code.

that the user_id field on the puppy record should match the User that is currently logged in, expressed in code. This could look like this. So in the destroy function here, we could say $owner equals the puppy's user_id field should be matching the request user or the logged in user's id. And here we could say, well if the user is not the owner, then we would abort with a 403, which is not authorized to perform the action.

then we would abort with a 403, which is not authorized to perform the action. So now all these puppies are attached to Simon and I'm going to create a new User. Let's call this one otherSimon, right? So here I'm logged in as otherSimon. And so if I try to delete any of these puppies, I should not be allowed. I'll try to delete princess and it's going to ask me to do it,

I'll try to delete princess and it's going to ask me to do it, but if I do, I will not be allowed to do this and the action is forbidden. That said, if I go and create a Puppy of my own, my puppy fluffy, and I select this beautiful image from the previous video and I add this puppy this time as the owner of the puppy, I should be able to delete it successfully and I just did.

Create Puppy policy1:54

of the puppy, I should be able to delete it successfully and I just did. Alright, so that certainly works. But maybe this authorization rule we've created is something we might want to reuse across the application so we could create a gate, but because this is related to the Puppy model, let's go and create a policy here. Okay, so php artisan make:policy and I will call the policy PuppyPolicy.

Okay, so php artisan make:policy and I will call the policy PuppyPolicy. I believe it's going to ask me if I want to attach it to a model which I want and it's the Puppy, okay? And so it's created this policy file for us and it's gonna be pre-populated with typical actions like viewAny, view, create, update. We are not going to worry about all of these, but we are going to start with the delete here. And so what we wanted this delete function

but we are going to start with the delete here. And so what we wanted this delete function to express here is basically the check that we've done here is the logged in user, the owner of the puppy. So I will copy this code and instead of returning false, which means you never can delete, I will return exactly this expression except we don't need the request here we have access to the user directly.

Use policy in controller2:55

I will return exactly this expression except we don't need the request here we have access to the user directly. So we will replace that with userId. Super simple. And now the huge advantage of this setup is I'm able to check for the ability to perform such task in many, many places in the application. So if I go back in the PuppyController, delete or destroy method, I can replace this check with request()->user() and ask the question in English, can the user delete the puppy?

User and ask the question in English, can the user delete the Puppy? And because puppy refers to the Puppy model, the can question will understand that it's referring to the delete method on the PuppyPolicy. So that's going to behave exactly the same way as before. But obviously this is more reusable and I don't think we need to abstract the easeOwner to just use it once here. Actually I don't quite like it.

to just use it once here. Actually I don't quite like it. So instead I'm going to copy the check to the policy, I'm going to delete this line and I'm going to replace it here. But we actually want the opposite. So I could go negative, but we can also do the opposite of can in English, which is cannot. And so in plain English,

Return errors for UX4:01

which is cannot. And so in plain English, if the user cannot delete the puppy, we are aborting. Let's try it one more time. These are not my puppies because I'm logged in with the other Simon. And so when I try to delete, we get a 403 Forbidden. Now if you wanted a more Inertia experience, you could instead return back and then with errors like so. So we're going to flash an error

you could instead return back and then with errors like so. So we're going to flash an error that says you do not have permission to delete this puppy. And so because we've set up the notification toast for the errors, remember the experience should be a lot nicer when we try to delete a puppy. We are not allowed. We just get this notification telling us, Hey, sorry, but you can't and you shouldn't. And just to test that it still works, I'm going to create a gibberish puppy

And just to test that it still works, I'm going to create a gibberish Puppy because we are going to delete it immediately and now I should be able to delete that one without a problem and I can. Awesome. All right, so we've drastically improved the security and business logic of our application here, but the UX on the front end is still sort of not great. We have this delete button on every Puppy,

Expose permissions to frontend5:04

but the UX on the front end is still sort of not great. We have this delete button on every Puppy, even if we can't delete it. So I think we should only show that if the User can actually delete a Puppy. So what we wanna do is make this delete policy rule accessible on our front end. And we could go a few ways about it. We could put it on the use page hook like we've done for the flash notifications.

We could put it on the use page hook like we've done for the flash notifications. But I think a nice place to put it here is on the Puppy JSON resource that we've created. So whenever we are accessing the data for a Puppy on the front end, we use this Puppy resource. And so wouldn't it be nice if each Puppy has a canDelete, canUpdate and all sorts of authorization rules attached to the data?

can update and all sorts of authorization rules attached to the data? Let's do that. So I'm going to head over to my puppy resource here, which remember is where we set the shape of the data available to the react front end. For example, we've changed the image URL to camelCase from snake_case here. And so we could call that anything that we want. We could call this emissions for example. But we really want to know if a User can

We could call this emissions for example. But we really want to know if a User can or cannot do something. So why don't we call these can and then preparing for having multiple policy rules here, I'm going to have an array. And here the first key will be well the canDelete and let me add the comma so we have access to the request. So the first thing I want to check is do we have a request User?

So the first thing I want to check is do we have a request User? And if we do, let's do a null safe operator. Then we wanna check, can this User delete this puppy? Which in this context is this resource like so and so we want this to be a Boolean. So there's a chance here that this is going to be null. And so let's go ?. ?. false as a fallback. So what I'm hoping out of this is that each puppy,

False as a fallback. So what I'm hoping out of this is that each puppy, whenever we access the resource on the front end, will have, canDelete true or canDelete false. So let's first go and try pretty print the output of the scan properly for a given puppy on the front end I will go in the PuppyList component and specifically in the PuppyCard and maybe after the name and trade here I will have a pre tag. json_encode(puppy->canDelete);

after the name and trade here I will have a pret tag. Jason, that's string, puppy.dot can. So as you can see, uh, this is not recognized by TypeScript and we all know why I need to once again go in the types and update them. And this is where I believe Laravel, Wayfinder, and Ranger is going to be a game changer if we can change stuff in the backend and it's automatically reflected on the front end without us having to manually update the types all the time,

and it's automatically reflected on the front end without us having to manually update the types all the time, that's going to be amazing. Alright, but while we wait patiently here, I will find the Puppy interface and I will add a coPilot tellMeCan property with a delete true false boolean inside of it? Very nice. And so now our Puppy that can is understood properly. So let's go see what the output is.

that can is understood properly. So let's go see what the output is and just to make it a little bit prettier, I'll go null two, which will add some line breaks and indentation formatting. Whoops. Uh, and let me move it after the closing div here so it's on its own line. Nope, needs to move more. Here we go. We should be good here. Alright, so delete is false for every single puppy, which makes sense. But if I create a new

for every single Puppy, which makes sense. But if I create a new Puppy and add a photo and create that or it's more like you can see that this one has delete true, which is fantastic because this is what we want. Take a second to appreciate that we are accessing our policy rules from the front end and all we had to do is pass the can property to the PuppyResource. Super cool and super handy.

to the Jsun puppy resource. Super cool and super handy. And now we can conditionally display the delete button only if we can delete the puppy. And at this point it's a piece of cake. Really. We go here and wrap the puppy delete in a puppy.can.delete, all with TypeScript auto completion. And we are done. Now the delete action is only visible and available if we can in fact delete the puppy.

Now the delete action is only visible and available if we can in fact delete the puppy. How cool is that? All right, we can go clean up our pretty print here, which is the pre tag. And just before we wrap things up, I want to tie this functionality with the bow and finish with a bit of polish. So I want to change the color of this delete button that's a little bit too in your face.

Polish delete button styling9:40

So I want to change the color of this delete button that's a little bit too in your face. And also remember how we wanted to use the processing helper from the useForm hook in the delete action in case it took a little bit longer. I just wanna add a little bit of artificial delay or sleep just to make the action feel more async and then have the loading spinner to show the processing state. And then when it's finally operated, we can close the modal.

to show the processing state. And then when it's finally operated, we can close the model. Let's do these two things and we'll be out of here. All right, I'm going to log out and sign back in with Simon. So I have a few more puppies to delete and test because all of these are mine and I can delete all of them. And so let's start with the delete button. I think we can try the outline or maybe secondary variant and tweak from there. And so we are right here.

or maybe secondary variant and tweak from there. And so we are right here. I wanna change the variant of this delete button to maybe outline. Alright, that's still a little bit too much. I think what I wanna do is have a bit of transparency until we hover over it. So let's try that. We are not going to create our custom variant in the button component. We'll just pass class here directly,

to create our custom variant in the Button component. We'll just pass class here directly, which is totally a thing you can do with most chat CSS and UI components. So class name and let's go bg background, but at 30% opacity. And then on hover bg background, let's take a look. So yeah, that works, but I'm kind of thinking that instead of outline because I don't want a border, I should have used the secondary maybe color or variant.

of outline because I don't want a border, I should have used the secondary maybe color or variant. Sorry, let's try that. Uh, secondary, is it how you spell it? Yes. Alright, that's a bit more like it. And so now we have the sort of properties that we want, but I think I want a bit of red connotation in here. So let's maybe make the icon red. So in the trash icon here, I want the stroke, I believe. Uh, and is the danger, Ooh,

So in the trash icon here, I want the stroke, I believe. Uh, and is the danger, Ooh, stroke destructive is probably what we want. Uh, but uh, but that looks terrible. So maybe we'll make it dark. And on hover we will make it red like this. So I need to give the button a name of group and I can guarantee you there will be other groups nested as parents. So I will go group delete.

nested as parents. So I will go group delete. This is a trick to do scoped group names. And so here I can do on hover, we want stroke destructive, but we want to do it on group hover and then slash the name of the scope, which is delete. So that's a bit complicated, but that's how it works. It's going to check that it has group delete, which is the parent here. So we got a sort of subtle delete icon.

which is the parent here. So we got a sort of subtle delete icon. And then on hover it gets read like this and it still works. I really like this. We don't need to spend more time on this. This is kind of clear and it works and it's a little bit less in your face as before. And so there's one more thing I want to take care of and it's the pending state while the action is pending. And you know, I've said that we were gonna do this

and it's the pending state while the action is pending. And you know, I've said that we were gonna do this to wrap up this video, but I've changed my mind. People are allowed to change their minds. It's a good thing. Uh, we are going to do this in a new video because there's a little bit more to it and I don't want this video to become too long. So stay tuned. Next video, we going to tackle the pending state UI for our delete action.

Next video, we going to tackle the pending state UI for our delete action. See you there.

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