تغییر مهم مسیریابی (routing) در لاراول 8

تیلور اتول بالاخره در لاراول هشت از مواضع قبلی اش کوتاه اومد و پوشه ی «Models» رو به پوشه‌ی «app» اضافه کرد. چیزی که خیلیا پیگیرش بودند (و واقعیتش از نظر من، به عنوان یه تازه کار، زیاد مهم نبوده و نیست!).

به هر حال با این تغییر، یه کم ساختارهای قبلی به هم می خوره. اول خیلی سریع ببینیم قضیه چیه.

قضیه چیه؟

توی لاراول 7 و قبلترش، modelها توی پوشه ی app قرار داشتند اما نه در پوشه ای جداگونه. علتش هم تفسیرهای مختلفی بود که از model می شد و لاراول نمی خواست برنامه نویس ها به اشتباه بیفتند (طاها بیشتر توضیح داده). اما توی لاراول 8 بالاخره در مقابل «اصرارهای فراوان» برنامه نویسان کوتاه اومدند و یه پوشه ی Models به app اضافه کردند:

ساختار قبلی app در لاراول 7 و مدل پیش فرض User
ساختار قبلی app در لاراول 7 و مدل پیش فرض User


ساختار جدید app در لاراول 8، پوشه‌ی Models و مدل پیش فرض User
ساختار جدید app در لاراول 8، پوشه‌ی Models و مدل پیش فرض User

اما این تغییر به ظاهر کوچیک، یه سری مشکلات ایجاد کرده. نمونه اش رو می شه توی pull requestهای کاربران لاراول برای تغییر Seeder و Policies بر مبنای تغییرات اخیر مدل ها دید.

حالا مشکل چیه؟

یه مشکل برای کسایی پیش میاد که می خوان لاراول قبلی خودشون رو آپدیت کنند یا این که به روش قبلی، routerها رو بنویسند. این مسئله رو کانال یوتیوبی Laravel Business متذکر شده.

من یه پروژه ی لاراول هشت ساختم و با دستور زیر اجراش کردم:

php artisan serve

حالا می تونم توی مرورگرم http://localhost:8000 رو بزنم و صفحه ی جدید لاراول رو ببینم:

ویو جدید لاراول برای welcome.blade.php
ویو جدید لاراول برای welcome.blade.php

حالا یه مدل جدید به اسم Book می سازم و برای این مدل Controller و Resource هم می سازم:

php artisan make:model Book -cr

الان توی app>Http>Controllers یه کنترلر جدید به نام BookController دارم که خودش متدهای مربوط به ریسورس رو هم ساخته. من متد ایندکس رو خیلی ساده به شکل زیر تغییر می دم:


public function index()
{
      return view('welcome');
}

حالا توی routes این خط رو به web.php اضافه می کنم:

Route::get('/books', 'BookController@index');

تا این جا همه چی مثل قبله. چون resource ساختم، الان باید با رفتن به نشانی localhost:8000/books به همون صفحه ی welcome هدایت بشم. اما به جاش این خطا رو می بینم:

پیدا نشدن کلاس کنترلر در صورت نوشتن روتر به سبک قدیم
پیدا نشدن کلاس کنترلر در صورت نوشتن روتر به سبک قدیم

مشکل کجاست؟ همون چیزی که توی بخش «به روز رسانی مستندات لاراول هشت» به عنوان یه گزینه ی اختیاری توضیح داده. مسیریابی کنترلرها دیگه به شکل قبل قابل انجام نیست. چون توی نسخه های قبلی، RouteServiuceProvider یه ویژگی به اسم $namespace داشت که برابر App\Http\Controllers قرار داده شده بود اما توی نسخه ی جدید این ویژگی حذف شده (اول حذف نکرده بودند و null گذاشته بودند اما الان حذف کردند).

/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';

نیم اسپیس در لاراول هفت و قبل (کد بالا)


دلیل این تغییر چی بوده؟ به خاطر این که کد routerها خواناتر بشه و از استانداردهای PHP تبعیت کنه و توی IDEهای مختلف بشه با کلیک روی اسم کنترلر به تعریفش دسترسی پیدا کرد.

حالا چه کار کنیم که درست بشه؟ باید روتر رو به یکی از دو حالت زیر تغییر داد. یا کامل بنویسیم از چه کلاسی می خوایم استفاده کنیم یا با یه آرایه به متدی که می خوایم دسترسی پیدا کنیم:

Route::get('/books', 'App\Http\Controllers\BookController@index');
use App\Http\Controllers\BookController;
Route::get('/books', [BookController::class , 'index']);

این ایده جدید نیست و دو سال پیش با این pull request امکان مسیریابی با استفاده از آرایه به لاراول اضافه شده بود (خوندن این پول ریکوئست هم جالبه. یه نمونه ی خوب از این که چه جوری در لاراول مشارکت کنیم. هر تغییری که قرار باشه توی لاراول داده بشه، اگه تغییر کوچیک در حد typo و اینا نباشه، باید یه تست هم کنارش باشه. کاری که توی این پول ریکوئست شده). توی این مطلب بیشتر درباره ی این نوع مسیریابی و مزیتش گفته شده.

اگه هم همچنان بخوایم مسیریابی لاراول مثل قبل کار کنه، باید توی RouteServiceProvider مقدار $namespace رو به قبل برگردونیم و توی boot همین فایل هم اضافه اش کنیم:

protected $namespace = 'App\Http\Controllers';
...
public function boot()
{
    $this->configureRateLimiting();
    $this->routes(function () {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));
        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));
        });
}

حالا می تونیم مثل قبل مسیریابی خودمون رو بنویسیم.