Khái niệm và sử dụng Route trong Laravel
- 07-08-2023
- Toanngo92
- 0 Comments
Để bắt đầu một ứng dụng Hello World với laravel, hãy đảm bảo các bạn đã biết cách cài đặt môi trường và khởi tạo được ứng dụng Laravel đầu tiên. Xem các bài viết trước để biết thêm chi tiết:
Ngoài ra, bạn nên có kiến thức căn bản về form method, HTTP Request. Nếu bạn chưa nắm bắt được các kiến thức này, có thể tham khảo 2 bài viết dưới trước khi bắt đầu:
Mục lục
Khái niệm Route trong Laravel
Laravel tận dụng design pattern MVC để cung cấp giao diện tương tác cho người dùng.
Trong mẫu kiến trúc này, URI là một loại cấu trúc logic hơn là một vị trí thực tế của các trang Web. Sử dụng Laravel, các lược đồ logic có thể được ánh xạ tới các tài nguyên thực tế một cách dễ dàng bằng cách sử dụng các Route (định tuyến).
Xem thêm: https://laravel.com/docs/10.x/routing
Route là một tính năng thiết yếu trong Laravel cho phép tất cả các request (yêu cầu) của ứng dụng hướng tới controller (bộ điều khiển xử lý logic) tương ứng của nó. Có 4 loại route chính, nằm trong thư mục <appName>\routes:
- Web Route
- Command Line Route (Console)
- broadcast Route
- API Route
Để bắt đầu một ứng dụng web với laravel, chúng ta quan tâm tới file: <appName>\routes\web.php. hoặc <appName>\routes\api.php , với api routes, mặc định các url của chúng ta sẽ có thêm tiền tố API ở đằng trước, và thường được sử dụng trong các ứng dụng microservices, giao tiếp giữa 1 project FE với Laravel chỉ phục vụ BE.
Trong file web.php này sẽ là nơi chứa toàn bộ các định nghĩa xác định các URI(đường dẫn) khi người dùng truy cập đường dẫn hoặc gửi request lên ứng dụng và định tuyến tới Controller (bộ xử lý) xử lý logic tương ứng.
Ngoài ra, bạn vẫn có thể định nghĩa file route riêng nếu mong muốn, bằng cách khai báo file route của bạn trong file: app/Providers/RouteServiceProvider.php
Danh sách các kiểu route trong laravel:
- Route::get nhận resquest với phương thức GET.
- Route::post nhận resquest với phương thức POST.
- Route::put nhận resquest với phương thức PUT.
- Route::delete nhận resquest với phương thức DELETE.
- Route::match kết hợp nhiều phương phức như POST,GET,PUT,..
- Route::any nhận tất cả các phương thức.
- Route::group tạo ra các nhóm route.
- Route::controller gọi đến controller tương ứng mà chúng ta tự định.
- Route::resource sử dụng với resource controller.
Thông thường chúng ta hay sử dụng nhất get,post hoặc có thêm put, delete.
Sử dụng Route trong Laravel
Danh sách cú pháp HTTP Request Route trong laravel
Route::get($uri, $callback) // receive resquest with GET METHOD.
Route::post($uri, $callback) // receive resquest with POST METHOD.
Route::put($uri, $callback) // receive resquest with PUT METHOD.
Route::patch($uri, $callback) // receive resquest with PATCH METHOD.
Route::delete($uri, $callback) // receive resquest with DELETE METHOD.
Route::options($uri, $callback) // receive resquest with OPTIONS METHOD.
Route::match($methods, $uri, $callback) // receive and handle request with method passing by parameter
Route::any($uri, $callback) // receive and handle request with all method
Trong đó:
- $uri: xác định end point (đường dẫn) của route (có thể hiểu giống việc người dùng nhập url trên trình duyệt để truy cập website, đó chính là end point )
- $callback: là một callback (hàm gọi lại) có kiểu dữ liệu callable ( để hiểu sâu hơn, tìm hiểu khái niệm dynamic function call hoặc xem thêm https://www.php.net/manual/en/language.types.callable.php) chứa thông tin Controller và phương thức (hàm) của đối tượng Controller đó để xử lý Logic.
- $methods là một mảng (array) chứa các phương thức mà bạn muốn route này xử lý (áp dụng cho tính huống Route::match )
Ví dụ khai báo 1 route Hello World trong file web.php:
<?php
// file routes/web.php
use Illuminate\Support\Facades\Route;
Route::get('hello', function () {
return "Hello World";
});
Sau khi route được định nghĩa, chạy ứng dụng và test theo route tương ứng:
http://127.0.0.1:<port>/hello
Giao diện web sẽ trả ra văn bản Hello World.
Về bản chất, với phong cách định, chúng ta hoàn toàn có thể trả ra HTML nếu muốn, nhưng thông thường trong dự án Laravel nói chung, chúng ta sẽ định nghĩa một nơi để xử lý logic gọi là Controller, route chỉ mang nhiệm vụ định tuyến tới Controller đó để xử lý.
Cú pháp:
Route::routeMethod($uri, [$controllerName, $method]);
Route::routeMethod($uri, '<ControllerName>@<methodName>'); // old version laravel use this syntax
Trong đó:
- routeMethod là method (vd: get/post/put/delete …) của route.
- $uri là path mà bạn muốn xử lí.
- $controllerName là controller chứa logic.
- $method là phương thức trong controller chứa logic để xử lí cho route đó.
Ví dụ: Mình sẽ tạo một file HelloController trong thư mục app\Http\Controllers
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HelloController extends Controller
{
public function helloWorld()
{
return "<h1>Hello World !</h1>";
}
}
Cập nhật nội dung file web.php:
<?php
use Illuminate\Support\Facades\Route;
Route::get('hello', [\App\Http\Controllers\HelloController::class, 'helloWorld']);
// in older version
// Route::get('hello', 'HelloController@helloWorld');
Với ví dụ trên, giải thích nôm na là chúng ta đang nói với ứng dụng Laravel rằng: nếu người dùng truy cập vào http://<urlungdung>/hello, ứng dụng sẽ tìm tới Controller có tên HelloController và gọi phương thức (hàm) helloWorld() ra để thực thi. Khi truy cập, Laravel sẽ trả ra văn bản HTML theo đúng nội dung return ở Controller. Trong tình huống này, phương thức helloWorld() chỉ làm một logic đơn giản lả return ra một thẻ p với nội dung là “Hello World !”
Tuy nhiên, ở bài này, chúng ta chưa đề cập sâu tới Controller, nó sẽ được nhắc đến ở các phần sau.
Trong một vài tình huống cụ thể, chúng ta cần đăng ký Route đáp ứng nhiều phương thức HTTP. Khi đó, chúng ta có thể sử dụng phương thức match hoặc any. Ví dụ:
Route::match(['get', 'post'], '/', function () {
// ...
});
Route::any('/', function () {
// ...
});
Lưu ý:
Khi xác định nhiều Route chia sẻ trên cùng một URI, các route sử dụng các phương thức get, post, put, patch, delete và options phải được xác định trước các route sử dụng các phương thức any, match và redirect. Điều này đảm bảo request gửi đến được khớp với route chính xác.
Redirect Route (định tuyến chuyển hướng)
Laravel cũng hỗ trợ một số loại route khác, ví dụ như tình huống này là Route Redirect giúp điều hướng thuận tiện mà không cần phải định nghĩa logic phức tạp trong controller khi thực hiện thủ tục chuyển hướng đơn giản, cơ chế hoạt động tương tự như chuyển hướng trình duyệt bằng hàm header() dựng sẵn trong PHP
Route::redirect($uri, $redirectTo, $status)
View Route (định tuyến khung nhìn/view)
Trong một số tình huống cụ thể nào đó, chẳng hạn bạn chỉ cần xây dựng một trang tĩnh mà không cần bất kỳ logic nào, bạn có thể sử dụng phương thức Route::view(). Giống như phương thức redirect, phương thức này cung cấp một lối tắt đơn giản để bạn không phải xác định một route hoặc controller đầy đủ. Phương thức view() chấp nhận URI làm đối số đầu tiên và tên view làm đối số thứ hai. Ngoài ra, chúng ta có thể cung cấp một mảng dữ liệu để chuyển đến view giúp view có thể sử dụng như các biến đơn, dưới dạng đối số thứ ba (tùy chọn).
Ví dụ:
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
Dependency Injection (tiêm phụ thuộc)
Bạn có thể nhập gợi ý bất kỳ phụ thuộc nào theo yêu cầu của route trong hàm callback của route nếu muốn. Các phần phụ thuộc đã khai báo sẽ tự động được giải quyết và đưa vào hàm callback bởi service container của Laravel . Ví dụ: bạn có thể nhập gợi ý clas Illuminate\Http\Request để yêu cầu HTTP hiện tại tự động được đưa vào hàm callback của route và hứng vào biến $request như ví dụ:
use Illuminate\Http\Request;
Route::get('/myroute', function (Request $request) {
// to do
});
CSRF Protection (bảo vệ CSRF)
Lưu ý rằng các biểu mẫu (HTML form) với các phương thức POST, PUT, PATCH, DELETE (các phương thức cập nhật dữ liệu ngoài GET), cần phải bao gồm một trường có tên là CSRF token (sử dụng để bảo mật, tránh XXS attack, spam…). Nếu không có trường này, Request sẽ bị từ chối.
Ví dụ:
<form method="POST" action="/test">
@csrf
</form>
Các lệnh artisan làm việc với route:
Lệnh artisan route:list cung cấp tổng quan về tất cả các route được xác định bởi ứng dụng của bạn:
php artisan route:list
Theo mặc định, route middleware được gán cho từng tuyến sẽ không được hiển thị trong output của lệnh route:list tuy nhiên, chúng ta có thể yêu cầu Laravel hiển thị route middlewảe bằng cách thêm tùy chọn -v vào lệnh:
php artisan route:list -v
Hoặc chỉ hiển thị routes mà bắt đầu với URI yêu cầu với tùy chọn –path. ví dụ:
php artisan route:list --path=api
Ngoài ra, thêm tùy chọn –except-vendor để ẩn các route được xác định bởi bên thứ 3 (một số thư viện bên ngoài được kéo về thư mục vendor thông qua composer) như sau:
php artisan route:list --except-vendor
Hoặc là, sử dụng –only-vendor để yêu cầu laravel chỉ hiển thị các route xác định bởi bên thứ 3, ngược lại với tùy chọn –except-vendor. Ví dụ:
php artisan route:list --only-vendor
Truyền tham số trong route (Route Parameter)
Tham số được yêu cầu (required parameter)
Đôi khi, bạn sẽ cần nắm bắt các phân đoạn của URI trong định tuyến (route) của mình. Ví dụ: bạn có thể cần lấy ID của người dùng từ URL. Chúng ta có thể xử lý bằng cách xác định các tham số cho route:
Route::get('/user/{id}', function (string $id) {
return 'User '.$id;
});
Chúng ta hoàn tooàn có thể xác định nhiều tham số định tuyến theo yêu cầu của định tuyến của bạn:
Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) {
// ...
});
Các tham số định tuyến luôn được đặt trong dấu ngoặc nhọn {} và phải bao gồm các ký tự chữ cái. Dấu gạch dưới (_) cũng được chấp nhận trong tên tham số định tuyến. Các tham số định tuyến được đưa vào các callbacks (hàm gọi lại)/controllers (trình điều khiển) định tuyến dựa trên thứ tự của chúng – tên của các đối số cuộc callbacks (hàm gọi lại)/controllers (trình điều khiển) định tuyến không quan trọng.
Tham số và tiêm phụ thuộc (Parameters & Dependency Injection)
Nếu định tuyến của bạn có các phụ thuộc mà bạn muốn bộ chứa dịch vụ Laravel tự động đưa vào hàm gọi lại (callbacks) của route, bạn nên liệt kê các tham số route sau các phụ thuộc của mình:
use Illuminate\Http\Request;
Route::get('/user/{id}', function (Request $request, string $id) {
return 'User '.$id;
});
Tham số tùy chọn (Optional Parameters)
Đôi khi, bạn có thể cần chỉ định tham số tuyến đường có thể không phải lúc nào cũng có trong URI. Bạn có thể làm như vậy bằng cách đặt ? đánh dấu sau tên tham số. Hãy đảm bảo rằng bạn sẽ cung cấp giá trị mặc định cho biến tương ứng của định tuyến:
Route::get('/user/{name?}', function (string $name = null) {
return $name;
});
Route::get('/user/{name?}', function (string $name = 'John') {
return $name;
});
Ràng buộc biểu thức chính quy (Regular Expression Constraints)
Chúng ta có thể giới hạn định dạng của các tham số định tuyến của mình bằng cách sử dụng phương thức where trên một thể hiện(thực thể) định tuyến. Phương thức where chấp nhận tên của tham số và một biểu thức chính quy xác định cách ràng buộc tham số:
Route::get('/user/{name}', function (string $name) {
// ...
})->where('name', '[A-Za-z]+');
Route::get('/user/{id}', function (string $id) {
// ...
})->where('id', '[0-9]+');
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
Để thuận tiện, một số mẫu biểu thức chính quy thường được sử dụng có các phương thức trợ giúp cho phép bạn nhanh chóng thêm các ràng buộc mẫu vào các định tuyến của mình:
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->whereNumber('id')->whereAlpha('name');
Route::get('/user/{name}', function (string $name) {
// ...
})->whereAlphaNumeric('name');
Route::get('/user/{id}', function (string $id) {
// ...
})->whereUuid('id');
Route::get('/user/{id}', function (string $id) {
//
})->whereUlid('id');
Route::get('/category/{category}', function (string $category) {
// ...
})->whereIn('category', ['movie', 'song', 'painting']);
Nếu yêu cầu gửi đến không khớp với các ràng buộc mẫu định tuyến, phản hồi HTTP 404 sẽ được trả ra.
Ràng buộc toàn cục (Global Constraints)
Nếu bạn muốn một tham số định tuyến luôn bị ràng buộc bởi một biểu thức chính quy nhất định, bạn có thể sử dụng phương thức pattern(). Bạn nên định nghĩa các mẫu này trong phương thức boot() của lớp App\Providers\RouteServiceProvider:
/**
* Define your route model bindings, pattern filters, etc.
*/
public function boot(): void
{
Route::pattern('id', '[0-9]+');
}
Khi mẫu đã được xác định, nó sẽ tự động được áp dụng cho tất cả các định tuyến sử dụng tên tham số đó:
Route::get('/user/{id}', function (string $id) {
// Only executed if {id} is numeric...
});
Dấu ‘/’ được mã hóa (Encoded Forward Slashes)
Thành phần định tuyến của Laravel cho phép tất cả các ký tự ngoại trừ ‘/’ có mặt trong các giá trị tham số định tuyến. Bạn phải cho phép ‘/’ trở thành một phần của trình giữ chỗ của mình một cách rõ ràng bằng cách sử dụng biểu thức chính quy điều kiện trong phương thức where:
Route::get('/search/{search}', function (string $search) {
return $search;
})->where('search', '.*');
Các dấu gạch chéo về phía trước được mã hóa chỉ được hỗ trợ trong đoạn định tuyến cuối cùng.
Đặt tên cho định tuyến (Named Routes)
Các định tuyến được đặt tên cho phép tạo các URL hoặc chuyển hướng thuận tiện cho các tuyến cụ thể. Bạn có thể chỉ định tên cho một định tuyến bằng cách sử dụng phương thức name() ngay sau khi định nghĩa định tuyến:
Route::get('/user/profile', function () {
// ...
})->name('profile');
Bạn cũng có thể chỉ định tên định tuyến cho các hành động của trình điều khiển (controller):
Route::get(
'/user/profile',
[UserProfileController::class, 'show']
)->name('profile');
Lưu ý: tên định tuyến luôn luôn nên là duy nhất
Tạo URL cho các định tuyến được đặt tên
Một khi bạn đã gán tên cho một route nhất định, bạn có thể sử dụng tên của route đó khi tạo URL hoặc chuyển hướng thông qua các hàm trợ giúp route và redirect của Laravel:
// Generating URLs...
$url = route('profile');
// Generating Redirects...
return redirect()->route('profile');
return to_route('profile');
Nếu định tuyến được đặt tên xác định các tham số, bạn có thể chuyển các tham số làm đối số thứ hai cho hàm định tuyến. Các tham số đã cho sẽ tự động được chèn vào URL được tạo ở đúng vị trí của chúng:
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1]);
Nếu bạn truyền các tham số bổ sung trong mảng, các cặp key/value đó sẽ tự động được thêm vào chuỗi truy vấn của URL được tạo:
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
// /user/1/profile?photos=yes
Đôi khi, bạn có thể muốn chỉ định các giá trị mặc định trên toàn bộ yêu cầu cho các tham số URL, chẳng hạn như ngôn ngữ hiện tại. Để thực hiện điều này, bạn có thể sử dụng phương thức URL::defaults.
Kiểm tra định tuyến hiện tại
Nếu bạn muốn xác định xem yêu cầu hiện tại có được định tuyến đến một định tuyến đã đặt tên hay không, bạn có thể sử dụng phương thức named() trên một thể hiện(thực thể) Route. Ví dụ: bạn có thể kiểm tra tên định tuyến hiện tại từ route middleware:
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->route()->named('profile')) {
// ...
}
return $next($request);
}
Nhóm định tuyến (Route Groups)
Các nhóm định tuyến cho phép bạn chia sẻ các thuộc tính định tuyến, chẳng hạn như middle ware, trên một số lượng lớn các định tuyến mà không cần xác định các thuộc tính đó trên từng định tuyến riêng lẻ.
Các nhóm lồng nhau cố gắng “hợp nhất” các thuộc tính với nhóm cha của chúng. Middleware và điều kiện where() được hợp nhất trong khi tên và tiền tố được thêm vào. Dấu phân cách không gian tên (namespace) và dấu gạch chéo trong tiền tố URI sẽ tự động được thêm vào khi thích hợp.
Middleware (tầng trung gian)
Để gán middleware cho tất cả các định tuyến trong một nhóm, bạn có thể sử dụng phương thức middlewee() trước khi xác định nhóm. Middleware sex được thực thi theo thứ tự chúng được liệt kê trong mảng:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// Uses first & second middleware...
});
Route::get('/user/profile', function () {
// Uses first & second middleware...
});
});
Controllers (Trình điều khiển)
Nếu một nhóm các định tuyến đều sử dụng cùng một trình điều khiển (controller), bạn có thể sử dụng phương thức của trình điều khiển để xác định bộ trình khiển chung cho tất cả các định tuyến trong nhóm. Sau đó, khi xác định các định tuyến, bạn chỉ cần cung cấp phương thức của trình điều khiển mà chúng gọi:
use App\Http\Controllers\OrderController;
Route::controller(OrderController::class)->group(function () {
Route::get('/orders/{id}', 'show');
Route::post('/orders', 'store');
});
Subdomain Routing (Định tuyến tên miền phụ)
Các nhóm định tuyến cũng có thể được sử dụng để xử lý định tuyến tên miền phụ. Tên miền phụ có thể được chỉ định tham số định tuyến giống như URI định tuyến, cho phép bạn nắm bắt một phần tên miền phụ để sử dụng trong định tuyến hoặc trình điều khiển của mình. Tên miền phụ có thể được chỉ định bằng cách gọi phương thức domain() trước khi xác định nhóm:
Route::domain('{account}.example.com')->group(function () {
Route::get('user/{id}', function (string $account, string $id) {
// ...
});
});
Để đảm bảo có thể truy cập các tuyến miền phụ của bạn, bạn nên đăng ký các định tuyến miền phụ trước khi đăng ký các định tuyến miền gốc. Điều này sẽ ngăn các tuyến miền gốc ghi đè các định tuyến miền phụ có cùng đường dẫn URI.
Route Prefixes (Tiền tố định tuyến)
Phương thức prefix() có thể được sử dụng để thêm tiền tố cho mỗi định tuyến trong nhóm với một URI nhất định. Ví dụ: bạn có thể muốn thêm tiền tố vào tất cả các URI định tuyến trong nhóm bằng ‘admin’:
Route::prefix('admin')->group(function () {
Route::get('/users', function () {
// Matches The "/admin/users" URL
});
});
Route Name Prefixes Tiền tố tên định tuyến
Phương thức name() có thể được sử dụng để thêm tiền tố vào mỗi tên định tuyến trong nhóm bằng một chuỗi đã cho. Ví dụ: bạn có thể muốn thêm tiền tố vào tên của tất cả các định tuyến trong nhóm bằng ‘admin’. Chuỗi đã cho được thêm tiền tố vào tên định tuyến chính xác như được chỉ định, vì vậy chúng tôi chắc chắn sẽ cung cấp phần đuôi ký tự ” . ” trong tiền tố:
Route::name('admin.')->group(function () {
Route::get('/users', function () {
// Route assigned name "admin.users"...
})->name('users');
});