In this lesson, we will take on two tasks:
- Implementing Laravel Eloquent API Resource
- Implementing CRUD operations for the
Category
model
At the end of this lesson, we will have a fully working CRUD for the Category
model with transformed API responses:
Creating First Resource
Let's start by creating our first resource using the artisan command:
php artisan make:resource CategoryResource
This will create a scaffold for our resource:
app/Http/Resources/CategoryResource.php
use Illuminate\Http\Request;use Illuminate\Http\Resources\Json\JsonResource; class CategoryResource extends JsonResource{ /** * Transform the resource into an array. * * @return array<string, mixed> */ public function toArray(Request $request): array { return parent::toArray($request); }}
Here, we should focus on the toArray
method. This is where we can transform our model data into the desired format. So let's add our transformation logic:
app/Http/Resources/CategoryResource.php
use App\Models\Category;use Illuminate\Http\Request;use Illuminate\Http\Resources\Json\JsonResource; class CategoryResource extends JsonResource{ /** * Transform the resource into an array. * * @return array<string, mixed> */ public function toArray(Request $request): array { return parent::toArray($request); return [ 'id' => $this->id, 'name' => $this->name, ]; }}
We have also added a mixing annotation to the class. This will help IDE to understand that this resource is related to the Category
model and provide better code completion.
Using Resource in Controller
The last thing we must do is use this resource in our Controller. Let's update our CategoryController
:
app/Http/Controllers/CategoryController.php
use App\Http\Resources\CategoryResource;// ... public function index(){ return CategoryResource::collection(Category::all());}
Now, if we run our Postman request, we should see the transformed response:
If we need to add more fields to the response, we can easily do that by updating the CategoryResource
class:
app/Http/Resources/CategoryResource.php
return [ 'id' => $this->id, 'name' => $this->name, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at,];
Now our response will include created_at
and updated_at
fields as well:
This is a great way to keep our API responses consistent and clean.
Note: Remember to remove the created_at
and updated_at
fields, which are an example of how to add more fields to the response.
Another thing you might have noticed is that we have a difference in response structure.
- Without Resource, we get an array like this
[{...}, {...}, ...]
- With Resource, we get an array like this
{"data": [{...}, {...}, ...]}
The Resource wraps our data in a data
key. It is a good practice as this allows us to add more metadata (like pagination) in the future without changing the structure of the response.
Note: When using typed models (like we will later), we can parse the response.data
into individual models without worrying about the structure of the response and the metadata.
Note 2: You can remove the data
key by following the official documentation
Adding CRUD Operations
Now that we have our API resource, we can finish our CRUD operators for the Category
model.
Let's start by creating our Form Requests:
php artisan make:request StoreCategoryRequestphp artisan make:request UpdateCategoryRequest
For both of them, we need to define the validation rules:
// ... public function authorize(): bool{ return true;} public function rules(): array{ return [ 'user_id' => ['nullable', 'integer'], 'name' => ['required'], ];} // ...
Now, let's add the Logic to our CategoryController
:
app/Http/Controllers/CategoryController.php
use App\Http\Controllers\Controller;use App\Http\Requests\StoreCategoryRequest;use App\Http\Requests\UpdateCategoryRequest;use App\Http\Resources\CategoryResource;use App\Models\Category;use Illuminate\Http\Request; class CategoryController extends Controller{ /** * Display a listing of the resource. */ public function index() { return CategoryResource::collection(Category::all()); } /** * Store a newly created resource in storage. */ public function store(Request $request) public function store(StoreCategoryRequest $request) { return new CategoryResource(Category::create($request->validated())); } /** * Display the specified resource. */ public function show(Category $category) { return new CategoryResource($category); } /** * Update the specified resource in storage. */ public function update(Request $request, Category $category) public function update(UpdateCategoryRequest $request, Category $category) { $category->update($request->validated()); return new CategoryResource($category); } /** * Remove the specified resource from storage. */ public function destroy(Category $category) { $category->delete(); return response()->noContent(); }}
From all of this, there's just few key points to mention:
- We are using
$request->validated()
to get only the validated data from the request. Do not use$request->all()
as it will return all the data from the request and not just the validated data (which can be a security risk). - We are returning
response()->noContent()
for thedestroy
method. This will return a204 No Content
response, the correct response for a successful deletion. For all methods, we are using theCategoryResource
to transform our data before returning it to the client.
And that's it! We have a fully working CRUD for the Category
model with transformed API responses.
In the next lesson, we will focus on handling 404
responses from our API gracefully.
Check out the GitHub Commit
No comments yet…