📚

Laravel 9 Upgrade Memo

2022/02/09に公開
1

Laravel9が2022/02/08リリースされたので、新機能およびアップグレードメモ。Deepl だったり意訳だったりするので、本家本元を読んでくださいな。

~~ 前回の LTS が Laravel 6(2019/09/03 Release)なので、それから久しぶりの LTSです。~~

サポートしているPHPが 8.0〜8.1(現在の所。おそらく8系はサポート?)となっており、7.x 系は動きません。これにともない named arguments -- www.php.net がサポートされます。

バグフィックスは 2024/02/08まで、セキュリティフィックスは 2025/02/08 までです。

わりとクソデカ変更ですので、アップグレードは注意です。 PHP7→PHP8 で返り値が追加とか、SymfonyMail へ変更とか、 FlySystem3.x へ対応とか。


  • Update : 2024-02-19 : TrustedProxy の項目を更新

変更点

多いので、右側の一覧を見てください。

変更量が特に大きいもの

  1. PHP8.0 以上が必須、PHP8.1以上が推奨、になりました。PHP8.1以上が推奨なのは Enum を使えたほうが良いからです。
  2. SwiftMailer の使用が終わり、SymfonyMail へ変更されました。それの対応する変更が大幅にあります。
  3. AWS S3 等にファイルを置く FlySystem の利用バージョンが 3.0 系に更新されました。これに対応する変更があります。

アップグレード方法

composer.json を変更してください。

  • 変更: laravel/framework:^9.0
  • 変更: nunomaduro/collision:^6.0
  • 追加: facade/ignition
  • 追加: spatie/laravel-ignition": "^1.0"

さらに Notification のアップグレードもありますので、各自頑張ってください。

[NEW][インパクト高] PHP8.0

PHP 8.0.2 以上が推奨です。ただ、Enum使うために PHP8.1以上が良いかも。

see : https://laravel.com/docs/9.x/upgrade#updating-dependencies

Return Type

PHP Methods の offsetGet,offsetSet の返り値が付きました。ゴリゴリ書いて行きましょう。

Application

[変更][インパクト低] The Application Contract

Illuminate\Contracts\Foundation\Application クラスの storagePath$path 引数が付きました。継承して使っている時は public function storagePath($path = ''); 的にしましょう。

[変更][インパクト低] Exception Handler ignore Method

同じクラスの ignore メソッドが public になりましたので、追従変更をしてください。

Blade

[追加] Rendering Inline Blade Templates

see : https://laravel.com/docs/9.x/releases#rendering-inline-blade-templates

テンプレートファイルがなくても Blade のテンプレートを指定して実行することが出来ます。

use Illuminate\Support\Facades\Blade;
 
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);

追記:: 2022/03/29 :: 調べたら v8.80 から使える機能みたいです。なんちゅうバージョン番号だ… see: https://github.com/laravel/framework/commit/5e104a0244f82d2f7c562044fc8208ace49cb633

[追加] Slot Name Shortcut

x-slot タグに <x-slot name="title"> としてたところを <x-slot:title> という感じでショートカットができるようになりました。

[追加] Checked / Selected Blade Directives

https://laravel.com/docs/9.x/releases#checked-selected-blade-directives

@checked および @selected タグが追加されました。(三項演算子でええやんけ)

<input type="checkbox"
        name="active"
        value="active"
        @checked(old('active', $user->active)) />

<select name="version">
    @foreach ($product->versions as $version)
        <option value="{{ $version }}" @selected(old('version') == $version)>
            {{ $version }}
        </option>
    @endforeach
</select>

[追加] Bootstrap 5 Pagination Views

BootStrap5 を使うようになりましたので、使う時は下記のような感じで App\Providers\AppServiceProvider に書き込んでください。

use Illuminate\Pagination\Paginator;
 
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Paginator::useBootstrapFive();
}

[変更][インパクト低] Lazy Collections & The $loop Variable

ループをしているとき、$loop を使っている場合は Lazy Collections 全体がメモリーにロードされるので、Lazy Collectionsの使用が無意味になります。

Collections

[変更][インパクト低] The Enumerable Contract

  1. Illuminate\Support\Enumerable クラスに solo メソッドが追加されました。使用している時は、このメソッドを実装してください。
  2. reduceWithKeys メソッドは reduce メソッドと同意なので、廃止されました。
  3. reduceMany メソッドは reduceSpread に名称が変わりました。

Container

[変更][影響ほぼない] The Container Contract

Illuminate\Contracts\Container\Container にある scopedscopedIf が新しく実装されました。継承して実装をしている場合は実装をしてください。

[変更][影響ほぼない]The ContextualBindingBuilder Contract

Illuminate\Contracts\Container\ContextualBindingBuildergiveConfig が新しく実装されました。使用している時は新しく実装してください。

Database

[追加] Full Text

see : https://laravel.com/docs/9.x/releases#full-text

MySQL/PostgreSQL でフルテキスト検索のインデックスを作成するオプションが付きました。

$table->text('bio')->fullText();

使う時はこんな感じ。

$users = DB::table('users')
           ->whereFullText('bio', 'web developer')
           ->get();

[追加] Laravel Scout Database Engine

よくわかんないけど、 Scout エンジンを Eloquent から使えるようになるっぽい。

see : https://laravel.com/docs/9.x/scout

[変更][インパクト並] Postgres "Schema" Configuration

PostgreSQLを使う場合の config/database.php にある、接続パスを設定するために使用するスキーマ設定オプションは search_path に名前を変更する必要がある。

[変更][インパクト低] Schema Builder registerCustomDoctrineType Method

Illuminate\Database\Schema\BuilderregisterCustomDoctrineType メソッドが廃止されました。代わりに registerDoctrineType メソッドを使うか、 config/database.php にカスタムの Doctrine タイプを書いてください。

Eloquent

[追加] Improved Eloquent Accessors / Mutators

今まで Mutators として getHogeAttribute/setHogeAttribute となっていましたが、もっと楽になります。

use Illuminate\Database\Eloquent\Casts\Attribute;
 
public function name(): Attribute
{
    return new Attribute(
        get: fn ($value) => strtoupper($value),
        set: fn ($value) => $value,
    );
}

さらに Illuminate\Database\Eloquent\Casts\Attribute を継承してカスタマイズをすることが出来ます。

use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
 
public function address(): Attribute
{
    return new Attribute(
        get: fn ($value, $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
        set: fn (Address $value) => [
            'address_line_one' => $value->lineOne,
            'address_line_two' => $value->lineTwo,
        ],
    );
}

[追加] Enum Eloquent Attribute Casting

Enumを使って cast をすることができます。PHP8.1+のみ。

use App\Enums\ServerStatus;
 
/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'status' => ServerStatus::class,
];

enum Cast を使えば下記のように書くことが出来ます。

if ($server->status == ServerStatus::provisioned) {
    $server->status = ServerStatus::ready;
 
    $server->save();
}

[変更][インパクト並] Custom Casts & null

DEEPL: Laravelの以前のリリースでは、カスタムキャストクラスのsetメソッドは、キャスト属性がNULLに設定されている場合は呼び出されませんでした。しかし、この動作はLaravelのドキュメントと矛盾していました。Laravel 9.xでは、キャストクラスのsetメソッドは、提供された$value引数としてnullで呼び出されます。したがって、カスタムキャストがこのシナリオを十分に処理できるようにする必要があります。

/**
 * Prepare the given value for storage.
 *
 * @param  \Illuminate\Database\Eloquent\Model  $model
 * @param  string  $key
 * @param  AddressModel  $value
 * @param  array  $attributes
 * @return array
 */
public function set($model, $key, $value, $attributes)
{
    if (! $value instanceof AddressModel) {
        throw new InvalidArgumentException('The given value is not an Address instance.');
    }
 
    return [
        'address_line_one' => $value->lineOne,
        'address_line_two' => $value->lineTwo,
    ];
}

[変更][インパクト並] Belongs To Many firstOrNew, firstOrCreate, and updateOrCreate Methods

よくわかんない。多対多関連でつかう中間テーブルの扱いに対して変更があるみたい?

[変更][インパクト低] The touch Method

touch メソッドに引数 $attribute が付きましたので、追従変更をしてください。

Encryption

[変更][インパクト低] The Encrypter Contract

Illuminate\Contracts\Encryption\Encrypter クラスに getKey メソッドが実装されましたので、実装をしている場合は追従変更をしてください。

Facades

[変更][インパクト低] The getFacadeAccessor Method

getFacadeAccessor メソッドの返り値がは常にコンテナーの Binding Key を返すようにしてください。以前のリリースまではオブジェクトを返すようにしていましたが、近いうちに廃止されますので、クラスを返すようにしてください。

/**
 * Get the registered name of the component.
 *
 * @return string
 */
protected static function getFacadeAccessor()
{
    return Example::class;
}

FlySystem

FlySystem を 3.x 系にアップグレードしました。

see : https://laravel.com/docs/9.x/upgrade#flysystem-3

[Upgrade][インパクト高] ドライバーアップデート

S3 などのパッケージをアップデートしてください。

> composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^3.0"

[変更][インパクト高] put, write, writeStream の挙動変更

put, write, writeStream のメソッドの挙動が変わります。これからは既存ファイルに対して上書きを実行します。ファイルの存在確認は書き出す前に実効をしてください。

[変更][インパクト高] Reading Missing Files

存在しないファイルにアクセスした時に null を返していましたが Illuminate\Contracts\Filesystem\FileNotFoundException の例外を投げるようになりました。

[変更][インパクト高] Deleting Missing Files

存在しないファイルを削除した時は true を返すようになります。

[変更][インパクト高] Cached Adapters

FlySystem から cached adapters のサポートがなくなりました。それにともない Laravel もサポートしなくなります。

[変更][インパクト高] Custom Filesystems

カスタマイズしている時に変更が必要みたい。

// Laravel ~8
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
 
Storage::extend('dropbox', function ($app, $config) {
    $client = new DropboxClient(
        $config['authorization_token']
    );
 
    return new Filesystem(new DropboxAdapter($client));
});
// Laravel 9~
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
 
Storage::extend('dropbox', function ($app, $config) {
    $adapter = new DropboxAdapter(new DropboxClient(
        $config['authorization_token']
    ););
 
    return new FilesystemAdapter(
        new Filesystem($adapter, $config),
        $adapter,
        $config
    );
});

[変更][インパクト低] The FILESYSTEM_DRIVER Environment Variable

.envにある変数 FILESYSTEM_DRIVERFILESYSTEM_DISK に変更されました。

もし変更したい時は変更をしてください。変更をしなくても現状は問題ないはず。

[変更][インパクト低] The "Cloud" Disk

2020/11 のリリースより cloud Disk Configuration は削除されました。

HTTP Client

[変更][インパクト中] Default Timeout

Laravel の HTTP_Client にある default timeout が 30 秒になりました。変更をする場合は下記のようにすればいいと思います。

$response = Http::timeout(120)->get(...);

[変更][インパクト低] HTTP Fake & Middleware

Http::fake() としても GuzzleMiddleware は実行されませんでしたが、これからは動くようになります。

[変更][インパクト低] HTTP Fake & Dependency Injection

力尽きた

Mailer

大幅にテコ入れです。

[NEW][インパクト高] Symfony Mailer

SwiftMailerが使われていましたが、2021/12からメンテナンスがされていないので、SymfonyMailerに乗り換えました。

これにともない、たくさんの変更点があります。(白目)

see : Upgrade : Symfony Mailer -- laravel.com

ドライバーとしての前提条件

mailgun や postmark のドライバーをそれぞれインストールする必要があります。

> composer require symfony/mailgun-mailer
> composer require symfony/sendgrid

send/html/text/plain の返り値の変更

今まで送信数を返していたのが Illuminate\Mail\SentMessageインスタンスに変更されました。このオブジェクトは Symfony\Component\Mailer\SentMessage の継承クラスで getSymfonySentMessageで動的に作成されたメッセージのアクセスができます。

Rename Swift to Symfony

Laravel 8 の頃と 9 になってからでは、メソッド名が変更されています。

// Laravel 8.x...
$this->withSwiftMessage(function ($message) {
    $message->getHeaders()->addTextHeader(
        'Custom-Header', 'Header Value'
    );
});
 
// Laravel 9.x...
use Symfony\Component\Mime\Email;
 
$this->withSymfonyMessage(function (Email $message) {
    $message->getHeaders()->addTextHeader(
        'Custom-Header', 'Header Value'
    );
});

Email クラスのメソッドは Symfony Mailer -- symfony.com で確認できます。

Proxied Illuminate\Mail\Message methods

Illuminate\Mail\Message は存在しないメソッドにアクセスした場合は Swift_Message のメソッドを呼び出していました。したがって、対応するメソッドを Symfony\Component\Mime\Email に変更する必要あります。

// Laravel 8.x...
$message
    ->setFrom('taylor@laravel.com')
    ->setTo('example@example.org')
    ->setSubject('Order Shipped')
    ->setBody('<h1>HTML</h1>', 'text/html')
    ->addPart('Plain Text', 'text/plain');
 
// Laravel 9.x...
$message
    ->from('taylor@laravel.com')
    ->to('example@example.org')
    ->subject('Order Shipped')
    ->html('<h1>HTML</h1>')
    ->text('Plain Text');

Generated Messages IDs

よくわからん

Forced Reconnections

よくわからん。

メール送信時の接続に失敗した時に再接続をするが、それも失敗した時は例外を投げて終わる感じ?

SMTP Stream Options

SMTP の stream オプションがサポートされなくなりました。代わりに直接に定義をすることになります。これらのメールの設定は Symfony の Transport Setupを参考にしてください。

'smtp' => [
    // Laravel 8.x...
    'stream' => [
        'ssl' => [
            'verify_peer' => false,
        ],
    ],
 
    // Laravel 9.x...
    'verify_peer' => false,
],

SMTP auth_mode

mail 設定の auth_mode はサポートされなくなりました。

Failed Recipients

メールの送信後に失敗した受信者リストを取得することができなくなりました(え、できたの?)

代わりに送信に失敗すると Symfony\Component\Mailer\Exception\TransportExceptionInterface の例外を放つようになりました。メールアドレスの検証は送信前にチェックするようにしましょう。

Packages

https://laravel.com/docs/9.x/upgrade#the-lang-directory

[変更][インパクト中] The lang Directory

resources/lang ディレクトリパスが app()->langPath() でアクセスできるようになります。

queue

[変更][インパクト中] The opis/closure Library

https://laravel.com/docs/9.x/upgrade#the-opis-closure-library

opis/closure を使わず laravel/serializable-closure に変更されます。これにともない Illuminate\Queue\SerializableClosureFactoryIlluminate\Queue\SerializableClosure は削除されました。

もし使用し続ける場合は https://github.com/laravel/serializable-closure を個別でインポートしてください。

[変更][インパクト中] The Failed Job Provider flush Method

flush メソッドの実装は Illuminate\Queue\Failed\FailedJobProviderInterface にありますが、$hour 引数が追加されました。それは、どれだけ古い失敗したJobかを図る(単位は時間)のに使うみたいです?

Session

[変更][インパクト低] The getSession Method

力尽きた。

Routing

Routing に色々と機能が増えました。

[NEW] Implicit Route Bindings With Enums

see : https://laravel.com/docs/9.x/releases#implicit-route-bindings-with-enums

PHP8.1+ から Enum が実装されました。それに合わせて、Laravel9 も Routing に Enum を使えるようになりました。

enum Category: string
{
    case Fruits = 'fruits';
    case People = 'people';
}

Route::get('/categories/{category}', function (Category $category) {
    return $category->value;
});

[NEW] Forced Scoping Of Route Bindings

see : https://laravel.com/docs/9.x/releases#forced-scoping-of-route-bindings

よくわかんない

[NEW] Controller Route Groups

Controller の指定を controller メソッドで使用できるようになりました。

Route::controller(\App\Http\Controllers\OrderController::class)->group(function () {
    Route::get('/orders/{id}', 'show');
    Route::post('/orders', 'store');
});

[NEW] Improved route:list CLI Output

https://laravel.com/docs/9.x/releases#improved-route-list

route:list の出力が人道的になりました。

Exception Page

https://laravel.com/docs/9.x/releases#exception-page

何か開発中に出る例外エラーの画面がリッチになったっぽい。

Test

[NEW] Test Coverage Using Artisan test Command

https://laravel.com/docs/9.x/releases#test-coverage-support-on-artisan-test-Command

Coverage の出力がわかりやすくなりました。そして --min をつけると、Coverage の敷居として設定して、Coverage の結果が min で指定した値より低い時はエラーを起こすようになります。

[変更][インパクト中] The assertDeleted Method

全ての assertDeletedassertModelMissing が呼ばれます。

[変更][インパクト低] Trusted Proxies

追記 必要なコマンドが抜けていたので修正。

app/Http/Middleware/TrustProxies.php がある時、このファイルを編集します。

  1. そのファイルの use Fideloper\Proxy\TrustProxies as Middlewareuse Illuminate\Http\Middleware\TrustProxies as Middleware に変更する

  2. そのファイルにある TrustProxies クラスのクラス変数 $headers を下記に編集します。

    protected $headers =
        Request::HEADER_X_FORWARDED_FOR |
        Request::HEADER_X_FORWARDED_HOST |
        Request::HEADER_X_FORWARDED_PORT |
        Request::HEADER_X_FORWARDED_PROTO |
        Request::HEADER_X_FORWARDED_AWS_ELB;
    
  3. composer remove fideloper/proxy を実行して削除します。

see: https://github.com/fideloper/TrustedProxy

Validator

[追加] Improved Validation Of Nested Array Data

https://laravel.com/docs/9.x/releases#improved-validation-of-nested-array-data

配列で来たデータに対して、個々の要素チェックができるようになります…?

use App\Rules\HasPermission;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
 
$validator = Validator::make($request->all(), [
    'companies.*.id' => Rule::forEach(function ($value, $attribute) {
        return [
            Rule::exists(Company::class, 'id'),
            new HasPermission('manage-company', $value),
        ];
    }),
]);

[変更][インパクト中] Form Request validated Method

validated メソッドの引数が変更されたみたいです。

[変更][インパクト中] The password Rule

現在登録しているパスワードと認証をするのに current_password メソッドに変更されました。

[変更][インパクト中] Unvalidated Array Keys

https://laravel.com/docs/9.x/upgrade#unvalidated-array-keys

よくわかんない

IDE Support

[NEW] Improved Collections IDE Support

https://laravel.com/docs/9.x/releases#improved-collections-ide-support

IDE-Helper とどう違うのかわからないけど、公式としてIDE/PHPStanをサポートします。

Helper

https://laravel.com/docs/9.x/releases#new-helpers

str

str の返り値が Illuminate\Support\Stringable を返すようになりました。

to_route

to_route でいろいろ……?

[変更][インパクト中] The when / unless Methods

https://laravel.com/docs/9.x/upgrade#when-and-unless-methods

when/unless メソッドに変更があったみたい。使ったことがないからわからない…

Discussion

tobigumotobigumo

langディレクトリのとこは、resources以下からデフォルトでプロジェクトルートに移動したよ、ってことみたいですね。
resources以下のままでもちゃんと認識してくれるけど、パッケージ作者でlangファイルを扱う場合はパスを表すのにapp()->langPath()使ってね、って感じかと