跳到内容

Prompts

简介

Laravel Prompts 是一个 PHP 包,用于为你的命令行应用程序添加美观且用户友好的表单,具有类似浏览器的功能,包括占位符文本和验证。

Laravel Prompts 非常适合在你的 Artisan 控制台命令中接受用户输入,但它也可以在任何命令行 PHP 项目中使用。

lightbulb - Laravel 框架

Laravel Prompts 支持 macOS、Linux 和 Windows with WSL。有关更多信息,请参阅我们关于 不支持的环境和回退的文档。

安装

Laravel Prompts 已经包含在最新版本的 Laravel 中。

Laravel Prompts 也可以通过使用 Composer 包管理器安装在你的其他 PHP 项目中

composer require laravel/prompts

可用提示

文本

text 函数将使用给定的问题提示用户,接受他们的输入,然后返回它

use function Laravel\Prompts\text;
 
$name = text('What is your name?');

你还可以包括占位符文本、默认值和信息性提示

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

必填值

如果你需要输入一个值,你可以传递 required 参数

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

如果你想自定义验证消息,你也可以传递一个字符串

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

其他验证

最后,如果你想执行额外的验证逻辑,你可以传递一个闭包到 validate 参数

$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 参数提供一个包含属性名称和所需验证规则的数组

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

文本区域

textarea 函数将使用给定的问题提示用户,通过多行文本区域接受他们的输入,然后返回它

use function Laravel\Prompts\textarea;
 
$story = textarea('Tell me a story.');

你还可以包括占位符文本、默认值和信息性提示

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

必填值

如果你需要输入一个值,你可以传递 required 参数

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

如果你想自定义验证消息,你也可以传递一个字符串

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

其他验证

最后,如果你想执行额外的验证逻辑,你可以传递一个闭包到 validate 参数

$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 参数提供一个包含属性名称和所需验证规则的数组

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

密码

password 函数与 text 函数类似,但用户的输入会在控制台中输入时被屏蔽。这在询问密码等敏感信息时很有用

use function Laravel\Prompts\password;
 
$password = password('What is your password?');

你还可以包括占位符文本和信息性提示

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

必填值

如果你需要输入一个值,你可以传递 required 参数

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

如果你想自定义验证消息,你也可以传递一个字符串

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

其他验证

最后,如果你想执行额外的验证逻辑,你可以传递一个闭包到 validate 参数

$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 参数提供一个包含属性名称和所需验证规则的数组

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

确认

如果你需要询问用户 “是” 或 “否” 的确认,你可以使用 confirm 函数。用户可以使用箭头键或按 yn 选择他们的响应。此函数将返回 truefalse

use function Laravel\Prompts\confirm;
 
$confirmed = confirm('Do you accept the terms?');

你还可以包括默认值、自定义 “是” 和 “否” 标签的措辞以及信息性提示

$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 参数来要求你的用户选择 “是”

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

如果你想自定义验证消息,你也可以传递一个字符串

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

选择

如果你需要用户从预定义的一组选项中选择,你可以使用 select 函数

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

你还可以指定默认选项和信息性提示

$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 参数,以返回所选键而不是其值

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

在列表开始滚动之前,将显示最多五个选项。你可以通过传递 scroll 参数来自定义此项

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

其他验证

与其他提示函数不同,select 函数不接受 required 参数,因为不可能什么都不选。但是,如果你需要显示一个选项但阻止它被选中,你可以传递一个闭包到 validate 参数

$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 函数

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

你还可以指定默认选项和信息性提示

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 参数,以返回所选选项的键而不是其值

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

在列表开始滚动之前,将显示最多五个选项。你可以通过传递 scroll 参数来自定义此项

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

需要一个值

默认情况下,用户可以选择零个或多个选项。你可以传递 required 参数来强制选择一个或多个选项

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

如果你想自定义验证消息,你可以向 required 参数提供一个字符串

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

其他验证

如果你需要显示一个选项但阻止它被选中,你可以传递一个闭包到 validate 参数

$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 函数可用于为可能的选项提供自动完成功能。用户仍然可以提供任何答案,而与自动完成提示无关

use function Laravel\Prompts\suggest;
 
$name = suggest('What is your name?', ['Taylor', 'Dayle']);

或者,你可以将闭包作为 suggest 函数的第二个参数传递。每次用户键入输入字符时都会调用该闭包。闭包应接受一个包含用户到目前为止的输入的字符串参数,并返回一个用于自动完成的选项数组

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

你还可以包括占位符文本、默认值和信息性提示

$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 参数

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

如果你想自定义验证消息,你也可以传递一个字符串

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

其他验证

最后,如果你想执行额外的验证逻辑,你可以传递一个闭包到 validate 参数

$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 参数提供一个包含属性名称和所需验证规则的数组

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

如果你有很多选项供用户选择,则 search 函数允许用户键入搜索查询来过滤结果,然后再使用箭头键选择选项

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 Collection 方法来确保该数组不会变成关联数组

$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(),
);

你还可以包括占位符文本和信息性提示

$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 参数来自定义此项

$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 参数

$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 函数允许用户键入搜索查询来过滤结果,然后再使用箭头键和空格键选择选项

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 Collection 方法来确保该数组不会变成关联数组

$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(),
);

你还可以包括占位符文本和信息性提示

$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 参数来自定义此项

$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 参数来强制选择一个或多个选项

$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 参数提供一个字符串。

$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 参数

$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 键以确认他们希望继续。

use function Laravel\Prompts\pause;
 
pause('Press ENTER to continue.');

验证前转换输入

有时您可能希望在进行验证之前转换提示输入。例如,您可能希望从任何提供的字符串中删除空格。为了实现这一点,许多提示函数提供了一个 transform 参数,该参数接受一个闭包。

$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 函数创建一个分组的提示集合,供用户完成。

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 参数为每个提示提供一个名称。当提供名称时,可以通过该名称访问已命名提示的响应。

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 方法会传递用户提供的所有先前的响应。

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 函数可用于显示信息性消息。

use function Laravel\Prompts\info;
 
info('Package installed successfully.');

表格

table 函数可以轻松地显示多行和多列数据。您只需提供列名和表格的数据即可。

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

旋转

spin 函数在执行指定的闭包时显示一个微调器以及可选的消息。它用于指示正在进行的过程,并在完成后返回闭包的结果。

use function Laravel\Prompts\spin;
 
$response = spin(
message: 'Fetching response...',
callback: fn () => Http::get('http://example.com')
);
exclamation - Laravel 框架

spin 函数需要 pcntl PHP 扩展来动画显示微调器。当此扩展不可用时,将显示静态版本的微调器。

进度条

对于长时间运行的任务,显示一个进度条,告知用户任务的完成程度会很有帮助。使用 progress 函数,Laravel 将显示一个进度条,并为给定可迭代值的每次迭代推进其进度。

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

progress 函数的行为类似于 map 函数,并将返回一个数组,其中包含您的闭包每次迭代的返回值。

该闭包还可以接受 Laravel\Prompts\Progress 实例,允许您在每次迭代时修改标签和提示。

$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 方法推进进度条。

$progress = progress(label: 'Updating users', steps: 10);
 
$users = User::all();
 
$progress->start();
 
foreach ($users as $user) {
$this->performTask($user);
 
$progress->advance();
}
 
$progress->finish();

清除终端

可以使用 clear 函数清除用户的终端。

use function Laravel\Prompts\clear;
 
clear();

终端注意事项

终端宽度

如果任何标签、选项或验证消息的长度超过用户终端中的“列”数,它将自动截断以适应。如果您的用户可能正在使用较窄的终端,请考虑最小化这些字符串的长度。通常安全的长度上限是 74 个字符,以支持 80 个字符的终端。

终端高度

对于任何接受 scroll 参数的提示,配置的值将自动减少以适应用户终端的高度,包括验证消息的空间。

不支持的环境和回退

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

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

lightbulb - Laravel 框架

当在 Laravel 框架中使用 Laravel Prompts 时,每个提示的回退都已为您配置,并且将在不受支持的环境中自动启用。

回退条件

如果您未使用 Laravel 或需要自定义何时使用回退行为,您可以将布尔值传递给 Prompt 类上的 fallbackWhen 静态方法。

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

回退行为

如果您未使用 Laravel 或需要自定义回退行为,您可以将闭包传递给每个提示类上的 fallbackUsing 静态方法。

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);
});

必须为每个提示类单独配置回退。该闭包将接收提示类的实例,并且必须为提示返回适当的类型。