【Laravel Prompts】ファイル選択フォーム

Laravel

Laravel11の公式パッケージPromptsにファイル選択機能はありませんが、既存のsuggestを使ってそれっぽいものを実装できます。

Laravel - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...

前提条件

  • Laravel Promptsインストール済

Laravel Promptsのインストールについては次の記事をご覧ください。

ファイル選択機能を実装する

Laravel Promptsのsuggestと、

PHPのRecursiveDirectoryIteratorを使います。

▼ディレクトリ内のエントリーを返すクラス

「src/Dir.php」

<?php

namespace MyLib;

class Dir
{
    /**
     * ディレクトリ内の全エントリーをRecursiveDirectoryIteratorで返す
     *
     * @param   string  $value
     * @return  \RecursiveDirectoryIterator|\RegexIterator|array{}
     */
    public static function glob(string $value)
    {
        if (strlen($value) === 0) {
            return new \RecursiveDirectoryIterator('.');
        }
        if (str_ends_with($value, '/')) {
            if (is_dir($value) && is_readable($value)) {
                return new \RecursiveDirectoryIterator($value);
            }
            return [];
        }
        $dir = './';
        $file = $value;
        if (str_contains($value, '/')) {
            $dir = dirname($value);
            $file = pathinfo($value)['basename'];
        }
        $pattern = sprintf("/%s/", preg_quote($file));
        return new \RegexIterator(
            new \RecursiveDirectoryIterator($dir),
            $pattern
        );
    }

    /**
     * ディレクトリ内の全エントリーを配列で返す
     *
     * @param   string  $value
     * @return  string[]
     */
    public static function entries(string $value)
    {
        $entries = array_map(
            fn (string $e) => is_dir($e)
                ? str_replace('//', '/', $e . '/')
                : str_replace('//', '/', $e),
            iterator_to_array(self::glob($value))
        );
        sort($entries);
        return $entries;
    }
}

▼ファイル選択機能を実装したコード

「src/selectFile.php」

<?php

require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/Dir.php';

use function Laravel\Prompts\suggest;

$selected = suggest(
    label: 'ファイルを選んでください。',
    placeholder: './vendor/autoload.php',
    default: '',
    options: fn (string $value) => MyLib\Dir::entries($value),
    required: 'ファイル選択は必須です。',
    validate: fn (string $value) => match (true) {
        is_dir($value) => 'ディレクトリは選択できません。',
        !is_readable($value) => '読み取りできません。',
        default => null,
    },
);

var_dump($selected);

動作確認

上記のコードを実行してみます。

php -f src/selectFile.php

[↓]または[↑]を押すとカレントディレクトリ内のエントリーが表示されます。

「./vendor/」を選択して確定してみます。

入力欄に「./vendor/」が入力補間されます。

「ディレクトリは選択できません。」は無視してください。

[→]を押してから[End]でカーソルを入力欄最後尾に配置し、

続けて「.」を入力すると、候補の選択肢が「./vendor/.」内のエントリーに切り替わります。

「./vendor/autoload.php」を選択してみます。

選択したファイルパスが返されました。

「/root/」のような読取権限の無いディレクトリの場合は

エントリーが表示されません。

「/etc/sudoers」のような読取権限のないファイルは選択できません。

まとめ

一応、それっぽい動きになっていると思いますが、

課題を挙げるとすれば次のものでしょうか。

  • ディレクトリ選択後に文字列更新しないとエントリーが更新されない
  • ディレクトリ選択時にバリデーションエラーが表示される
  • エントリーの検索があいまい検索になっている
  • 拡張子のフィルタリングは施していない

後半の課題は「Dir.php」をリファクタリングすれば解決できそうですが、前半の課題をクリアするなら、やはり機能開発して公式リポジトリにPRするべきでしょうか。

※2024/05/29 ソッコー却下されるかもしれませんが、Laravel Prompts公式リポジトリにPRしておきました。

Feature: FileSelector by macocci7 · Pull Request #150 · laravel/prompts
This PR adds support for file selector. Here's a demo video, which you can play with for yourself using php playground/f...

※ 上記課題の「あいまい検索」以外は解決済です。

※ あえて「あいまい検索」は残しておきました。

※2024/05/30追記:ソッコー却下されましたが、否定的な理由では無く、慎重な表現を用いて提案が示されています。

▼自動翻訳

Laravel へのプルリクエストをありがとうございます。

残念ながら、今のところこのコードのマージは延期する予定です。フレームワークを適切に維持する能力を維持するには、含めるコードの量について細心の注意を払う必要があります。

可能であれば、コミュニティがあなたの貢献を引き続き活用できるように、コードをパッケージとしてリリースすることを検討してください。

このコードがフレームワークのバグを修正していると確信できる場合は、追加の説明とともにフォローアップ コメントで私に「@」を付けてください。そうすれば、GitHub からあなたの応答の通知が私に送信されます。

※ 特にバグ修正はしていないので、提案通りにパッケージとしてリリースすることの「検討を加速させる所存であります」。

※ 記念にDEMO動画を貼り付けておきます。

※ 2024/06/02 「file-selector-prompt」パッケージとしてリリースしました。

コメント

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