Урок 03

Роуты

Что такое роут

Роут — правило, которое сопоставляет URL + HTTP-метод с кодом, который должен выполниться.

Аналогия: роут = запись в таблице «если пришёл такой запрос, делай вот это».

Поток:

GET /posts/5 → Router ищет правило → находит → вызывает Controller@show(5)

Где регистрируются

  • routes/web.php — браузер, сессии, CSRF, cookie.
  • routes/api.php — API, без сессий, префикс /api.
  • routes/console.php — команды CLI.
  • routes/channels.php — broadcast.

Простой роут

use Illuminate\Support\Facades\Route;

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

Разбор:

  • Route::get — метод фасада Route.
  • '/hello' — URL.
  • Второй аргумент — замыкание или [Controller::class, 'method'].

HTTP-методы

МетодДействиеПример
GETЧитатьСписок постов
POSTСоздатьНовый пост
PUTОбновить целикомПост полностью
PATCHОбновить частьТолько заголовок
DELETEУдалитьУдалить пост

В Laravel:

Route::get('/posts', fn () => 'Список');
Route::post('/posts', fn () => 'Создание');
Route::put('/posts/{id}', fn ($id) => "Обновление {$id}");
Route::patch('/posts/{id}', fn ($id) => "Частичное {$id}");
Route::delete('/posts/{id}', fn ($id) => "Удаление {$id}");

Несколько методов

match:

Route::match(['get', 'post'], '/form', function () {
    // оба метода
});

any:

Route::any('/webhook', function () {
    // любой метод
});

Параметры роута

Обязательные:

Route::get('/user/{id}', function (string $id) {
    return "ID: {$id}";
});

Несколько:

Route::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) {
    return "{$postId}, {$commentId}";
});

Порядок параметров в URL = порядок аргументов.

Необязательные:

Route::get('/user/{name?}', function (string $name = 'Guest') {
    return "Hello, {$name}";
});

? — опционально. Обязательно значение по умолчанию.

Ограничения параметров

where:

Route::get('/user/{id}', function ($id) {
    return $id;
})->where('id', '[0-9]+');

Если URL не подходит — 404.

Несколько:

Route::get('/user/{id}/{slug}', function ($id, $slug) {
    //
})->where(['id' => '[0-9]+', 'slug' => '[a-z-]+']);

Хелперы:

->whereNumber('id')          // только числа
->whereAlpha('slug')         // только буквы
->whereAlphaNumeric('code')  // буквы + цифры
->whereUuid('uuid')          // UUID
->whereIn('type', ['a', 'b']) // из списка

Глобальные паттерны

В AppServiceProvider::boot():

use Illuminate\Support\Facades\Route;

public function boot(): void
{
    Route::pattern('id', '[0-9]+');
}

Теперь любой {id} — только числа.

Именованные роуты

Имя роута — метка для генерации URL.

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

Использование:

$url = route('dashboard');           // /dashboard

// С параметром
Route::get('/posts/{post}', fn ($post) => $post)->name('posts.show');
route('posts.show', ['post' => 5]);   // /posts/5
route('posts.show', 5);                // то же

// С query
route('posts.show', ['post' => 5, 'tab' => 'comments']);
// /posts/5?tab=comments

Редирект по имени:

return redirect()->route('posts.index');

В Blade:

<a href="{{ route('posts.show', $post->id) }}">Читать</a>

Группы роутов

Группа применяет общие настройки к нескольким роутам.

Префикс

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

Middleware

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

Имя

Route::name('admin.')->group(function () {
    Route::get('/users', fn () => 'Users')->name('users');   // admin.users
    Route::get('/posts', fn () => 'Posts')->name('posts');   // admin.posts
});

Комбинация

Route::middleware('auth')
    ->prefix('admin')
    ->name('admin.')
    ->group(function () {
        Route::get('/users', fn () => 'Users')->name('users');
        Route::get('/posts', fn () => 'Posts')->name('posts');
    });

Домен

Route::domain('admin.example.com')->group(function () {
    Route::get('/users', fn () => 'Users');
});

Динамический поддомен

Route::domain('{account}.example.com')->group(function () {
    Route::get('/dashboard', function ($account) {
        return "Account: {$account}";
    });
});

Вложенные

Route::prefix('api')->group(function () {
    Route::prefix('v1')->group(function () {
        Route::get('/users', fn () => 'API v1 Users');
    });
});

Просмотр роутов

php artisan route:list
php artisan route:list --path=admin
php artisan route:list --method=POST
php artisan route:list --name=posts

Продвинутые приёмы

Fallback

Если ничего не совпало:

Route::fallback(function () {
    return response()->json(['error' => 'Not found'], 404);
});

Роуты для SPA

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

Ловит все пути и отдаёт один шаблон.

⚠️ API-роуты должны быть раньше.

Пропуск CSRF для вебхука

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'webhooks/*',
    ]);
})

Продвинутое — роутинг по файлам

Для больших проектов — разбить по файлам.

bootstrap/app.php:

->withRouting(
    web: __DIR__.'/../routes/web.php',
    api: __DIR__.'/../routes/api.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
    then: function () {
        Route::middleware('web')
            ->prefix('admin')
            ->name('admin.')
            ->group(base_path('routes/admin.php'));
    },
)

Теперь routes/admin.php — отдельный файл.

Практика

Задача: сделать группу роутов для блога.

// routes/web.php

Route::prefix('blog')->name('blog.')->group(function () {
    Route::get('/', [PostController::class, 'index'])->name('index');
    Route::get('/{slug}', [PostController::class, 'show'])
        ->whereAlphaNumeric('slug')
        ->name('show');
    Route::middleware('auth')->group(function () {
        Route::get('/create', [PostController::class, 'create'])->name('create');
        Route::post('/', [PostController::class, 'store'])->name('store');
    });
});

// Использование
route('blog.index');                 // /blog
route('blog.show', ['slug' => 'x']); // /blog/x
route('blog.create');                // /blog/create

Частые ошибки

1. Роут в api.php, но нужны сессии. Сессии только в web.php.

2. Параметры не в порядке:

Route::get('/posts/{post}/comments/{comment}', function ($comment, $post) { });
// Перепутаны местами

3. Забыли ->name():

route('posts.show');   // Route [posts.show] not defined

4. where не работает:

Route::get('/user/{id}', fn ($id) => $id)->where('id', '[0-9]+');
// Если параметр называется иначе — не сработает

5. fallback ловит всё, включая /api/*:

Route::fallback(fn () => '404');
// API тоже вернёт 404

Решение: проверять префикс в fallback.

Итог

  • Роуты — routes/web.php и routes/api.php.
  • HTTP-методы — get, post, put, patch, delete.
  • Параметры — {id}, {id?}.
  • Ограничения — where, whereNumber, whereUuid.
  • Имена — ->name().
  • Группы — prefix, middleware, name, domain.
  • Просмотр — route:list.
  • Fallback — 404.

Дальше — контроллеры.