弊社で毎月開催し、PHPエンジニアの間で好評いただいているPHP TechCafe。
2023年2月のイベントでは「Laravel10の新機能」について語り合いました。
弊社のメンバーが事前にまとめてきたLaravel10の新機能の情報にしたがって、他の参加者に意見を頂いて語り合いながら学びました。
今回はその内容についてレポートします。
- 特集:Laravel10の新機能
- Laravelのリリースサイクルについて
- PHP8.0系の非対応
- Laravel Pennant
- Native type declarations in Laravel 10 skeleton
- Invokable Validation rules are the default
- Processes 関連機能
- テストのプロファイル オプション
- Pest Scaffolding
- パスワード生成ヘルパー Str::password()
- 設定ファイルパスのカスタマイズ
- doctrine/dbal is not needed anymore to modify columns in migrations
- Laravel 10 requires at least Composer 2.2
- 非推奨となる変更点
- 参考資料
- まとめ
特集:Laravel10の新機能
PHP TechCafeでは過去に何度かLaravelを取り上げています。
下記が過去のLaravel回のまとめになります。資料には過去のバージョンのものも記載していますので、参考にしていただければと思います。
- PHPerによるPHPerのための「Laravel8を中心に語り合う」TechCafe
- PHPerのための「Laravel/PHP8/Dockerで開発環境作りを語り合う」TechCafe
- PHPerのための「2020年のPHP/Laravel振り返り+2021年」を語るTechCafe
- PHPerのための「Laravel 入門を語り合う」PHP TechCafe
- PHPerのための「2021年のPHP/Laravel振り返り+2022年」を語るTechCafe
- PHPerのための「Laravel 9 について語る」PHPTechCafe
Laravelのリリースサイクルについて
- LaravelはLaravel8以降1年に1回のメジャーバージョンアップになっています。
PHP8.0系の非対応
- Laravel10はPHP8.1以上が必要です。
PHP8.0系は2023年の11月末くらいでセキュリティサポートが切れるので妥当かなという意見がありました。
Laravel Pennant
下記のような特徴を持つLaravel Pennantというパッケージが追加されました。
- 新しいファーストパーティパッケージ
- フィーチャーフラグを追加できる
- Composer 経由でインストールが可能
フィーチャーフラグとは??
フィーチャーフラグとはコードを書き換えることなく、システムの振る舞いを変更できるようにする開発手法です。 フラグによって機能のON/OFFが可能となります。 新しい機能を段階的にロールアウトしたり、 A/B テストを行ったりできます。
「最近、結構(フィーチャーフラグの話を)聞くようになった」
「PDCAサイクルを早める手法として紹介されたり、流行っている印象です。」
などの意見が上がりました。
導入手順
下記の手順でComposer で簡単で導入することができます。
$ composer require laravel/pennant $ php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" $ php artisan migrate
機能利用のON/OFFの定義
下記が機能利用のON/OFFの定義方法になります。
サービスプロバイダで定義する方法
<?php namespace App\Providers; use App\Models\User; use Illuminate\Support\Lottery; use Illuminate\Support\ServiceProvider; use Laravel\Pennant\Feature; // ★ class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. */ public function boot(): void { // 新しいAPIについて Feature::define('new-api', fn (User $user) => match (true) { // 内部メンバーは利用可能 $user->isInternalTeamMember() => true, // トラフィックの多いユーザは利用不可 $user->isHighTrafficCustomer() => false, // それ以外の場合、1/100の確率で利用可能 default => Lottery::odds(1 / 100), }); } }
クラスでの定義
クラスでの定義には下記2つの特徴があります。
- 機能フラグをクラスで定義することも可能
- artisanコマンドから雛形を作成できる
$ php artisan pennant:feature NewApi
<?php namespace App\Features; use Illuminate\Support\Lottery; class NewApi { /** * Resolve the feature's initial value. */ public function resolve(User $user): mixed { // 新しいAPIについて return match (true) { // 内部メンバーは利用可能 $user->isInternalTeamMember() => true, // トラフィックの多いユーザは利用不可 $user->isHighTrafficCustomer() => false, // それ以外の場合、1/100の確率で利用可能 default => Lottery::odds(1 / 100), }; } }
機能フラグの利用方法
Feature::active('KEY名')
で ON/OFF を取得できます。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Pennant\Feature; class PodcastController { /** * Display a listing of the resource. */ public function index(Request $request): Response { return Feature::active('new-api') // 定義した条件通りにtrue/falseが返される ? $this->resolveNewApiResponse($request) : $this->resolveLegacyApiResponse($request); } // ... }
クラスに定義している場合
フィーチャーフラグを管理しているクラス名を指定する必要があります。
<?php namespace App\Http\Controllers; use App\Features\NewApi; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Pennant\Feature; class PodcastController { /** * Display a listing of the resource. */ public function index(Request $request): Response { return Feature::active(NewApi::class) // ★ ? $this->resolveNewApiResponse($request) : $this->resolveLegacyApiResponse($request); } // ... }
Blade内での利用方法
@feature
ディレクティブを使ってBladeで使用することも可能です。
@feature('new-api') <!-- 'site-redesign' is active --> @else <!-- 'site-redesign' is inactive --> @endfeature
下記のような話が上がり、今回のバージョンアップの注目の機能として紹介されました。
- フィーチャーフラグは実はよく使われる機能!?
- フィーチャーフラグ、フィーチャートグルという言い方だとあまり使ったことないという感じます
- よくある課金ユーザだけ機能をONにするような機能で使えると思います
- フィーチャーフラグを定義しておけば可読性が上がるのではないかと考えられます
- オレオレ実装した時にハマりそうな罠を一通り回避してくれる
- フィーチャートグルは名前で損していると感じます
- 仕様を見ると下記のようなことが可能です
- フィーチャートグルの管理下の機能を一括で有効にしたりできます
- 処理の中で、あるユーザに対して「ルートAを通ったら有効、ルートBを通ったら無効」になる場合にキャッシュを取って整合性を取ってくれます
- オレオレ実装でハマりそうなこと
- 同じ条件のはずのif文同士なのに条件が微妙に違うといったことがあります。
- 今後、ブログ等で紹介されることを期待しています
- 複数チームでの開発でも使えます
- 並行開発するときにも使えます
- OFFになっていたらマージが簡単になります
- 複数人で開発していると、ブランチを切ってからブランチがどんどんかけ離れていってしまうことがあります
- 実装した機能がif文で囲われていて、そこがfalseになっているからマージしてもOKという話だと思います
- コンフリクトに時間をかけることも少なくできることが期待できます
- デメリット
- どこかでifを削除する必要があります
- カンファレンス等でそのifを自動削除するツールを作った発表を見ましたが、自動テストが充実していないと事故になりかねないと思われます
- どこかでifを削除する必要があります
- 並行開発するときにも使えます
- ソーシャルゲームの場合はよく使われている機能ではないか
Native type declarations in Laravel 10 skeleton
- 以前のバージョンでは新しくひな型を作ると、引数と戻り値に型宣言が追加されていました
- PHPDocで補足していた型ヒントが消えて、型宣言に置き換えられました
- 下位互換性があるので、型宣言が追加されたからといって既存プロジェクトが動かなくなることはないと記述されています。
「型のない言語はもう許されない(流行らない)のか……?」という意見もありました。
Native type declarations in Laravel 10 skeleton
Invokable Validation rules are the default
Laravel9で導入されたInvokable 検証ルールをデフォルトにする変更です。
この変更によって得られるメリットは2点です。
- コードが簡潔になる
- コストがシンプルになった分、学習コストが減る
Laravelはphp artisan make:rule [ルールのファイル名]
で独自のバリデーションルールを設定できます。
makeコマンドに引数が無い場合はpassesメソッドとmessageメソッドが実装されたコードが生成されます。
下記コードはGithubのものです。
<?php class Quantity implements Rule { protected $messages = []; public function passes($attribute, $value) { if (! is_array($value)) { $this->messages[] = trans('validation.quantity.must_be_an_object'); return false; } if (! array_key_exists('magnitude', $value)) { $this->messages[] = trans('validation.quantity.missing_magnitude'); } if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } return $this->messages === []; } public function message() { return $this->messages; } }
passesでバリデーションを実施してfalseであればmessageをreturnします。
上記のコードでいうと、配列のkeyにmagnitude
が存在しない時、validation.quantity.missing_magnitude
メッセージが返されます。
makeコマンドの引数にinvokableを指定するとinvokeメソッドのみが実装されたシンプルなコードが生成されます。(invokable rule)
php artisan make:rule [ルールのファイル名] --invokable
下記コードはGithubのものです。
<?php class InvokableQuantity implements InvokableRule { public function __invoke($attribute, $value, $fail) { if (! is_array($value)) { return $fail('validation.quantity.must_be_an_object')->translate(); } if (! array_key_exists('magnitude', $value)) { $fail('validation.quantity.missing_magnitude')->translate(); } if (! array_key_exists('units', $value)) { $fail('validation.quantity.missing_units')->translate(); } } }
invokeメソッドの引数$fail
は失敗時に実行されるコールバック関数です。
Laravel10からmake:ruleコマンドに引数invokableを渡さなくてもinvokable ruleが適用されるようになりました。
なお呼び出し方はどちらも同じなので後方互換性が無くなることはありません。
移行する場合もシンプルで、手順は以下になります。
- Quantityクラスpasses内の下記のコードをInvokableQuantityクラスの__invoke関数内にコピペします。
<?php public function passes($attribute, $value) { 中略... if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } }
↓
<?php public function __invoke($attribute, $value, $fail) { if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } }
- messagesに値を入れている部分を$failを使用するように修正します。
<?php public function __invoke($attribute, $value, $fail) { if (! array_key_exists('units', $value)) { $fail('validation.quantity.missing_units')->translate(); } }
Invokable Validation rules are the default
下記のような話があがりました。
- invokeメソッドとは
- 見た目がスッキリしました
- 元々はオプションだった機能をデフォルトにするので、気をつけたほうがいい
- 旧方式のバリデートルールを使っている人が新方式に置き換える場合の修正内容
- passesの中身をInvokeの中身に移植
- messageのところを$failに渡す
- 実装時の手間が省くことができそうです。
- 実案件のコードだとrulesの配列に100行、messageの配列に100行書かないといけなかったです
- それが一気にスッキリかけるようになりました
- Laravel10の雛形ファイルがInvokeからvalidateに変更されています
Processes 関連機能
- 別のプロセス呼び出しがファサード経由で実行可能になりました。
- 並行プロセスの実行と管理が容易になります。
プロセスの実行方法
- Processファサードの
run
メソッドで実行可能です。 - プロセスは同期、非同期を選択できます。
同期実行(処理が終わるまで待つ)
<?php use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); return $result->output();
非同期実行
<?php use Illuminate\Support\Facades\Process; // 非同期実行(タイムアウトも設定) $process = Process::timeout(120)->start('bash import.sh'); // 実行中かどうかを判定することもできる while ($process->running()) { // ... } $result = $process->wait(); // 処理が終わるまで待つことも可能
便利なメソッド群
run
で実行したプロセスの結果を検査するためのメソッドです。
<?php $result = Process::run('ls -la'); $result->successful(); $result->failed(); $result->exitCode(); $result->output(); $result->errorOutput();
並行プロセスの管理
複数のプロセスをプールさせることも簡単になります。
<?php use Illuminate\Process\Pool; use Illuminate\Support\Facades\Process; $pool = Process::pool(function (Pool $pool) { $pool->path(__DIR__)->command('bash import-1.sh'); $pool->path(__DIR__)->command('bash import-2.sh'); $pool->path(__DIR__)->command('bash import-3.sh'); })->start(function (string $type, string $output, int $key) { // ... }); while ($pool->running()->isNotEmpty()) { // ... } $results = $pool->wait();
テストのプロファイル オプション
artisan
のtest
コマンドに--profile
オプションが追加されました。- 実行が遅いテストを一覧表示できる。(最大10個)
Pest Scaffolding
$ laravel new example-application --pest
パスワード生成ヘルパー Str::password()
<?php Str::password()
- 特定の長さのランダムパスワードを生成できます。
- パスワードは、文字、数字、記号、スペースの組み合わせで構成されます。
- デフォルトでは、パスワードの長さは 32 文字です。
設定ファイルパスのカスタマイズ
<?php $app->configPath(__DIR__ . '/../some/path');
設定ファイルへのパスを設定することができるようになりました。
下記のような意見があげられました。 Laravelプロジェクト自体の話も飛び出しました。
- Gitのプルリクで「なんでこれが必要なんだ」という議論がなされていました
- 下記の理由が記述されていました
- 「カスタマイズされたLaravel構造のプロジェクトに取り組んでいて、現在の構成パスはベースパスに残っているが、別ディレクトリに移動したいと考えています」
- 「息の長いプロジェクトだとこういうことも考えないといけないんですね」と感想がありました
- Laravelのプルリクでは「なんで?」が多いです
- (Laravelのプロジェクトに対する)プルリクが4万件
- 「ユーザのメリットを教えてくれるかい?」など少し怖さを感じることもあります
- すごい数のプルリクが来ているから本当に必要なことを判別するため?
- 作者本人から即レスされたり、書いたプルリクはちゃんと読まれている印象
- Symfonyに支えられているのがLaravel
doctrine/dbal is not needed anymore to modify columns in migrations
- Laravel9では、マイグレーションにてテーブル列名を変更する場合は doctrine/dbal をインストールする必要がありました。
- Laravel 10 からは
doctrine/dbal
がインストールは不要になります。
<?php return new class extends Migration { public function up() { Schema::table('foo', function (Blueprint $table) { $table->unsignedBigInteger('bar')->change(); }); } … }
既に doctrine/dbal
がインストールされている場合は、サービスプロバイダに以下の記述が必要です。
<?php use Illuminate\Support\Facades\Schema; … class AppServiceProvider extends ServiceProvider { public function boot() { Schema::useNativeSchemaOperationsIfPossible(); } }
Laravel 10 requires at least Composer 2.2
- Laravel10.xからは Composer 2.2 以上が必要となります。
非推奨となる変更点
以下が非推奨となる変更点です。
- Remove various deprecations Pull Request #41136
getBaseQuery
の削除Illuminate\Database\Eloquent\Relations\Relation
クラスのgetBaseQuery
メソッドの名前がtoBase
に変更されました。
MaintenanceModeException
の削除MaintenanceModeException
はアプリケーションがメンテナンスモードの時にステータスコード503で投げられる例外です。
MocksApplicationServices
- Remove deprecated dates property in Pull Request #42587
Eloquent
モデルの非推奨の$dates
プロパティが削除されました。$casts
プロパティを使用する必要があります。
- Remove handleDeprecation method in Pull Request #42590
- 非推奨のログを出力するメソッドです。
handleDeprecation
メソッドが削除されました。- 代わりに
handleDeprecationError
を使います。 - https://laravel.com/api/9.x/Illuminate/Foundation/Bootstrap/HandleExceptions.html#method_handleDeprecation
assertTimesSent
メソッドが削除された。 #42592- 通知が送信された回数の合計をassertするテスト用のメソッドです。
assertSentTimes
メソッドを代わりに使用します。
ScheduleListCommand.php
の$defaultName
プロパティが削除された。コミットコメント- スケジュールされているタスクのリストを表示するコマンドです。
$defaultName
プロパティは遅延ロード中にコマンドを識別するために使用されていた模様です。(修正コミット)- 使用する側には影響なしと思われます。
Route::home
メソッドが削除された。#42614home
として登録されているルートに遷移するメソッドのようです。
dispatchNow()
が削除された。#42591- ジョブをすぐに実行するメソッドです。
- ジョブをすぐに実行したい場合は代わりに
dispatchSync()
を使用する必要があります。
参考資料
まとめ
今回はLaravel10の新機能について、イベント参加者の生の声を交えてまとめてみましたがいかがでしたでしょうか? イベントでは追加される新機能の内容だけでなく、実用的な知見やノウハウなども紹介されていて、有意義なTech Cafeであったと思います。
「PHP TechCafe」では今後もPHPに関する様々なテーマのイベントを企画していきます。 皆さまのご参加をお待ちしております。