Courses

How to Build Laravel 12 API From Scratch

Finishing CRUD: Update, Delete and Resource Controller

Summary of this lesson:
- Implementing PUT and DELETE endpoints
- Handling response codes
- Using apiResource for route grouping

In this lesson, we will finalize our resource Controller with two missing methods: update and delete the category.


First, the update method. The method for the Route is PUT, and in the Controller, the method is usually update.

routes/api.php:

Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
 
Route::get('categories', [\App\Http\Controllers\Api\CategoryController::class, 'index']);
Route::get('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'show']);
Route::post('categories', [\App\Http\Controllers\Api\CategoryController::class, 'store']);
Route::put('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'update']);
 
Route::get('products', [\App\Http\Controllers\Api\ProductController::class, 'index']);

app/Http/Controllers/Api/CategoryController.php:

class CategoryController extends Controller
{
// ...
 
public function update(Category $category, StoreCategoryRequest $request)
{
$category->update($request->all());
 
return new CategoryResource($category);
}
}

In the Controller, we use a Route Model Binding to find a record and the same Form Request for validation. In the update method, we update the category and return the updated category.

In the client, we can send a PUT request to the /api/categories endpoint and pass the ID of an existing category.

In the result, we see the updated data.


Now, let's do the delete.

route/api.php:

Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
 
Route::get('categories', [\App\Http\Controllers\Api\CategoryController::class, 'index']);
Route::get('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'show']);
Route::post('categories', [\App\Http\Controllers\Api\CategoryController::class, 'store']);
Route::put('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'update']);
Route::delete('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'destroy']);
 
Route::get('products', [\App\Http\Controllers\Api\ProductController::class, 'index']);

In the Controller, after the record is deleted, what to return? We don't have the record anymore. We can return a 204 no content status in such cases.

app/Http/Controllers/Api/CategoryController.php:

use Symfony\Component\HttpFoundation\Response;
 
class CategoryController extends Controller
{
// ...
 
public function destroy(Category $category)
{
$category->delete();
 
return response(null, Response::HTTP_NO_CONTENT);
}
}

Or, we can use a shorter syntax.

app/Http/Controllers/Api/CategoryController.php:

class CategoryController extends Controller
{
// ...
 
public function destroy(Category $category)
{
$category->delete();
 
return response(null, Response::HTTP_NO_CONTENT);
return response()->noContent();
}
}

Let's launch the endpoint from the client.

As you can see, there is no content in the preview, and the status is 204 no content.


Finally, let's wrap all the Routes into a resource. In a typical Laravel project for the CRUD Controllers, you would make a resource Route.

Route::resource('categories', \App\Http\Controllers\Api\CategoryController::class);

But with API, there is a specific method called apiResource. This method adds five Routes instead of seven. We don't need the create and edit methods when working with the API.

routes/api.php:

Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
 
Route::get('categories', [\App\Http\Controllers\Api\CategoryController::class, 'index']);
Route::get('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'show']);
Route::post('categories', [\App\Http\Controllers\Api\CategoryController::class, 'store']);
Route::put('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'update']);
Route::delete('categories/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'destroy']);
Route::apiResource('categories', \App\Http\Controllers\Api\CategoryController::class);
 
Route::get('products', [\App\Http\Controllers\Api\ProductController::class, 'index']);
Previous: Validation, Errors and Status Codes
avatar
Briere Mostafa Amine

Thank you.

avatar
You can use Markdown
avatar
Сергій Каліш

when updating and creating data, I would not write : request->all(), but would write the validated data: request->safe()

avatar
Briere Mostafa Amine

or request->validated()

avatar
You can use Markdown
avatar

Delete does not work on categories that has products linked to it and throws "Integrity constraint violation: 19 FOREIGN KEY constraint failed" the migration needs to have onDelete

$table->foreignId('category_id')->constrained()->onDelete('cascade');

avatar

In theory you wouldn't want to delete products as they would be sold. Also, there's a helper cascadeOnDelete()

avatar
You can use Markdown
avatar
You can use Markdown