Back to Course |
Laravel Travel Agency API From Scratch

Editor Endpoint: Update Travel

Another short lesson will be about the endpoint of updating the Travel record by the editor user role. We will mostly re-use the things we had done already in previous lessons.


Task Description from Client

A private (editor) endpoint to update a travel.

That's it. That's all we have.


Controller Method and Route

We already have a method to create Travel by admin user. We will use the same Controller and add the update() method.

app/Http/Controllers/Api/V1/Admin/TravelController.php:

class TravelController extends Controller
{
public function store(TravelRequest $request)
{
$travel = Travel::create($request->validated());
 
return new TravelResource($travel);
}
 
public function update(Travel $travel, TravelRequest $request)
{
$travel->update($request->validated());
 
return new TravelResource($travel);
}
}

We use the same TravelRequest for the validation because the rules are identical.

The difference in using those methods will be in the Middleware:

  • store() should be accessed by the admin role
  • update() should be accessed by both admin and editor roles

Here's the updated Route:

routes/api.php:

Route::prefix('admin')->middleware(['auth:sanctum'])->group(function () {
Route::middleware('role:admin')->group(function () {
Route::post('travels', [Admin\TravelController::class, 'store']);
Route::post('travels/{travel}/tours', [Admin\TourController::class, 'store']);
});
 
Route::put('travels/{travel}', [Admin\TravelController::class, 'update']);
});

As you can see, for the update() method, we use a PUT request with Route::put(). This is a standard practice in REST API systems.

Notice: in this case, we rely on the fact that there are only admin and editor roles, so we can assume that the Travel Update endpoint is accessible to any logged-in user. If the system adds more roles in the future, we will need to add something like role:admin,editor and modify the Middleware to accept a comma-separated role list as a parameter.

We launch it in Postman, and the record is successfully updated!


Automated Test

This one is also simple. We just add one more method to the existing AdminTravelTest file.

tests/Feature/AdminTravelTest.php:

class AdminTravelTest extends TestCase
{
use RefreshDatabase;
 
// ... other methods
 
public function test_updates_travel_successfully_with_valid_data(): void
{
$this->seed(RoleSeeder::class);
$user = User::factory()->create();
$user->roles()->attach(Role::where('name', 'editor')->value('id'));
$travel = Travel::factory()->create();
 
$response = $this->actingAs($user)->putJson('/api/v1/admin/travels/'.$travel->id, [
'name' => 'Travel name',
]);
$response->assertStatus(422);
 
$response = $this->actingAs($user)->putJson('/api/v1/admin/travels/'.$travel->id, [
'name' => 'Travel name updated',
'is_public' => 1,
'description' => 'Some description',
'number_of_days' => 5,
]);
 
$response->assertStatus(200);
 
$response = $this->get('/api/v1/travels');
$response->assertJsonFragment(['name' => 'Travel name updated']);
}
}

We launch the test suite, and it's green! We've finished writing the functionality of the project, which is covered by 19 tests, in total.

As a few final steps, we need to perform a cleanup and write the documentation.


GitHub commit for this lesson: