در لاراول برای هر URI یک روت (Route یا مسیر) تعریف می کنیم. این روت را با متدهای استاتیک کلس Route تعریف می کنیم. وقتی یک درخواست از طریق URI فرستاده میشود، روت درخواست را می گیرد و یک function برای پاسخ به آن فراخوانی می کند. این function می تواند یک closure یا متدی از یک کنترلر باشد. البته بیشتر مواقع برای هندل کردن درخواست ها از یک کنترلر استفاده می کنیم. هر URI ای که برای آن Route ای تعریف نشده باشد به صفحه “404 not found” هدایت می شود. متد های استاتیک Route بر اساس فعل(verb)های http نام گذاری شده اند و انتخاب متد مناسب به نوع درخواست http و کاری که میخواهیم انجام بدهیم بستگی دارد.
در لاراول routeها در فولدر routes در root پروژه ما قرار دارند.
همانطور که مشاهده می کنید درون این پوشه چهار فایل وجود دارد و از بین اینها برای تعریف کردن route، ما با web.php و api.php سروکار داریم. web.php برای توسعه وب اپ و api.php برای توسعه api است. در این مطلب ما با web.php کار خواهیم کرد.
تعریف روت ها
روت به عنوان پارامتر اول آدرس و به عنوان پارامتر دوم یک تابع دریافت می کند. برای پاس دادن متد یک کنترلر به عنوان پارامتر دوم، یک آرایه با دو مقدار پاس می دهیم. مقدار اول را نام کلس کنترلر را به صورت ControllerName::class وارد می کنیم و مقدار دوم را نام متد کنترلر را داخل quotation mark وارد می کنیم. اگر نام کلس کنترلر را به تنهایی بدون براکت وارد کنیم مجیک متد invoke__ درصورتی که تعریف شده باشد از کنترلر فراخوانی می شود.
تعریف روت ها بر اساس جدول بالا: (اولین خط متد invoke__ را صدا می زند.)
use App\Http\Controllers\MainController;
Route::get('/', MainController::class);
Route::get('/posts', [MainController::class, 'index']);
Route::post('/posts' [MainController::class, 'store']);
Route::put('/posts' [MainController::class, 'update']);
Route::patch('/posts' [MainController::class, 'update']);
Route::delete('/posts' [MainController::class, 'destroy']);
همانطور که می دانید درست است برای پاک کردن و بروزرسانی اطلاعات از فعل های DELETE و PATCH استفاده کنیم و همانطور که در کد بالا مشاهده می کنید، روت هم برای پاسخ به هرکدام از این درخواست ها، متد های مخصوص به خودشان را دارد. نکته اینجاست که فرم های html به خودی خود از این فعل ها پشتیبانی نمی کنند و ما در اتربیوت متد فقط می توانیم get یا post را وارد کنیم. برای حل این مشکل ما داخل viewهای blade که فرم را نوشته ایم از (' ')method@ استفاده می کنیم و داخل آن نام متد را وارد می کنیم. به این عمل method spoofing می گویند.
<form action="/posts/{{$post['id']}}" method="POST">
@method('PATCH')
<input type="text" name="title">
@csrf
<input type="submit" value="submit">
</form>
اگر بخواهیم روت مورد نظر فقط یک view را برگرداند می توانیم مستقیما از view route استفاده کنیم.
Route::view('/', 'homepage', [$data]);
یا اگر بخواهیم روت را طوری تعریف کنیم که به یک URI دیگر redirect شود از redirect route استفاده می کنیم. همچنین به عنوان ورودی سوم هم می توان http status code وارد کرد.
Route::redirect('/here', '/there', 301);
همچنین می توانیم یک fallback route هم تعریف کنیم تا در صورت یکسان نبودن ریکوئست با هیچکدام از روت ها، closure ای که تعریف می کنیم اجرا شود. این روت جایگزین صفحه “404 not found” می شود.
Route::fallback(function () {
// code
});
نام گذاری روت ها
برای راحتی redirect کردن می توان به روت مورد نظر یک نام هم اختصاص داد. این کار با chain کردن متد name به خطی که روت را تعریف کردیم انجام می شود.
Route::get('/posts', [MainController::class, 'index'])->name('posts.index')
به این صورت می توانیم از نام روت یک URI تولید کنیم:
$url = route('posts.index');
redirect کردن به یک URI در حالت عادی به صورت زیر انجام می شود:
return redirect(/posts);
و با داشتن نام روت به صورت زیر:
return redirect()->route('posts.index');
گروهبندی روت ها
روت ها قابلیت گروهبندی شدن هم دارند. گروهبندی به ما کمک می کند که ویژگی های روت ها را بین تمام هم گروه ها یشان share کنیم، بدون اینکه نیاز باشد هرکدام را به طور جداگانه روی هر روت تعریف کنیم. مثلا در کد زیر تمام روت هایی که URI آنها پیشوند یکسان دارند را درون یک گروه قرار دادیم:
Route::prefix('admin')->group(function () {
Route::get('/posts', function () {
// Matches The "/admin/posts" URL
});
});
یا روت هایی که پیشوند نام یکسان دارند:
Route::name('admin.')->group(function () {
Route::get('/posts', function () {
// Route assigned name "admin.posts"...
})->name('posts');
});
یا حتی می توان تمام روت هایی که از یک کنترلر استفاده می کنند را در یک گروه قرار داد و داخل روت ها فقط متد مورد نظر از کنترلر را مشخص کرد:
use App\Http\Controllers\MainController;
Route::controller(MainController::class)->group(function () {
Route::get('/posts', 'index');
Route::get('/posts/create', 'create');
Route::post('/posts', 'store');
...
});
از گروههای روت ها میتوان برای هندل کردن مسیرهای subdomain هم استفاده کرد: (درون subdomain می توان مانند URI، پارامتر داد با این روش می توان یک قسمت از subdomain را گرفت و از آن درون کنترلر استفاده کرد.)
Route::domain('{account}.example.com')->group(function () {
Route::get('user/{id}', function (string $account, string $id) {
// code
});
});
گرفتن پارارمتر ها از URI
برای دریافت پارامتر از URI در روت به صورت {id}/ متغیری همنام با ورودی متد کنترلر تعریف می کنیم. به این نوع پارامتر Route param می گویند.
داخل فایل روت:
Route::get('/posts/{id}', [MainController::class, 'show']);
داخل کنترلر:
public function show($id)
{
//...
}
به عنوان مثال اگر بخواهیم از URI یک id دریافت کنیم تا پست مرتبط با آنرا نمایش دهیم به صورت زیر عمل می کنیم:
use App\Models\Post;
public function show($id)
{
$post = Post::find($id);
return view('post', [
'post' => $post
]);
}
در کد بالا id را دریافت کردیم و با استفاده از query builder پست مورد نظر را پیدا کردیم. (Post نام مدلی است که با آن کار داریم.) و محتویات پست را داخل یک آرایه با کلید post به داخل view فرستادیم و در آنجا با متغیری هم نام با کلید تعریف شده قابل دسترسی خواهد بود.
برای خلاصه کردن کار این قسمت لاراول قابلیتی به نام Route model binding دارد.
Route model binding
در Route model binding به جای پاس دادن متغیر id و بعدا پیدا کردن مدل مربوط به آن می توانیم کلاس مدل را پاس دهیم و لاراول خود به خود مدل مربوط به آن id را برای ما پیدا می کند و برمی گرداند. درواقع با این کار مدل را مستقیما به روت bind می کنیم.
برای انجام این کار باید داخل ورودی متد کنترلر، متغیری همنام با Route param وارد کنیم و نام مدل را برای آن type-hint کنیم.
داخل فایل روت:
Route::get('/posts/{post}', [MainController::class, 'show']);
داخل کنترلر:
use App\Models\Post;
public function show(Post $post)
{
return view('post', [
'post' => $post,
]);
}
با این روش لاراول خودش تمام کارهای مورد نیاز را انجام می دهد. دقت کنید علیرغم تغییر نام متغیر، درون URI فقط می توانیم id پست را وارد کنیم. Laravel هم از این طریق پست مورد نظر ما را پیدا می کند.
حال اگر بخواهیم با یک مشخصه دیگر بجز id به پست دسترسی پیدا کنیم باید آدرس داخل روت را به صورت زیر تغییر دهیم:
Route::get('/posts/{post:author}', [MainController::class, 'show']);
اگر هم بخواهیم حالت پیشفرض کلید را از id به چیز دیگری تغییر دهیم باید داخل فایل مدل متد زیر را بسازیم:
public function getRouteKeyName(): string
{
return 'author';
}
با این کار دیگر نیازی به تغییر دادن آدرس داخل روت نیست.
درصورتی که مدلی که به اصطلاح bind شده پیدا نشود پاسخ 404not found" http" برمی گردد برای جایگزین کردن این پاسخ متد missing را به انتهای خط زنجیر می کنیم. این متد یک closure دریافت میکند که در شرایطی که گفته شده اجرا می شود.
use App\Http\Controllers\LocationsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::get('/posts/{post:author}', [MainController::class, 'show'])
->missing(function (Request $request) {
return Redirect::route('posts.show');
});
اگر این مطلب را تا اینجا خوانده اید، باید دانش خلاصه ای درباره ی روتینگ در لاراول به دست آورده باشید.
برای دانستن بیشتر توصیه می کنم به documentation لاراول مراجعه کنید.