早いものでL10がリリースされてからもう1年が経ちました。個人的な感想ですがL10では正直なところそこまで大きな変更が無かった様に思います。しかし大抵その様な場合は水面下で激しい議論が酌み交わされており次バージョンにてドラスティックな変更がやってくるもの。さてさてL11はどうなるやら、一緒に見ていきましょう!

サポート

いつも通りサポート表の確認から。まず、注意すべき点としてL11ではPHP8.2以上が必須となっています。前回のL9からL10にアップグレードする際もPHP8.0から8.1へPHPのVer要件が引き上げられました。忙しない感じもしますが、今回もPHP8.2が未インストールの場合は予め環境にインストールしておきましょう。macOSの方はphpbrewを使うと複数バージョンをインストールして切替られるので便利ですよ。

新規プロジェクト作成

早速L11をインストールしてみましょう。

$ composer create-project laravel/laravel L11.x

インストール完了後にプロジェクトディレクトリに移動し、バージョンを確認してみます。

$ cd L11.x
$ php artisan --version
>> Laravel Framework 11.0.7

2024年3月18日現在、11.0.7がインストールされました。続いて、ビルドインサーバを起動してみます。

php artisan serve

お馴染みのメニューが表示されました。

L10と比べて凝ったデザインになりましたね。続いて従来のバージョンからの変更点についてチェックしていきます。

ファイル構成の変更

Streamlined Application Structureというのが今回のアップデートの目玉のようです。要はファイル構成の変更ですが、方向性としてはより簡素で合理的な構造を目指す意図があるようです。その為、app配下の更新頻度が低いと判断されたファイル群が省かれ、各ファイルで行っていた設定はbootstrap/app.phpに集約されメソッドベースで設定する事となりました。以下がインストール直後のbootstrap/app.phpです。

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

withMiddleware()

ミドルウェアの設定に関するファイル群、app/Http/Middleware(ディレクトリごと)とapp/Http/Kernel.phpが削除され、それらで行っていた設定はwithMiddleware()で行います。

withExceptions()

以前はapp/Exceptions/Handler.phpにて、アプリケーション上で発生するあらゆる例外を検知しレポート(ログや通知など)やレンダリング(エラー画面の表示)の制御を行っていましたが、こちらも削除されwithExceptions()にまとめられました。

withCommands()

上に表示したbootstrap/app.phpにはありませんがwithCommands()というメソッドもあります。こちらのメソッドでは元々app/Console/Kernel.phpにて行っていたCommandクラスのディレクトリを指定できます。従来通り、app/Console/Commands配下のコマンドクラスは何も設定せずとも自動でロードされますが、もし他のディレクトリに分けて運用している場合はこのメソッドを使って追加登録する必要があります。

その他、bootstrap/app.php にて行える設定についてはvendor/laravel/framework/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php をご確認下さい。

ServiceProviderの統合

app/Providers配下もとてもスッキリしたものになりました。AppServiceProviderクラスを除き以下の4つのServiceProviderクラスが削除されました。

  • AuthServiceProvider
  • BroadcastServiceProvider
  • EventServiceProvider
  • RouteServiceProvider

従来はAuthServiceProviderではModelとPolicyの紐付けを、EventServiceProviderではEventとListenerの紐付けを行なっていましたが、L11.xではそれらはAppServiceProviderのboot()にてGate::policy()Event::listen()を用いて行う様です。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // ModelクラスとPolicyクラスの紐付け
        Gate::policy(
            YourModel::class,
            YourPolicy::class,
        );

        // EventクラスとListenerクラスの紐付け
        Event::listen(
            YourEvent::class,
            YourListener::class,
        );
    }
}

集約されてしまうことでAppServiceProviderが肥大化してしまうのでは?と心配になりますが、Laravel標準のディレクトリ構造、且つ、標準の命名規則に従って開発していれば大抵はLaravelが自動でクラス同士の紐付けを行なってくれるので手動で設定が必要なケースはそう多くなさそうです。(ModelクラスとPolicyクラスの自動紐付けについてはPolicyで認可チェックにて解説しているので是非そちらもチェックしてみてください。)

シンプルになったControllerの基底クラス

Controllerクラスの基底クラスであるApp\Http\Controllers\Controller.phpもシンプルになりました。

<?php

namespace App\Http\Controllers;

abstract class Controller
{
    //
}

只の真っさらなabstractクラスですね。以下のL10.xのと見比べてみてください。以前はIlluminate\Routing\Controllerを継承し、2つのトレイトAuthorizesRequestsとValidatesRequestsをuseしていました。

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController
{
    use AuthorizesRequests, ValidatesRequests;
}

これらのトレイトは削除された訳では無く「必要あれば適宜使ってね」という事の様です。例えば以前にPolicyで認可チェックにて紹介したコントローラヘルパのauthorize()を使用して認可チェックを行う場合はAuthorizesRequestsが必要となります。

once()

最後にリリースノートを読んでいてこれは便利そうだ、と思った新しいヘルパ関数once()を紹介します。この関数は引数に指定したコールバックの結果をそのプロセスの間中キャッシュしてくれるというものです。これは例えばDBに保存しているマスタデータを取得する際などに便利そうです。例として以下にDBに保存された都道府県名を取得するコードを書いてみました。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Prefecture extends Model
{
    use HasFactory;

    protected $table = 'prefecture';

    public static function list(): array
    {
        return once(fn () => self::pluck('name')->all());
    }
}

once()を使って一度データを取得すれば2回目以降の呼び出しではキャッシュから参照されるのでクエリが発行されず効率的です。同一プロセス上で何度も参照が必要な場合に有効ですね。因みに以前は以下の様にstaticプロパティに退避するなどして対応していました。once()なら1行で書ける処理ですが、以前は5行も必要でした。

...
static $list;

public static function list(): array
{
    if (! isset(self::$list)) {
        self::$list = self::pluck('name')->all();
    }

    return self::$list;
}

By hikaru