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

Introducing API Resources0:00

Okay, next up, we can move on to API resources. I'm a big fan of these. So have a look here at my UsersController. This is where we fetch users and pass them to the client. And you'll see I'm basically grabbing all users, we're paginating them, and then ultimately we pass through the ID, name, and then I also add this canAuthorization property. Don't be confused by through, it's basically map, but if we stuck with map, it would replace the paginator entirely. What I actually want to do is map over the underlying items or collection in the paginator.

but if we stuck with map, it would replace the paginator entirely. What I actually want to do is map over the underlying items or collection in the paginator. So we use through in that case. But yeah, actually, this looks pretty good to me. Remember, whenever you're writing things, you have to distinguish between the complexity and the size of the app you're building. So if I were building something relatively small like this, I don't really have a problem with this at all. However, on the other hand, if you're building medium to larger projects, you might run into some roadblocks.

However, on the other hand, if you're building medium to larger projects, you might run into some roadblocks. So for example, for Laracasts, I use API resources heavily. Think about things like videos or series on the site. Well, I show you those all over the place. So if in every single route where I need to fetch a series, I'm having to basically recreate this array here, that gets pretty cumbersome pretty fast. So instead, we can create a dedicated class that you can think of sort of as a transformation layer.

Generating UserResource1:24

So instead, we can create a dedicated class that you can think of sort of as a transformation layer for an Eloquent model over to its JSON form or representation. All right, let's play around with it. I'm going to run php artisan make:resource. And in this case, we're working with User. So a User model corresponds to a UserResource. Great. So we should find that in the app/Http directory. And here it is, UserResource.

So we should find that in the app/Http directory. And here it is, UserResource. Now, keep in mind, we're following basic conventions. And one of those conventions is where we'll see this, and it'll say, okay, UserResource, you probably have a User model. And we do. But of course, you can override the associated or underlying model if you want to. Okay, so check this out. If I go back to my controller at the top here,

Okay, so check this out. If I go back to my controller at the top here, let's do a couple of things. First, I'm going to grab all of the users in our system, and I will return it directly from the controller. So as you know, that will be converted to JSON automatically by Laravel. So we'll give it a refresh. And, oh, yeah, this actually signals back to the last episode where we talked about serialization concerns. And you'll remember, I noted that temporarily,

where we talked about serialization concerns. And you'll remember, I noted that temporarily, if you're migrating over a large application, you might want to disable serialization entirely for a model. And this is how we could do it. But, yeah, that was just an example. It's not overly necessary in this case. Okay, so now if I come back and refresh, we can see the JSON form of the User model. Great.

Wrapping Models in Resources2:56

we can see the JSON form of the User model. Great. But now let's wrap it in that UserResource. So we'll go back to UserController, and I'm going to wrap this collection in our new UserResource. Now, there's two primary methods you'll reach for here. make is when you need to wrap a single Eloquent model instance in a resource. And collection, of course, is if you have a collection of Eloquent models that need to be wrapped. And that's the one we need in this particular case.

that need to be wrapped. And that's the one we need in this particular case. So now I'm fetching all of our Users, but first I'm wrapping them in this UserResource. So let's see how this changes. I give it a refresh, and it looks similar, but we can see it's a little different. So by default, API resources will wrap your collection items within a data property, which is, again, a very common convention. But, yeah, if you want to disable that, you can definitely do so.

Disabling Data Wrapping3:45

which is, again, a very common convention. But, yeah, if you want to disable that, you can definitely do so. In fact, let me show you right now. If I were to go to AppServiceProvider, we could do it right down here. And all we have to do is say json_resource, and what is the method? Yeah, without wrapping. Okay, so now we're basically saying, Laravel, don't nest any collections within a data property. I don't care about that.

Laravel, don't nest any collections within a data property. I don't care about that. And you'll see now this looks virtually identical to what we had before. But the key thing here is that now we have quite a bit of flexibility in deciding what we return or what relationships to return, if any, or when to return certain relationships and not others. So let's come back, and this was just an example, so I will comment that out. And, yeah, now if I return to my UserResource, this toArray method right now is just returning the model's toArray method.

And, yeah, now if I return to my UserResource, this toArray method right now is just returning the model's toArray method. But we could override that. I could say return only the id of the User. So this is actually pretty cool. You'll see I'm interacting with this resource almost in the same way that I would interact with the Eloquent model. And that's because behind the scenes, this is going to be proxied to the underlying Eloquent model. Let's see if I can find it.

this is going to be proxied to the underlying Eloquent model. Let's see if I can find it. It'll be a magic method. Yeah, right here. So when I try to grab a key, you can see that's being delegated to the underlying resource. And the resource would be the Eloquent model that you're working with. Yeah, that's kind of cool. So, yeah, when we are preparing the array form or the JSON form for our resource, we don't need to do anything like this and then find the User.

So, yeah, when we are preparing the array form or the JSON form for our resource, we don't need to do anything like this and then find the User and then grab the User's ID. Just imagine that you're working with the User because you are. Okay. So let's come back and you'll see that now we'll have a data property where each model includes only the ID. Okay, so now, again, notice the granularity here. We could say when I'm converting a User to its JSON form, I always want the ID maybe.

Conditional Resource Attributes5:42

We could say when I'm converting a User to its JSON form, I always want the ID maybe. I always want the, you know what, I don't even remember what we had before. The name. All right, this name. And now any User that is wrapped in this resource will automatically send through the ID and the name. Now, of course, as you can imagine, there will be situations where you conditionally want to load the email.

Now, of course, as you can imagine, there will be situations where you conditionally want to load the email. So, for example, what if you're displaying a list of Users and you want to do something like this? But the problem is because this is sent through as an AJAX request, everyone would have access to those email addresses, and maybe that's something you don't want. So there could be situations where you show the email address only if the User has certain permissions. Maybe you can see your own email address,

only if the user has certain permissions. Maybe you can see your own email address, but you can't see somebody else's email address. So you could apply that to billing details or administration stuff or use it all over the place for Laravel. You can do that by saying this when. So when a given condition is true, I want you to include the email. So, for example, if I were to... actually, let's just say foo. If I were to force this, now we will always see a foo property with a bar value.

If I were to force this, now we will always see a foo property with a bar value. But if that conditional returns false, then foo will never be included in the result set. Give it a refresh, and now it's not there. So, yeah, you can make this dynamic. You look on the User model. You check their role or their subscription level. And this is how you conditionally load certain properties. Now, on that node, alternatively, you could use this mergeWhen.

And this is how you conditionally load certain properties. Now, on that node, alternatively, you could use this mergeWhen. So, again, I could say some kind of condition and then merge these properties. And this could be useful, again, where we might say if the User we are working with is you, is the signed-in User, then we do want to include things like the email and more account-specific settings. So now, because we're forcing it to true, again, we will always see the email.

Okay, but anyways, let's keep going. We don't need any of this. So we have the ID, the name, and then finally we have this can property. So, yeah, notice your API resource doesn't have to be a one-to-one mapping to your database table. Or in other words, the fact that ID matches up here and name matches there, that's fine. But it doesn't have to. Maybe we want this to be username,

But it doesn't have to. Maybe we want this to be username, and that would be totally fine as well. We've now detached the client-side names and attributes we use from the underlying database representation. Okay, but let's keep it with name. So for can, I believe what we had, let's see, let's get rid of this, we had edit. So let's copy that and bring it over and update this. All right.

So let's copy that and bring it over and update this. All right. I think we're good to go here. So if I come back to UsersController, let's bring back that called UserResourceCollection. And now if I give it a refresh, we've reproduced exactly what we had before, but now we're wrapping it up within an API resource. Cool. So now let's update our code here.

Cool. So now let's update our code here. Let's bring all of that up, and we're going to wrap it in a User resource. Like so. So now I can get rid of through entirely, and this is what we get. All right, cross your fingers, come back, give this a refresh, and it looks like everything's working exactly the way it did before.

give this a refresh, and it looks like everything's working exactly the way it did before. If we open up view dev tools, let's go into our index page, here's our users, and there we go. Now I'll tell you what's cool about this is now if I ever want to change the structure or the representation for a User, I can go to a single location to do that. So maybe we also want to add,

I can go to a single location to do that. So maybe we also want to add, this is something I will sometimes do, I will add a links array for important links related to a User. And maybe we have a link to view a User, or even something like a link to edit the User. I find that things like this can often be incredibly useful. But yeah, we could even just keep it simple, like the path to the User,

But yeah, we could even just keep it simple, like the path to the User, or a link to the User’s profile. We don't really have a profile section here, but if we did, it might be something like profile/, and then their username, or something like that. We don't have a username, so I will stick with an ID. But yeah, you get the idea. You could have a profile, you could even follow a convention,

Single Model and Relationships11:10

Again, things like that can be really useful. So if I come back and refresh, now, excuse me, if we go to our index page, you'll see all of our users now include a link to their profile page. There's that one, here's another one. So let's finish up now, by returning to our UsersController, and right down here where we show a User, yeah, notice that before we were grabbing

and right down here where we show a User, yeah, notice that before we were grabbing a collection of users, but down here, we only want to fetch a single User. So I could say, UserResource, and again, I don't have a collection, we're just going to wrap a single User. And that's all I have to do. Because now, all of the specifics of what I want to provide

It is, in fact, working. So you'll notice right here, that includes all of the data from the resource class. But don't forget, because the default behavior for API resources is to nest the data in this property here, that now broke our view. So we can fix this in one of two ways. The first option is to simply update your view,

The first option is to simply update your view, and we can say, for example, right here, user.data, and you're good to go. Come back, refresh, and now that works. We're not seeing the timestamp, because in our resource class, we didn't pass it through. So if you want to include that,

we didn't pass it through. So if you want to include that, you could do so. You could even format it, if you want, using Carbon. And that way you handle things like that on the server side, if you want. But yeah, that would be one way to solve it. Another option would be to do what we talked about a little earlier.

Another option would be to do what we talked about a little earlier. Simply go to your AppServiceProvider, and disable wrapping. Okay, so now that would fix this problem as well. Notice it still works. But keep in mind, it's not going to break the users page. You can't remove that outermost data layer.

So that's two ways that you could solve that particular problem. But yeah, the main point is that now, by creating a dedicated resource class, we have, so to speak, a single source of truth where we can declare how we transform an Eloquent model to its JSON representation. And as you learned, you can conditionally add attributes. You could conditionally load relationships.

add attributes. You could conditionally load relationships. So I could say, if a User has posts or something like that, then we could pull in the posts, or we could even say something like, if I had a PostResource class, we could delegate here. So now, when we return a User, we include their id and their name,

So now, when we return a User, we include their id and their name, but then we also include all of the Posts that the User created. Or, and you can review the documentation for all of this stuff, but we could do something like this. So this says, if we included posts as part of our database query, or if they've been eager loaded onto the model,

as part of our database query, or if they've been eager loaded onto the model, only on that condition do we want to create this Post relationship. And that way, for some pages where you need to include the User's posts, you're all set to go. But for other pages where you're never going to show their posts, so there's no point in loading that data,

API ResourcesConditional LoadingHandling Pagination

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