Laravel 12 Routing Tutorial: Practical Examples for Real Projects (PHP 8.3 Ready)

Categories: Web Development

Let’s skip the theory and wire routes like we actually do in production apps.

In Laravel 12, routing lives mainly in:

routes/web.php      // browser routes
routes/api.php      // stateless API routes

Laravel loads these automatically via the RouteServiceProvider, so you rarely touch bootstrapping.


1. Your First Route (Closure Based)

use Illuminate\Support\Facades\Route;

Route::get('/hello', function () {
    return 'Hello Dev!';
});

Why this works

  • Route::get() registers a route responding to HTTP GET.
  • First argument = URI.
  • Second argument = handler.
  • Closure is fine for testing or tiny endpoints.

When NOT to use closures

Closures cannot be cached using php artisan route:cache. In production apps, always move logic into controllers.


2. Routing to Controllers (Real Usage)

Step 1 — Create controller

php artisan make:controller ProductController

Step 2 — Add method

class ProductController extends Controller
{
    public function index(): string
    {
        return 'Product list';
    }
}

Step 3 — Register route

use App\Http\Controllers\ProductController;

Route::get('/products', [ProductController::class, 'index']);

Why controller routing matters

  • Enables route caching
  • Keeps routes clean
  • Separates HTTP layer from business logic

3. Route Parameters (Dynamic URLs)

Route::get('/products/{id}', function (int $id) {
    return "Product ID: {$id}";
});

Why type-hinting helps (PHP 8.3)

  • Laravel casts route parameters
  • Helps static analysis
  • Prevents accidental string usage

Optional parameter

Route::get('/category/{slug?}', function (?string $slug = null) {
    return $slug ?? 'All categories';
});

4. Validation Constraints (Avoid Bad URLs)

Route::get('/user/{id}', function (int $id) {
    return $id;
})->whereNumber('id');

Why use constraints

Prevents invalid requests from reaching controller logic.

Alternative:

->where('id', '[0-9]+');

5. Named Routes (Essential for Real Apps)

Route::get('/dashboard', function () {
    return 'Dashboard';
})->name('dashboard');

Why naming routes matters

Instead of hardcoding URLs:

route('dashboard');

Benefits:

  • URLs can change without breaking views
  • Required for redirects and policies
  • Cleaner Blade templates

6. Route Groups (Clean Large Projects)

Prefix Group

Route::prefix('admin')->group(function () {
    Route::get('/users', fn () => 'Admin Users');
    Route::get('/reports', fn () => 'Reports');
});

Now URLs become:

/admin/users
/admin/reports

Middleware Group

Route::middleware(['auth'])->group(function () {
    Route::get('/profile', fn () => 'Profile');
});

Why groups matter

  • Avoid repetition
  • Centralized security rules
  • Easier maintenance

7. Resource Routes (CRUD in One Line)

Route::resource('products', ProductController::class);

What Laravel auto-creates

Method URI Action
GET /products index
GET /products/create create
POST /products store
GET /products/{product} show
GET /products/{product}/edit edit
PUT/PATCH /products/{product} update
DELETE /products/{product} destroy

Why resource routes are powerful

  • Standardizes controllers
  • Compatible with policies
  • Works seamlessly with form helpers

8. Route Model Binding (Huge Time Saver)

Route::get('/products/{product}', function (App\Models\Product $product) {
    return $product->name;
});

Why this is better than using ID

Laravel automatically:

  1. Fetches the model
  2. Throws 404 if not found
  3. Injects instance

No manual Product::findOrFail() needed.


9. API Routes Example

Inside routes/api.php:

Route::get('/products', [ProductController::class, 'index']);

Why API routes behave differently

  • Automatically prefixed with /api
  • Stateless middleware
  • No session/cookies

10. Route Caching for Performance

php artisan route:cache

Why this matters

  • Converts routes into optimized PHP array
  • Huge speed improvement in production

Common mistake

If you use closures, caching fails.

Fix:

Move closures into controllers.


Common Routing Errors & Fixes

1. 404 even though route exists

Cause: Cached routes outdated

Fix:

php artisan route:clear

2. Target class does not exist

Cause: Missing import

Fix:

use App\Http\Controllers\ProductController;

Or regenerate autoload:

composer dump-autoload

3. Method not allowed

Cause: Using POST on GET route

Fix: Check HTTP verb or use:

Route::match(['get','post'], '/form', ...);

4. Route model binding returns 404

Cause: Parameter name mismatch

Example wrong:

/products/{id}

Correct:

/products/{product}

Must match variable name + type-hint.