Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Admin Endpoint: Create Travels

Premium
14:14

As we have our Admin users created with the Artisan command, we can log in and create Travel records.

Task Description from Client is extremely simple: "A private (admin) endpoint to create new travels".

I'm a fan of doing things in this order:

  1. Create the endpoint without Auth
  2. Only then protect it with Middleware

So this is precisely the plan.


Controller / Route / Request

We generate the Controller:

php artisan make:controller Api/V1/Admin/TravelController

In addition to previously used Api/V1 prefixes, I added the third one, /Admin, to separate the Controllers for administrators.

Then, in the Routes, we add this:

routes/api.php:

use App\Http\Controllers\Api\V1\Admin;
 
// ...
 
Route::get('travels', [TravelController::class, 'index']);
Route::get('travels/{travel:slug}/tours', [TourController::class, 'index']);
 
Route::prefix('admin')->group(function () {
Route::post('travels', [Admin\TravelController::class, 'store']);
});

As you can see, now we have two Controllers with the same name of TravelController, just in different namespaces, so be careful which one you use in the Routes.

Did you know you can import use XXXXX as the entire namespace and then use it as a prefix for the route itself, like Admin\TravelController in my example above?

Alternatively, you can name those Controllers differently for clarity.

Next, let's generate a Form Request class for...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (31 h 16 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

AA
Ali Al Qahtani ✓ Link copied!

In LoginController (invoke method) returns (201) but in test_login_returns_token_with_valid_credentials method should fail because this test assert status (200).

PK
Povilas Korop ✓ Link copied!

Not sure, it's all working properly for me, can you compare your code with my code in the repository?

AA
Ali Al Qahtani ✓ Link copied!

Yes, you are right, I found the difference with my endpoint return custom status Response::HTTP_CREATED so, it return 201 but your code return by default 200.

H
hrsa ✓ Link copied!

What's your opinion on the aliases? To me it looks cleaner and easier to understand in this particular case:

<?php

use App\Http\Controllers\Api\V1\{Admin\TravelController as AdminTravelController,
    Auth\LoginController,
    TourController,
    TravelController};
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::get('travels', [TravelController::class, 'index']);
Route::get('travels/{travel:slug}/tours', [TourController::class, 'index']);

Route::prefix('admin')->group(function () {
    Route::post('travels', [AdminTravelController::class, 'store']);
});

Route::post('login', LoginController::class);

PK
Povilas Korop ✓ Link copied!

Yes, aliases are another option. I'm not against them, it's a personal preference, I'm just not used to them.

J
Joe ✓ Link copied!

In the final test, test_saves_travel_successfully_with_valid_data() it seems as though it's testing for two different things. Is this really the best way to write such a test, or would it be better to seperate into two methods?

PK
Povilas Korop ✓ Link copied!

It's personal preference, to be honest. But maybe people who want to me more strict would advise only one assertion per test, which means splitting that method into two methods, as you're saying. But it's only an opinionated recommendation, not a rule.