Skip to content
全部文档

Prompts

简介

Laravel Prompts 是一个 PHP 包,可为命令行应用添加美观、易用的表单,具备占位符与验证等类似浏览器的特性。

Laravel Prompts 非常适合在 Artisan 控制台命令 中接收用户输入,也可用于任何命令行 PHP 项目。

INFO

Laravel Prompts 支持 macOS、Linux 及带 WSL 的 Windows。更多信息请参阅不支持的环境与回退文档。

安装

最新版 Laravel 已内置 Laravel Prompts。

也可通过 Composer 在其他 PHP 项目中安装 Laravel Prompts:

shell
composer require laravel/prompts

可用提示

文本

text 函数会以给定问题提示用户,接收输入并返回:

php
use function Laravel\Prompts\text;

$name = text('What is your name?');

还可包含占位符、默认值与信息提示:

php
$name = text(
    label: 'What is your name?',
    placeholder: 'E.g. Taylor Otwell',
    default: $user?->name,
    hint: 'This will be displayed on your profile.'
);

必填值

若要求必须输入值,可传入 required 参数:

php
$name = text(
    label: 'What is your name?',
    required: true
);

若要自定义验证消息,也可传入字符串:

php
$name = text(
    label: 'What is your name?',
    required: 'Your name is required.'
);

额外验证

最后,若要执行额外验证逻辑,可向 validate 参数传入闭包:

php
$name = text(
    label: 'What is your name?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);

闭包接收已输入的值,验证失败可返回错误消息,通过则返回 null

也可利用 Laravel 验证器:向 validate 参数提供包含属性名与验证规则的数组:

php
$name = text(
    label: 'What is your name?',
    validate: ['name' => 'required|max:255|unique:users']
);

多行文本

textarea 函数会以给定问题提示用户,通过多行文本框接收输入并返回:

php
use function Laravel\Prompts\textarea;

$story = textarea('Tell me a story.');

还可包含占位符、默认值与信息提示:

php
$story = textarea(
    label: 'Tell me a story.',
    placeholder: 'This is a story about...',
    hint: 'This will be displayed on your profile.'
);

必填值

若要求必须输入值,可传入 required 参数:

php
$story = textarea(
    label: 'Tell me a story.',
    required: true
);

若要自定义验证消息,也可传入字符串:

php
$story = textarea(
    label: 'Tell me a story.',
    required: 'A story is required.'
);

额外验证

最后,若要执行额外验证逻辑,可向 validate 参数传入闭包:

php
$story = textarea(
    label: 'Tell me a story.',
    validate: fn (string $value) => match (true) {
        strlen($value) < 250 => 'The story must be at least 250 characters.',
        strlen($value) > 10000 => 'The story must not exceed 10,000 characters.',
        default => null
    }
);

闭包接收已输入的值,验证失败可返回错误消息,通过则返回 null

也可利用 Laravel 验证器:向 validate 参数提供包含属性名与验证规则的数组:

php
$story = textarea(
    label: 'Tell me a story.',
    validate: ['story' => 'required|max:10000']
);

密码

password 函数类似 text,但用户在控制台输入时会被掩码显示,适用于密码等敏感信息:

php
use function Laravel\Prompts\password;

$password = password('What is your password?');

还可包含占位符与信息提示:

php
$password = password(
    label: 'What is your password?',
    placeholder: 'password',
    hint: 'Minimum 8 characters.'
);

必填值

若要求必须输入值,可传入 required 参数:

php
$password = password(
    label: 'What is your password?',
    required: true
);

若要自定义验证消息,也可传入字符串:

php
$password = password(
    label: 'What is your password?',
    required: 'The password is required.'
);

额外验证

最后,若要执行额外验证逻辑,可向 validate 参数传入闭包:

php
$password = password(
    label: 'What is your password?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 8 => 'The password must be at least 8 characters.',
        default => null
    }
);

闭包接收已输入的值,验证失败可返回错误消息,通过则返回 null

也可利用 Laravel 验证器:向 validate 参数提供包含属性名与验证规则的数组:

php
$password = password(
    label: 'What is your password?',
    validate: ['password' => 'min:8']
);

确认

若需向用户确认「是或否」,可使用 confirm 函数。用户可用方向键或按 y/n 选择,函数返回 truefalse

php
use function Laravel\Prompts\confirm;

$confirmed = confirm('Do you accept the terms?');

还可包含默认值、自定义「是」「否」标签文案与信息提示:

php
$confirmed = confirm(
    label: 'Do you accept the terms?',
    default: false,
    yes: 'I accept',
    no: 'I decline',
    hint: 'The terms must be accepted to continue.'
);

要求选择「是」

如有必要,可传入 required 参数要求用户必须选择「是」:

php
$confirmed = confirm(
    label: 'Do you accept the terms?',
    required: true
);

若要自定义验证消息,也可传入字符串:

php
$confirmed = confirm(
    label: 'Do you accept the terms?',
    required: 'You must accept the terms to continue.'
);

选择

若需用户从预定义选项中选择,可使用 select 函数:

php
use function Laravel\Prompts\select;

$role = select(
    label: 'What role should the user have?',
    options: ['Member', 'Contributor', 'Owner']
);

还可指定默认选项与信息提示:

php
$role = select(
    label: 'What role should the user have?',
    options: ['Member', 'Contributor', 'Owner'],
    default: 'Owner',
    hint: 'The role may be changed at any time.'
);

也可向 options 传入关联数组,使返回选中项的键而非值:

php
$role = select(
    label: 'What role should the user have?',
    options: [
        'member' => 'Member',
        'contributor' => 'Contributor',
        'owner' => 'Owner',
    ],
    default: 'owner'
);

列表滚动前最多显示五个选项。可通过 scroll 参数自定义:

php
$role = select(
    label: 'Which category would you like to assign?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);

额外验证

与其他提示函数不同,select 不接受 required 参数,因为无法不选任何项。但若需展示某选项却禁止选中,可向 validate 传入闭包:

php
$role = select(
    label: 'What role should the user have?',
    options: [
        'member' => 'Member',
        'contributor' => 'Contributor',
        'owner' => 'Owner',
    ],
    validate: fn (string $value) =>
        $value === 'owner' && User::where('role', 'owner')->exists()
            ? 'An owner already exists.'
            : null
);

options 为关联数组,闭包接收选中的键,否则接收选中的值。闭包可返回错误消息,通过则返回 null

多选

若需用户选择多个选项,可使用 multiselect 函数:

php
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete']
);

还可指定默认选项与信息提示:

php
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete'],
    default: ['Read', 'Create'],
    hint: 'Permissions may be updated at any time.'
);

也可向 options 传入关联数组,使返回选中项的键而非值:

php
$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: [
        'read' => 'Read',
        'create' => 'Create',
        'update' => 'Update',
        'delete' => 'Delete',
    ],
    default: ['read', 'create']
);

列表滚动前最多显示五个选项。可通过 scroll 参数自定义:

php
$categories = multiselect(
    label: 'What categories should be assigned?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);

要求选择值

默认用户可选择零个或多个选项。可传入 required 参数要求至少选择一个:

php
$categories = multiselect(
    label: 'What categories should be assigned?',
    options: Category::pluck('name', 'id'),
    required: true
);

若要自定义验证消息,可向 required 参数提供字符串:

php
$categories = multiselect(
    label: 'What categories should be assigned?',
    options: Category::pluck('name', 'id'),
    required: 'You must select at least one category'
);

额外验证

若需展示某选项却禁止选中,可向 validate 传入闭包:

php
$permissions = multiselect(
    label: 'What permissions should the user have?',
    options: [
        'read' => 'Read',
        'create' => 'Create',
        'update' => 'Update',
        'delete' => 'Delete',
    ],
    validate: fn (array $values) => ! in_array('read', $values)
        ? 'All users require the read permission.'
        : null
);

options 为关联数组,闭包接收选中的键数组,否则接收选中的值数组。闭包可返回错误消息,通过则返回 null

建议

suggest 函数可为可能选项提供自动完成提示,用户仍可输入任意答案:

php
use function Laravel\Prompts\suggest;

$name = suggest('What is your name?', ['Taylor', 'Dayle']);

也可将闭包作为 suggest 的第二个参数。用户每输入一个字符都会调用闭包;闭包接收当前输入字符串,返回自动完成选项数组:

php
$name = suggest(
    label: 'What is your name?',
    options: fn ($value) => collect(['Taylor', 'Dayle'])
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
)

还可包含占位符、默认值与信息提示:

php
$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle'],
    placeholder: 'E.g. Taylor',
    default: $user?->name,
    hint: 'This will be displayed on your profile.'
);

必填值

若要求必须输入值,可传入 required 参数:

php
$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle'],
    required: true
);

若要自定义验证消息,也可传入字符串:

php
$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle'],
    required: 'Your name is required.'
);

额外验证

最后,若要执行额外验证逻辑,可向 validate 参数传入闭包:

php
$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle'],
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);

闭包接收已输入的值,验证失败可返回错误消息,通过则返回 null

也可利用 Laravel 验证器:向 validate 参数提供包含属性名与验证规则的数组:

php
$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle'],
    validate: ['name' => 'required|min:3|max:255']
);

若选项很多,search 函数允许用户输入搜索查询过滤结果,再用方向键选择:

php
use function Laravel\Prompts\search;

$id = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);

闭包接收用户已输入的文本,须返回选项数组。若返回关联数组则返回选中项的键,否则返回值。

过滤数组且打算返回值时,应使用 array_values 或集合的 values 方法,避免数组变为关联数组:

php
$names = collect(['Taylor', 'Abigail']);

$selected = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => $names
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
        ->values()
        ->all(),
);

还可包含占位符与信息提示:

php
$id = search(
    label: 'Search for the user that should receive the mail',
    placeholder: 'E.g. Taylor Otwell',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    hint: 'The user will receive an email immediately.'
);

列表滚动前最多显示五个选项。可通过 scroll 参数自定义:

php
$id = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    scroll: 10
);

额外验证

若要执行额外验证逻辑,可向 validate 传入闭包:

php
$id = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    validate: function (int|string $value) {
        $user = User::findOrFail($value);

        if ($user->opted_out) {
            return 'This user has opted-out of receiving mail.';
        }
    }
);

options 闭包返回关联数组,验证闭包接收选中的键,否则接收选中的值。可返回错误消息,通过则返回 null

多选搜索

若可搜索选项很多且需多选,multisearch 允许用户输入搜索查询过滤结果,再用方向键与空格选择:

php
use function Laravel\Prompts\multisearch;

$ids = multisearch(
    'Search for the users that should receive the mail',
    fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);

闭包接收用户已输入的文本,须返回选项数组。若返回关联数组则返回选中项的键,否则返回值。

过滤数组且打算返回值时,应使用 array_values 或集合的 values 方法,避免数组变为关联数组:

php
$names = collect(['Taylor', 'Abigail']);

$selected = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => $names
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
        ->values()
        ->all(),
);

还可包含占位符与信息提示:

php
$ids = multisearch(
    label: 'Search for the users that should receive the mail',
    placeholder: 'E.g. Taylor Otwell',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    hint: 'The user will receive an email immediately.'
);

列表滚动前最多显示五个选项。可通过 scroll 参数自定义:

php
$ids = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    scroll: 10
);

要求选择值

默认用户可选择零个或多个选项。可传入 required 参数要求至少选择一个:

php
$ids = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    required: true
);

若要自定义验证消息,也可向 required 参数提供字符串:

php
$ids = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    required: 'You must select at least one user.'
);

额外验证

若要执行额外验证逻辑,可向 validate 传入闭包:

php
$ids = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    validate: function (array $values) {
        $optedOut = User::whereLike('name', '%a%')->findMany($values);

        if ($optedOut->isNotEmpty()) {
            return $optedOut->pluck('name')->join(', ', ', and ').' have opted out.';
        }
    }
);

options 闭包返回关联数组,验证闭包接收选中的键,否则接收选中的值。可返回错误消息,通过则返回 null

暂停

pause 函数可向用户显示信息文本,并等待其按 Enter/Return 确认继续:

php
use function Laravel\Prompts\pause;

pause('Press ENTER to continue.');

验证前转换输入

有时你可能希望在验证前转换提示输入,例如去除字符串首尾空白。许多提示函数提供接受闭包的 transform 参数:

php
$name = text(
    label: 'What is your name?',
    transform: fn (string $value) => trim($value),
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);

表单

通常会有多个提示依次显示以收集信息。可使用 form 函数创建一组提示供用户完成:

php
use function Laravel\Prompts\form;

$responses = form()
    ->text('What is your name?', required: true)
    ->password('What is your password?', validate: ['password' => 'min:8'])
    ->confirm('Do you accept the terms?')
    ->submit();

submit 方法返回包含表单所有响应的数字索引数组。也可通过 name 参数为每个提示命名,之后可通过该名称访问对应响应:

php
use App\Models\User;
use function Laravel\Prompts\form;

$responses = form()
    ->text('What is your name?', required: true, name: 'name')
    ->password(
        label: 'What is your password?',
        validate: ['password' => 'min:8'],
        name: 'password'
    )
    ->confirm('Do you accept the terms?')
    ->submit();

User::create([
    'name' => $responses['name'],
    'password' => $responses['password'],
]);

使用 form 的主要好处是用户可用 CTRL + U 返回之前的提示,修正错误或更改选择而无需取消并重新开始整个表单。

若需对表单中的提示做更细粒度控制,可调用 add 方法而非直接调用提示函数。add 方法会传入用户此前的所有响应:

php
use function Laravel\Prompts\form;
use function Laravel\Prompts\outro;

$responses = form()
    ->text('What is your name?', required: true, name: 'name')
    ->add(function ($responses) {
        return text("How old are you, {$responses['name']}?");
    }, name: 'age')
    ->submit();

outro("Your name is {$responses['name']} and you are {$responses['age']} years old.");

信息消息

可使用 noteinfowarningerroralert 函数显示信息消息:

php
use function Laravel\Prompts\info;

info('Package installed successfully.');

表格

table 函数便于显示多行多列数据,只需提供列名与表格数据:

php
use function Laravel\Prompts\table;

table(
    headers: ['Name', 'Email'],
    rows: User::all(['name', 'email'])->toArray()
);

旋转等待

spin 函数在执行指定回调时显示旋转指示器与可选消息,表示进行中的过程,完成后返回回调结果:

php
use function Laravel\Prompts\spin;

$response = spin(
    message: 'Fetching response...',
    callback: fn () => Http::get('http://example.com')
);

WARNING

spin 需要 PCNTL PHP 扩展以动画显示旋转器;不可用时将显示静态版本。

进度条

对于长时间运行的任务,显示进度条有助于告知完成度。使用 progress 函数,Laravel 会显示进度条并在每次迭代可迭代值时推进进度:

php
use function Laravel\Prompts\progress;

$users = progress(
    label: 'Updating users',
    steps: User::all(),
    callback: fn ($user) => $this->performTask($user)
);

progress 函数类似 map,返回包含回调每次迭代返回值的数组。

回调也可接收 Laravel\Prompts\Progress 实例,以便在每次迭代中修改标签与提示:

php
$users = progress(
    label: 'Updating users',
    steps: User::all(),
    callback: function ($user, $progress) {
        $progress
            ->label("Updating {$user->name}")
            ->hint("Created on {$user->created_at}");

        return $this->performTask($user);
    },
    hint: 'This may take some time.'
);

有时你需要更手动地控制进度条推进。先定义过程总步数,然后在处理每项后通过 advance 方法推进:

php
$progress = progress(label: 'Updating users', steps: 10);

$users = User::all();

$progress->start();

foreach ($users as $user) {
    $this->performTask($user);

    $progress->advance();
}

$progress->finish();

清空终端

可使用 clear 函数清空用户终端:

text
use function Laravel\Prompts\clear;

clear();

终端注意事项

终端宽度

若任何标签、选项或验证消息长度超过用户终端「列」数,将自动截断以适应。若用户可能使用较窄终端,请尽量缩短这些字符串。为支持 80 列终端,通常安全最大长度约为 74 字符。

终端高度

对于接受 scroll 参数的提示,配置值会自动缩减以适应用户终端高度,并预留验证消息空间。

不支持的环境与回退

Laravel Prompts 支持 macOS、Linux 及带 WSL 的 Windows。由于 Windows 版 PHP 的限制,目前在 WSL 外的 Windows 上无法使用 Laravel Prompts。

因此 Laravel Prompts 支持回退到替代实现,例如 Symfony Console Question Helper

INFO

在 Laravel 框架中使用 Laravel Prompts 时,各提示的回退已配置好,在不支持的环境中会自动启用。

回退条件

若未使用 Laravel 或需自定义何时使用回退,可向 Prompt 类的 fallbackWhen 静态方法传入布尔值:

php
use Laravel\Prompts\Prompt;

Prompt::fallbackWhen(
    ! $input->isInteractive() || windows_os() || app()->runningUnitTests()
);

回退行为

若未使用 Laravel 或需自定义回退行为,可向各提示类的 fallbackUsing 静态方法传入闭包:

php
use Laravel\Prompts\TextPrompt;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;

TextPrompt::fallbackUsing(function (TextPrompt $prompt) use ($input, $output) {
    $question = (new Question($prompt->label, $prompt->default ?: null))
        ->setValidator(function ($answer) use ($prompt) {
            if ($prompt->required && $answer === null) {
                throw new \RuntimeException(
                    is_string($prompt->required) ? $prompt->required : 'Required.'
                );
            }

            if ($prompt->validate) {
                $error = ($prompt->validate)($answer ?? '');

                if ($error) {
                    throw new \RuntimeException($error);
                }
            }

            return $answer;
        });

    return (new SymfonyStyle($input, $output))
        ->askQuestion($question);
});

回退须为每个提示类单独配置。闭包接收提示类实例,须返回该提示所需的合适类型。