Limit Authors to Ticket Creators0:00
For this episode, I originally planned to implement the AuthorsController to manage our users. But that's not really what we need to do. Our authors are of course users, but they are just authors. We need a UsersController to manage our users. So I'm going to make a quick little change to our AuthorsController. Because as it currently stands, we are returning all of our users. And not every user is going to have created a ticket. So what I want to do is change this, so that we return only the users that have created tickets.
So what I want to do is change this, so that we return only the users that have created tickets. Therefore, they are authors. So we're going to start by selecting all of the columns from our Users table, and we're going to join the Tickets table on the Users.id is equal to the Tickets.user_id. And this is going to give us just the users that have created tickets. However, this is going to give us a record for every ticket. So that means if a user with an ID of one has created 10 tickets, then we are going to have 10 records for the user with an ID of one. So what we want to do is call distinct.
to have 10 records for the User with an ID of one. So what we want to do is call distinct. That way we get only one record per User, and then we will paginate those. But of course, we need to include our filters. So before we call distinct, let's add in our filter. And that should be fine. And there we go. So this is going to return just our authors, and we're good to go. Now we need our UserController, and we can approach this in a variety of different ways. We could create our AuthorController through artisan, but our AuthorsController was originally
Create UsersController and Policy1:29
Now we need our UserController, and we can approach this in a variety of different ways. We could create our AuthorController through artisan, but our AuthorsController was originally the UsersController. So let's just copy and paste that, and we'll make the necessary changes. So of course, the name of this file is going to be UserController.php. Let's change the name of the class to UserController. And this is going to use the UserPolicy, which of course we need to create. So let's set that policy class to UserPolicy. And let's go ahead and create that class, and we'll do so once again by just copying what we have with TicketPolicy and pasting that.
And let's go ahead and create that class, and we'll do so once again by just copying what we have with TicketPolicy and pasting that. We could use artisan, but this is probably going to be faster, probably. And this policy is going to be very easy, because unlike the TicketPolicy, only users that are able to manage other users are going to have the abilities that they need. So this is going to be very simple. We will simply return where the user has a token that can, in this case, delete. So we will use our abilities and the DeleteUser ability. And we are essentially going to do the same thing for each individual method here. So ReplaceTicket will become ReplaceUser.
And we are essentially going to do the same thing for each individual method here. So ReplaceTicket will become ReplaceUser. The Store method is going to be CreateUser, and there's nothing else inside of here. So we'll just have one line. The Update method is going to be the same thing, to where we check if the user has the ability to update the user. And that's it. Very simple policy. We're done there. And while we are thinking about these things, let's also think about our requests.
Build User Request Validation3:09
We're done there. And while we are thinking about these things, let's also think about our requests. Because we need to create requests for, well, yeah, everything. So let's start with our BaseTicketRequest. Let's copy and paste that. And really, the only thing that we need to address is the mapped attributes. Because of course, we have different attributes here. So we're going to have the Name attribute, the Email attribute, the IsManager attribute, and then the Password attribute. And of course, we have other things, but we'll just leave it as those.
and then the Password attribute. And of course, we have other things, but we'll just leave it as those. And of course, those are going to map to the Name, Email, IsManager, but this is an underscore, and then Password. But one thing that we need to be aware of here is that the passwords that come from the client are going to be in plain text. Because the client isn't going to have, well, whatever we need to store in the database. So they're not going to have our hashing algorithm or anything like that. And we don't want the client to have that. Besides, if this were just a normal web form, that form would be submitting the password
And we don't want the client to have that. Besides, if this were just a normal web form, that form would be submitting the password in plain text. So the client is going to send the password in plain text. We then need to be sure that we hash that before we store it in the database. So we are going to, inside of our forEach loop, if we have that given key, we are going to get the input value here for that key. And then we are going to check if the attribute is equal to password, then we want to bcrypt that value. And then we will set that value to the appropriate attribute in our attributesToUpdate.
that value. And then we will set that value to the appropriate attribute in our AttributesToUpdate. So that way, regardless of what request is made, we be sure that we hash that password. So that should be it. We don't need this messages method anymore because we don't have any special message for any of our fields. And that's good. So we are done with BaseUserRequest. Let's take a look at our ReplaceUserRequest. So let's copy our ReplaceTicketRequest, change the name of the file to ReplaceUserRequest.
Let's take a look at our ReplaceUserRequest. So let's copy our ReplaceTicketRequest, change the name of the file to ReplaceUserRequest. And here are attributes. If we are replacing a user, they are all going to be required. Did I change the name of that? Nope. Don't forget to change the name of the classes as well. So BaseUserRequest and then ReplaceUserRequest. So we want to return true for authorize here. Our attributes, once again, are name, email, isManager, and then we had the password.
So we want to return true for Authorize here. Our attributes, once again, are name, email, isManager, and then we had the password. And these are going to be relatively simple as far as what we need. So everything is required. Name is a string, email is a string, but we can use the email validation. isManager will be a Boolean value, and then password will be a string. So that's going to be good. Yeah. I think we're good here. And in fact, we can take these rules and we can use those to update the StoreUserRequest.
I think we're good here. And in fact, we can take these rules and we can use those to update the StoreUserRequest. We need to return true for the authorize method. And then our rules, because the rules for creating a User is going to be the same as replacing a User. So we can just return those. And let's see. We need to inherit the BaseUserRequest. Let's go back to ReplaceUserRequest. Make sure that we inherit the correct request class.
Let's go back to ReplaceUserRequest. Make sure that we inherit the correct request class. And yep, we are good there. So now we just have the update. And the update request is going to be very similar, except that everything is going to be sometimes. So let's go to UpdateUserRequest. We need to inherit the BaseUserRequest. We need to return true for the authorize method. And then we will simply change these rules from required to sometimes.
Implement User CRUD Methods6:52
We need to return true for the authorize method. And then we will simply change these rules from required to sometimes. And we'll be good as far as our requests are concerned. So now, instead of our UserController, we need a use statement for our UserPolicy. Our index method is going to be what it was before, to where we call user, then filter, pass in our filters, and then we want to paginate. And that's going to be good as far as our index is concerned. We have the author filter here, and we might want to either change that to user filter or create a user filter. But I think for the sake of this process, we can leave it as author filter because really
or create a user filter. But I think for the sake of this process, we can leave it as author filter because really they're going to be one in the same. And that's just something else that you have to see me type out. So I'm going to be lazy. I'm going to use the author filter here. And so next, we have the store method. Now, let's open up the TicketController because a lot of this functionality is going to be the same. We just need to modify it to work with our users instead of our tickets.
the same. We just need to modify it to work with our Users instead of our tickets. So let's copy the code from our store method, and let's paste that inside of the store method for the UserController. We of course need to make some changes here. So instead of working with the Ticket class, we're working with the User class. We are going to return a new UserResource where we create that user passing in mapped attributes. We've already handled that. So we are good to go as far as that class is concerned.
We've already handled that. So we are good to go as far as that class is concerned. We do need the AuthorizationException here so that we can say that you are not authorized. We don't want to update to create that resource. So that should be fine there. And while I'm thinking about it, we do need to modify our UserResource because now we have the isManager. So let's do this. We'll have isManager, and that will be the isManager. So that should get that value in.
We'll have IsManager, and that will be the IsManager. So that should get that value in. We have our show method, which everything's okay there. I want to change that parameter to user, and that'll be fine. Then we have the update method. So let's once again go to our TicketController. Let's get the update method. Let's copy that and paste it inside of our UserController. So here we are going to be working with our userId, and we are going to findOrFail on our User model.
So here we are going to be working with our userId, and we are going to find or fail on our User model. We're going to store that in a $user variable, which we can then update all of these other variable names. We check if we can update the $user. We update the $user. Then we return the UserResource, and we need the ModelNotFoundException being brought in. Otherwise, you're not authorized to update that resource. That's good.
Otherwise, you're not authorized to update that resource. That's good. Now we need the replace method. Let's just whole copy and paste the replace method, and of course, this is going to be a replaceUserRequest. We'll get the userId, which we will then once again call findOrFail on our User model. We'll just change the ticket variable names to user. We want to return a new UserResource, and if that's not found, we'll say User cannot be found. Then we just have the destroy method.
be found. Then we just have the destroy method. So once again, copy and paste. We could type all of this out, but why? Why would we do that? So here we will have our userId. We'll try to find the User with that userId, and we'll just change these variables to user. Delete it. We'll say that the User successfully deleted it. Otherwise, we'll say the User cannot be found.
Add Routes and Register Policy10:30
We'll say that the User successfully deleted it. Otherwise, we'll say the User cannot be found. So that was a lot of copy, pasting, and replacing. So I'm going to just go through these. Everything looks okay. So I think the only other thing that we need to do is set up our routes. So let's go to our v1/api/routes. Let's copy what we have for the tickets, because that's going to be the most straightforward thing that we can do. So let's change tickets to users, and we will change ticket to user.
thing that we can do. So let's change tickets to users, and we will change ticket to user. And then finally, we will change these controller classes to UserController class. And yep, that automatically sets up the replace and update. You know what? Since we're here, let's modify our authors, because we aren't going to be storing or deleting or any authors. Let's go ahead and let's call except here. And then we will say except store, update, and delete. And that should be good.
And then we will say except store, update, and delete. And that should be good. Okay, so we have our UserController. We have all of our UserRequests. We updated our UserResource. We have our UserPolicy. Oh, we need to set up the UserPolicy. We need to register that inside of OfficeServiceProvider. So let's do that. And you know what I should have done here?
So let's do that. And you know what I should have done here? User class, and then I should have done it like this, to where we had app, and then policies, and then V1, and then UserPolicy. So let's just copy that. Let's use that for the ticket. We don't necessarily need to do that for our model. Okay, so now we have controller, we have request classes, we have the resource updated, we have our policy, we have the OfficeServiceProvider set up, and we have our routes. So now we just need to go and test these things.
Test API in Postman12:15
have our policy, we have the OfficeServiceProvider set up, and we have our routes. So now we just need to go and test these things. And inside of Postman, I've already set up those routes. So I reordered things a little bit. I renamed the old users to authors, and I put everything there. But then I created a users collection, and I have these already set up to go. So let's start by sending our request to get our users. We should get, yep, we get our response. manager is zero. I guess that's fine.
Manager is zero. I guess that's fine. Let's get a User with an ID of one. That should get us our Solon Grimes. So we're good there. Well, let's create a User with a POST request. I already have this filled out, name, email, password, and isManager. We will submit that, and we get a new User with an ID of 15. And we have name, email, isManager, and that's great. Password is not included because we didn't want that included.
And we have name, email, isManager, and that's great. Password is not included because we didn't want that included. So we are good to go there. Let's try to update that User. So let's change the URL here to user15. And we're going to change the name to myUsersNewName, and we're going to make them a manager. So we will submit that. And let's see, argumentTwo, tickets, must be of type ticket. That's inside of our policy.
And let's see, argument two, tickets, must be of type Ticket. That's inside of our Policy. And yep, because all of these second arguments are tickets, that's easy enough to change. So we will simply change those to users, and let's change the parameter to model. So we'll find all of these other tickets, which will become users. The tickets will then be model, and we're good to go there. So let's submit that. We can see that the user is indeed updated. However, isManager is false. Why is that?
However, isManager is false. Why is that? The mapping should be fine. Let's go to the base User request, mapping, yes, isManager is isManager, okay? So the only other thing, is it fillable? Nope. So that's the problem. We need to come here. We need to add the isManager to fillable. And something else I'm going to do is change the casts here.
We need to add the isManager to fillable. And something else I'm going to do is change the casts here. Because the response was returning zero or one based upon if the user was a manager, and I don't want that. I want that to be true or false. So what we'll do here then is just say isManager is boolean. Okay, so that should fix that. So now we can submit this. We see now the user is a manager, and the value for that is true. So let's change that all back.
We see now the User is a manager, and the value for that is true. So let's change that all back. So we're going to change its name, and that User is going to be not a manager. So whenever we submit that, all of those changes we made with the patch request are replaced. And then finally, let's delete the User. So we will send that request. The User is successfully deleted. If we try to submit a request to retrieve that User, we see that the model does not exist. So there we have our UserController.
exist. So there we have our UserController. We can now manage our users as long as we are signed in as a manager. Now you might be thinking, well, what about user registration? Shouldn't a User be able to register an account? And to that, maybe, maybe not. It depends upon the application. Because I can tell you from my experience, the applications that I have written for a client have been specific for that client. They want to be able to add the users as they see fit.
client have been specific for that client. They want to be able to add the users as they see fit. And I would imagine in the vast majority of applications that we write, especially if we are writing them for a specific client, or if we are writing a product to make available to multiple clients, that they would want that ability themselves. They wouldn't want anyone to just create an account willy-nilly. So I'm fine with this approach, letting managers manage those users. And so now we are mostly done with our API. Most of the functionality is there. There are, however, some things that I want to talk about.
Most of the functionality is there. There are, however, some things that I want to talk about. Things like documentation, permissions, and just some other things, which we will discuss starting in the next episode.
