Ở bài trước chúng ta đã cài đặt Laravel 13 và tạo xong project TaskFlow. Tiếp tục với series, bài này mình sẽ giới thiệu với mọi người về route trong Laravel 13: cách khai báo route, truyền tham số, ràng buộc tham số và đặt tên cho route. Cuối bài TaskFlow sẽ có những route đầu tiên của nó.
1. Route là gì?
Route (định tuyến) là phần quyết định với mỗi request gửi đến thì ứng dụng sẽ xử lý bằng đoạn code nào. VD: khi truy cập /projects thì trả về danh sách project, khi truy cập /projects/1 thì trả về chi tiết project có id là 1.
Các route của ứng dụng được khai báo trong thư mục routes và được nạp bởi phương thức withRouting() trong file bootstrap/app.php. Trong đó file routes/web.php chứa các route cho giao diện web, được gắn nhóm middleware web (có sẵn session và chống CSRF). Route cho API (routes/api.php) sẽ được nói ở bài Xây dựng API với Sanctum (Bài 23).
2. Khai báo route cơ bản.
Để khai báo một route, các bạn sử dụng facade Route với cú pháp như sau:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
Route::query($uri, $callback);
Trong đó:
- Tên phương thức (
get,post...) là HTTP method mà route sẽ nhận. Routegetnhận cả methodHEAD. Cònquerydùng cho HTTP methodQUERY(cho phép gửi kèm body khi truy vấn dữ liệu). $urilà đường dẫn mà route xử lý.$callbacklà closure hoặc controller sẽ được gọi khi request khớp với route. Trong bài này mình dùng closure, controller sẽ có ở Bài 5.
VD: Mình khai báo route /hello trong file routes/web.php.
Route::get('/hello', function () {
return 'Xin chào TaskFlow!';
});
// Truy cập http://localhost:8000/hello
// Output: Xin chào TaskFlow!
Closure trả về chuỗi thì Laravel trả về chuỗi đó, còn nếu trả về một mảng thì Laravel sẽ tự chuyển thành JSON.
VD: Route /projects của TaskFlow, tạm thời trả về một mảng cố định (khi học đến Eloquent ở Bài 15 chúng ta sẽ lấy từ database).
- routes/web.php
Route::get('/projects', function () {
return [
['id' => 1, 'name' => 'Website toidicode.com'],
['id' => 2, 'name' => 'TaskFlow API'],
];
});
// Output: [{"id":1,"name":"Website toidicode.com"},{"id":2,"name":"TaskFlow API"}]
Route nhận nhiều HTTP method.
Nếu muốn một route nhận nhiều method, các bạn dùng Route::match() với tham số đầu tiên là mảng các method. Còn Route::any() sẽ nhận tất cả các method.
Route::match(['get', 'post'], '/contact', function () {
return 'Trang liên hệ';
});
Route::any('/ping', function () {
return 'pong';
});
Chú ý: Các route POST, PUT, PATCH, DELETE trong routes/web.php được bảo vệ chống CSRF. Gửi request POST đến /contact mà không có CSRF token sẽ nhận về lỗi 419. Cách gửi form kèm token mình sẽ nói ở bài Request và Response (Bài 8).
Route redirect và route view.
Với những route chỉ chuyển hướng sang đường dẫn khác, các bạn không cần viết closure mà dùng luôn Route::redirect():
Route::redirect('/du-an', '/projects'); // status 302
Route::redirect('/du-an', '/projects', 301); // tự chọn status
Route::permanentRedirect('/du-an', '/projects'); // status 301
Tương tự, route chỉ cần trả về một view thì dùng Route::view() với tham số thứ hai là tên view, tham số thứ ba (không bắt buộc) là mảng dữ liệu truyền vào view:
Route::view('/about', 'about');
Route::view('/about', 'about', ['name' => 'TaskFlow']);
View và Blade sẽ được nói chi tiết ở Bài 6.
3. Tham số của route.
Để lấy một phần của URI, VD id của project trong /projects/1, các bạn đặt tên tham số trong cặp ngoặc {}. Giá trị của nó sẽ được truyền vào closure.
VD: Route xem chi tiết project của TaskFlow.
Route::get('/projects/{id}', function (string $id) {
return 'Chi tiết project #'.$id;
});
// Truy cập /projects/1
// Output: Chi tiết project #1
Một route có thể có nhiều tham số:
Route::get('/projects/{projectId}/tasks/{taskId}', function (string $projectId, string $taskId) {
return "Task #$taskId của project #$projectId";
});
// Truy cập /projects/3/tasks/9
// Output: Task #9 của project #3
Lưu ý: Tên tham số chỉ nên gồm chữ cái và dấu gạch dưới _. Các tham số được truyền vào closure theo thứ tự xuất hiện trong URI, tên biến trong closure không cần trùng với tên tham số.
Tham số không bắt buộc.
Để một tham số có thể có hoặc không, các bạn thêm dấu ? sau tên tham số và nhớ gán giá trị mặc định cho biến tương ứng trong closure.
Route::get('/users/{name?}', function (?string $name = 'khách') {
return 'Xin chào '.$name;
});
// Truy cập /users => Xin chào khách
// Truy cập /users/Tai => Xin chào Tai
4. Ràng buộc tham số.
Ở ví dụ trên, truy cập /projects/abc vẫn khớp với route, dù id phải là số. Để giới hạn định dạng của tham số, các bạn dùng phương thức where() với tham số đầu tiên là tên tham số, tham số thứ hai là biểu thức chính quy (regular expression).
Route::get('/tags/{tag}', function (string $tag) {
return 'Tag: '.$tag;
})->where('tag', '[a-z-]+');
// /tags/laravel => Tag: laravel
// /tags/Laravel => 404
Nếu request không thỏa mãn ràng buộc, Laravel sẽ coi như không có route nào khớp và trả về lỗi 404. Với nhiều tham số, các bạn truyền vào một mảng: ->where(['id' => '[0-9]+', 'name' => '[a-z]+']).
Laravel có sẵn một số phương thức cho các trường hợp hay gặp, không cần tự viết regex:
whereNumber('id'): chỉ chứa chữ số.whereAlpha('name'): chỉ chứa chữ cái.whereAlphaNumeric('name'): chữ cái và chữ số.whereUuid('id'),whereUlid('id'): đúng định dạng UUID, ULID.whereIn('status', [...]): chỉ nhận một trong các giá trị cho trước (có thể truyền vào các case của một enum).
VD: Áp dụng cho TaskFlow, id của project phải là số, còn trạng thái chỉ nhận todo, doing, done.
Route::get('/projects/{id}', function (string $id) {
return 'Chi tiết project #'.$id;
})->whereNumber('id');
// /projects/1 => Chi tiết project #1
// /projects/abc => 404
Route::get('/status/{status}', function (string $status) {
return 'Trạng thái: '.$status;
})->whereIn('status', ['todo', 'doing', 'done']);
// /status/doing => Trạng thái: doing
// /status/archived => 404
Ràng buộc chung cho mọi route.
Nếu tham số {id} ở route nào cũng phải là số, thay vì gọi whereNumber('id') ở từng route, các bạn khai báo một lần bằng Route::pattern() trong method boot của AppServiceProvider:
- app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Route;
public function boot(): void
{
Route::pattern('id', '[0-9]+');
}
Lúc này mọi route có tham số tên id đều chỉ nhận chữ số. TaskFlow mới có một route như vậy nên mình vẫn dùng whereNumber('id').
5. Đặt tên cho route.
Mỗi route có thể được đặt một cái tên bằng phương thức name(). Khi cần tạo URL hay redirect đến route đó, chúng ta dùng tên thay vì viết cứng đường dẫn. Sau này có đổi URI thì chỉ cần sửa ở một chỗ.
Route::get('/projects/{id}', function (string $id) {
return 'Chi tiết project #'.$id;
})->whereNumber('id')->name('projects.show');
Mình đặt tên các route của TaskFlow theo dạng nhóm.hành-động (projects.index, projects.show), cũng là quy ước Laravel dùng cho resource controller.
Chú ý: Tên của route phải là duy nhất trong toàn ứng dụng.
Để tạo URL từ tên route, các bạn sử dụng hàm route() với cú pháp:
route($name, $parameters);
Trong đó:
$namelà tên của route.$parameterslà mảng giá trị cho các tham số của route. Những key không phải tham số của route sẽ được thêm vào query string.
route('projects.index'); // http://localhost:8000/projects
route('projects.show', ['id' => 5]); // http://localhost:8000/projects/5
route('projects.show', ['id' => 5, 'tab' => 'tasks']); // http://localhost:8000/projects/5?tab=tasks
Còn để redirect đến một route có tên, các bạn dùng to_route() (hoặc redirect()->route()):
return to_route('projects.show', ['id' => 1]); // chuyển hướng (302) sang /projects/1
6. Xem danh sách route với route:list.
Khi ứng dụng có nhiều route, các bạn có thể xem toàn bộ route bằng lệnh php artisan route:list. Sau khi thêm route cho TaskFlow, file routes/web.php của mình như sau:
- routes/web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
})->name('home');
Route::get('/projects', function () {
return [
['id' => 1, 'name' => 'Website toidicode.com'],
['id' => 2, 'name' => 'TaskFlow API'],
];
})->name('projects.index');
Route::get('/projects/{id}', function (string $id) {
return 'Chi tiết project #'.$id;
})->whereNumber('id')->name('projects.show');
Route::redirect('/du-an', '/projects');
Chạy lệnh route:list với option --except-vendor để ẩn các route do framework và package tạo ra (như up, storage/{path}):
php artisan route:list --except-vendor
# GET|HEAD / ................................... home › routes/web.php:5
# ANY du-an ............... Illuminate\Routing › RedirectController
# GET|HEAD projects .................. projects.index › routes/web.php:9
# GET|HEAD projects/{id} ............. projects.show › routes/web.php:16
#
# Showing [4] routes
Mỗi dòng cho biết HTTP method, URI, tên route và nơi xử lý (closure ở dòng nào của file nào, hoặc controller). Các bạn để ý route redirect nhận mọi method (ANY) và được xử lý bởi RedirectController có sẵn của Laravel.
Một số option khác:
-v: hiển thị thêm middleware của từng route (-vvđể xem chi tiết các middleware trong nhóm).--path=projects,--name=projects,--method=GET: lọc theo URI, tên route, HTTP method.
7. Lời kết.
Như vậy chúng ta đã tìm hiểu xong phần cơ bản của route trong Laravel 13: khai báo route, tham số, ràng buộc, đặt tên route và xem danh sách route. Các bạn có thể xem thêm tại tài liệu chính thức. Bài sau mình sẽ giới thiệu tiếp về nhóm route, route model binding, fallback route và giới hạn số request (rate limit).
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!





0 Comments