Routing در لاراول

Routing در لاراول

در لاراول برای هر  URI یک  روت (Route یا مسیر) تعریف می­ کنیم. این  روت را با متدهای استاتیک کلس  Route تعریف می کنیم. وقتی یک درخواست از طریق URI  فرستاده میشود، روت درخواست را می گیرد و  یک function برای پاسخ به آن فراخوانی می کند. این function می­ تواند یک  closure یا متدی از یک کنترلر باشد. البته بیشتر مواقع برای هندل کردن درخواست ها از یک کنترلر استفاده می­ کنیم. هر  URI ای که برای آن  Route ای تعریف نشده باشد به صفحه  “404 not found” هدایت می شود. متد های استاتیک  Route  بر اساس فعل(verb)های  http نام گذاری شده اند و انتخاب متد مناسب  به نوع درخواست http و کاری که میخواهیم انجام بدهیم بستگی دارد. 

در لاراول  routeها در فولدر  routes در root پروژه ما قرار دارند. 

routes-folder

همان­طور که مشاهده می­ کنید درون این پوشه چهار فایل وجود دارد و از بین این­ها برای تعریف کردن  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 لاراول مراجعه کنید. 

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس