September 14, 2020

Laravel8 新しいルーティングの書き方

はじめに

Laravel8ではルーティングの書き方が若干変更されている。
慣れてる人や旧バージョンからアップグレードした環境では影響は少ないけど
Laravel8リリース後に使い始めた初心者は大混乱している。

旧バージョン時の情報は使えなくなってるので新しい書き方を覚える必要がある。

読むところ

Laravel7までは

こう書けた。

Route::get('/user', 'UserController@index');

これで良かった理由はapp/Providers/RouteServiceProvider.phpで$namespaceが定義されていたから。

    /**
     * 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';

UserControllerだけ書いてもApp\Http\Controllersが追加されてApp\Http\Controllers\UserControllerとなる。

(そもそも名前空間を省略できるようにしてるのは名前空間使ってなかったLaravel4頃の名残り?)

例外はarrayでの指定時。callable syntax。

use App\Http\Controllers\UserController;
Route::get('/user', [UserController::class, 'index']);

Laravel8では

例外だった書き方が標準。

use App\Http\Controllers\UserController;

Route::get('/user', [UserController::class, 'index']);

RouteServiceProviderの$namespaceがコメントアウトされたので常に名前空間まで含むクラス名で指定。
「コントローラーをuseして::classで指定」と覚えておけばいいのでは。

この変更によりシングルアクションコントローラーはこう書けるようになった。

Route::get('/user', UserController::class);

Laravel7までは↑はできそうでできない。以下のどちらか。

Route::get('/user', [UserController::class, '__invoke']);
Route::get('/user', 'UserController');

Laravel8でUserControllerだけ書いてもApp\Http\Controllersが追加されないのでクラスが存在しないエラーとなる。

Laravel8でどうしても旧バージョンの書き方したい場合

アップグレードガイドに書かれてるようにRouteServiceProviderで$namespace使うように変更すればいい。

class RouteServiceProvider extends ServiceProvider
{
    /**
     * 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';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));

            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));
    });
}

旧バージョンからのアップグレード時はRouteServiceProviderを変更しなければそのまま使える。
新しい書き方に変えるかどうかは自由。

逆に、Laravel7以前でもRouteServiceProviderからnamespaceを削除すればLaravel8の書き方ができる。Laravel6(LTS)使ってるけど今後のためにLaravel8に合わせるのはあり。

変更した理由?

不明なもののJetstreamで使うLivewireのため?
https://laravel-livewire.com/docs/2.x/upgrading

シングルアクションコントローラーが便利になったのは偶然な気がする。
毎度同じだけど個人的には「リソースコントローラー以外は全部シングルアクションコントローラーで作る」が推奨。
これならarrayもなく全部::classの指定のみになる。

Route::get('user/{id}', ShowProfile::class);
Route::resource('photos', PhotoController::class);

'UserController'だとうっかりtypoすることもあるだろうしUserController::classのほうがミスは少ない。
IDEフレンドリー。

ついで

Laravel8からクロージャを使ったルーティングもphp artisan route:cacheできるようになっている。
デフォルトのこれのままでもキャッシュできる。

Route::get('/', function () {
    return view('welcome');
});

routes/web.phpは修正してもapi.phpを忘れてて最初のデプロイ後に修正、が定番だったけどそれもなくなる。

© kawax