Питання на співбесіді: Маршрутизація
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
9 питань
Маршрутизація визначає, як застосунок відповідає на запити: вона прив'язує URL та HTTP-метод до конкретної дії - замикання або методу контролера.
Маршрути оголошуються у файлах директорії routes:
web.php- веб-інтерфейс із сесіями, cookie та CSRF-захистом.api.php- stateless API (без сесій, з префіксом/api).console.php- команди та розклад.
Laravel підтримує методи для кожного HTTP-дієслова - Route::get, post, put, patch, delete, а також Route::match([...]) і Route::any:
Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
// Групи: спільні middleware, префікс, простір імен
Route::middleware('auth')->prefix('admin')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
Ключові можливості:
{post}- параметр маршруту; його можна одразу резолвити в модель (Route Model Binding).->name('posts.show')- іменований маршрут для генерації URL черезroute('posts.show', $post).Route::resource(...)- одразу 7 RESTful-маршрутів для CRUD.
Переглянути всі зареєстровані маршрути: php artisan route:list.
Route Model Binding автоматично резолвить параметр маршруту в екземпляр моделі - не треба вручну робити findOrFail().
Route::get('/posts/{post}', [PostController::class, 'show']);
// $post - вже знайдена модель
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
За замовчуванням пошук іде за id. Прив'язати за іншим стовпцем: {post:slug}. Якщо запис не знайдено - автоматично 404.
Іменований маршрут має псевдонім, за яким генерують URL - замість хардкоду шляху.
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
route('posts.show', $post); // у PHP
<a href="{{ route('posts.show', $post) }}">Деталі</a>
Навіщо: якщо URL зміниться (/posts → /articles), достатньо поправити маршрут - усі посилання оновляться автоматично. Також redirect()->route('posts.show', $post).
Route::resource() одним рядком реєструє 7 маршрутів за RESTful-конвенцією:
Route::resource('posts', PostController::class);
| Метод | URL | Дія |
|---|---|---|
| GET | /posts | index |
| GET | /posts/create | create |
| POST | /posts | store |
| GET | /posts/{post} | show |
| GET | /posts/{post}/edit | edit |
| PUT/PATCH | /posts/{post} | update |
| DELETE | /posts/{post} | destroy |
apiResource()- те саме безcreate/edit(для API).->only([...])/->except([...])обмежують набір.
Rate Limiting обмежує кількість запитів за період. Іменовані обмежувачі визначають через RateLimiter::for() (у Laravel 11+ зазвичай у bootstrap/app.php або AppServiceProvider::boot()):
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
Застосування до маршрутів:
Route::middleware('throttle:api')->group(...);
Route::post('/login', ...)->middleware('throttle:5,1'); // 5 за хвилину
При перевищенні - HTTP 429 із заголовками Retry-After та X-RateLimit-*.
Група задає спільні налаштування для кількох маршрутів, щоб не повторювати їх у кожному.
Route::middleware(['auth', 'verified'])
->prefix('dashboard')
->name('dashboard.')
->controller(InvoiceController::class)
->group(function () {
Route::get('/invoices', 'index')->name('invoices.index'); // /dashboard/invoices
Route::get('/invoices/{invoice}', 'show')->name('invoices.show');
});
Що можна спільно задати:
middleware()- автентифікація, верифікація, обмеження частоти;prefix()- префікс URI;name()- префікс імен маршрутів (крапку в кінці пишуть самі);controller()- спільний контролер, тоді в маршруті лише назва методу;domain()- піддомен, зокрема з параметром:{account}.example.com;scopeBindings()- вкладені моделі шукаються через батьківську.
Вкладені групи об'єднують налаштування: middleware додаються, префікси й імена склеюються.
На практиці групи - головний спосіб не забути auth для нового маршруту: він просто потрапляє в захищену групу.
Маршрути перевіряються в порядку реєстрації, і перемагає перший збіг.
Route::get('/posts/{post}', [PostController::class, 'show']);
Route::get('/posts/create', [PostController::class, 'create']); // ніколи не спрацює
Запит /posts/create збігається з першим маршрутом: create стає значенням {post}, прив'язка моделі не знаходить пост - 404.
Два способи виправити:
- Статичні маршрути - вище за параметризовані.
- Обмежити параметр, щоб він не ловив чуже:
Route::get('/posts/{post}', [PostController::class, 'show'])->whereNumber('post');
Route::get('/users/{name}', ...)->whereAlpha('name');
Route::get('/orders/{order}', ...)->whereUuid('order');
Route::get('/category/{type}', ...)->whereIn('type', ['news', 'guides']);
Глобальне обмеження для всіх маршрутів з параметром {id}:
// AppServiceProvider::boot()
Route::pattern('id', '[0-9]+');
Route::resource() реєструє create раніше за {post} саме з цієї причини. Проблема виникає, коли маршрути додають вручну в різних місцях файлу.
Докладніше в документації: Обмеження параметрів регулярними виразами
php artisan route:cache компілює всі маршрути в один PHP-файл у bootstrap/cache. Laravel більше не виконує routes/*.php на кожен запит, і реєстрація маршрутів стає майже миттєвою - на застосунках із сотнями маршрутів це помітно.
Коли він ламається чи заважає:
- Маршрути не оновлюються. Після кешування нові маршрути не з'являються, доки кеш не перебудувати. Тому
route:cache(чиoptimize) виконують на кожному деплої, а локально не вмикають. - Маршрути, що залежать від стану під час реєстрації. Якщо в
routes/web.phpмаршрути генеруються з бази чи з конфігурації, яка змінюється без деплою, кеш «заморозить» їх на момент збирання. - Конфлікт імен. Два маршрути з однаковим ім'ям без кешу мовчки перезаписують одне одного, а при кешуванні команда падає з помилкою - це навіть корисно.
Маршрути на замиканнях у сучасних версіях кешуються (їх серіалізують), але звичайна практика - контролери: їх легше тестувати й шукати.
Діагностика: php artisan route:list показує маршрути з кешу, якщо він є; route:clear прибирає кеш.
Неявна прив'язка за замовчуванням шукає модель за первинним ключем. Є кілька рівнів налаштування - від простого до повного контролю.
Інша колонка для одного маршруту:
Route::get('/posts/{post:slug}', [PostController::class, 'show']);
Інша колонка для моделі завжди:
public function getRouteKeyName(): string
{
return 'slug';
}
Власна логіка - наприклад, лише опубліковані пости для гостей:
public function resolveRouteBinding($value, $field = null): ?Model
{
return $this->where($field ?? $this->getRouteKeyName(), $value)
->when(! auth()->user()?->isEditor(), fn ($q) => $q->published())
->first();
}
null дає 404.
Явна прив'язка в провайдері - коли параметр не відповідає моделі напряму:
Route::bind('user', fn (string $value) => User::where('name', $value)->firstOrFail());
Поряд корисне:
->withTrashed()на маршруті - знаходити м'яко видалені моделі;->missing(fn () => redirect(...))- що робити замість 404;scopeBindings()- вкладена модель шукається через батьківську, і чужий ID не пройде.
Важливо: прив'язка моделі - це пошук, а не авторизація. Перевірку прав робить політика.