【Laravel Prompts】入力変換オプションの追加

Laravel

Laravel11の公式パッケージPromptsで提案されている入力変換オプションがそろそろ出てきそうです。

Add transformation support by emenkens ?? Pull Request #156 ?? laravel/prompts
I thought it would be nice to be able to make changes to input before it gets validated.ProblemSuppose you want to trim ...

執筆時点でレビュアーのレビューが完了しているので、あとはマージを待つだけの状態です。

おそらく来週のリリースになると思われます。

※筆者がヨコヤリで、更新が必要なファイルを追加指摘したのでマージが遅れました。

※2024/08/08追記:2024/08/07にマージされたので、2024/08/11の週のリリースになると思われます。

※2024/08/21追記:2024/08/21にv0.1.25としてリリースされました。

入力変換オプションの内容

プルリクエストの提案内容を確認していきます。

検証される前に入力を変更できればいいのにと思いました。

問題
ユーザー入力が検証される前にトリミングしたいとします。ユーザー名を尋ねる基本的なプロンプトから始めます。

$username = text(
    label: 'Please select a username:',
    required: true,
);

string(8) ‘ ‘ (スペース 8 個) と入力すると、それは完全に有効なユーザー名になります。もちろん、これは検証によって解決できるため、ユーザー名がトリミングされたときに少なくとも 4 文字になるように検証ルールを追加しましょう。

$username = text(
    label: 'Please select a username:',
    required: true,
    validate: fn (string $value) => strlen(trim($value)) < 4 ? 'Too short!' : '',
);

そして、 string(8) ‘ ssss ‘ (スペース 2 つ、文字 s を 4 回、その後にさらにスペース 2 つ) を入力します。上記の検証ロジックは文字列をトリミングし、string(4) ‘ssss’ が有効なユーザー名であることを確認します。ただし、それは私たちが入力した入力ではありません…

trim関数は検証ロジック内で呼び出されました。つまり、$username の値は引き続き string(8) ‘ ssss ‘ になります。これは、文字列の先頭と末尾をスペースで禁止するなど、より高度な検証ロジックで解決できますが、これはすぐに管理できなくなる可能性があります。

もちろん、上記のコードをすべて trim() でラップすることもできます。

$username = trim(text(
    ...
));

しかし、今では繰り返しになりますが、検証ロジックが変更されるたびにコードを変更することを忘れないようにする必要があります。大規模なフォーム セットを扱う場合、特に複数の開発者が同じコードベースで作業している場合、これは面倒になる可能性があります。

解決策
この PR では、Closure を受け入れ、次のように使用できる transform オプションを追加しました。

$username = text(
    label: 'Please select a username:',
    transform: fn ($value) => trim($value),
    validate: fn (string $value) => strlen($value) < 4 ? 'Too short!' : '',
);

これにより、検証が行われる前に値が変更されます。つまり、同じことを繰り返す必要がなく、$username に割り当てられた値が検証された値と同じであることが確実にわかります。

これは、文字列をトリミングするだけでなく、電話番号のスペースや UUID のハイフンなど、他の種類の不要な文字を除外するのにも非常に便利です。 Str ヘルパーと一緒に使用することもできます。

use Illuminate\Support\Str;

$slug = text(
    label: 'Please enter a slug for your blog post:',
    transform: fn ($value) => Str::slug($value),
    validate: ...
);

すべての入力タイプがサポートされており、各タイプのテストを追加しました。どう考えているか教えてください!

ありがとう

※confirm, password, textarea等の全てのプロンプトでサポートされています。

実際に使ってみる

PR元のリポジトリをローカルにクローンして、「playground/」内のソースをいじって挙動を確認していきます。

PR元のブランチ指定で「prompts_transformations」としてクローンしてみます。

git clone -b transformations https://github.com/
emenkens/prompts.git prompts_transformations

※PR後のリポジトリの対象ブランチはマージ後に削除される可能性が高いです。

※2024/08/08追記:2024/08/07のマージ直後に対象ブランチが削除されました。

フォルダ内に入って、依存パッケージをインストールします。

cd prompts_transformations
composer install

「playground/」内に入り、「text.php」のソースを見てみます。

<?php

use function Laravel\Prompts\text;

require __DIR__.'/../vendor/autoload.php';

$email = text(
    label: 'What is your email address',
    placeholder: 'E.g. taylor@laravel.com',
    validate: fn ($value) => match (true) {
        strlen($value) === 0 => 'Please enter an email address.',
        ! filter_var($value, FILTER_VALIDATE_EMAIL) => 'Please enter a valid email address.',
        default => null,
    },
    hint: 'We will never share your email address with anyone else.',
    transform: fn ($value) => strtolower($value),
);

var_dump($email);

echo str_repeat(PHP_EOL, 5);

※「validate」オプションのClosureでemail形式チェックをしています。

※「transform」オプションのClosureで入力値を全て小文字に変換しています。

※「validate」のClosureが実行される前に「transform」のClosureが実行されるようです。

実行してみます。

php -f text.php

「email」の入力欄が表示されたので、すべて大文字で、ドットなしドメインで入力してみます。

叱られました。

ドットありドメイン名に変更してみます。

email形式チェックを通過したようです。

また、すべて小文字に変換されていますね。

入力のタイプに応じた「transform」や「validate」を事前に定型化してグローバル変数やオブジェクトプロパティとして宣言しておき、それを適用するルールにすると、バグ発生率の低減、利便性の向上、メンテナンス性の向上が見込めると思います。

file-selector-promptへの適用

筆者が作成したlaravel/promptsアプリケーションへの

ファイル選択機能追加パッケージ「file-selector-prompts」ですが、

上記PRのレビュー完了の時点で、「add_transform_option」ブランチに適用済です。

laravel/promptsの新バージョンリリース後にテストを行ってからmainブランチへマージ予定です。

※2024/08/21追記:mainブランチにマージし、v0.1.2としてリリースしました。

▼「playground/fileselector.php」での「transform」オプション実装例

$model = fileselector(
    label: 'Select a file to import.',
    placeholder: 'E.g. ./vendor/autoload.php',
    validate: fn (string $value) => match (true) {
        !is_readable($value) => 'Cannot read the file.',
        default => null,
    },
    hint: 'Input the file path.',
    extensions: [
        '.json',
        '.php',
    ],
    transform: fn ($value) => realpath($value),
);

※相対パスでの入力が「transform」オプションの「realpath()」によって絶対パスに変換されています。

  • 0
  • 0
  • 0
  • 0

コメント

タイトルとURLをコピーしました