跳至内容

Prompts

介绍

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

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

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

安装

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

您还可以使用 Composer 包管理器将 Laravel Prompts 安装在其他 PHP 项目中:

1composer require laravel/prompts

可用Prompts

文本

text函数将向用户Prompts给定的问题,接受他们的输入,然后返回它:

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

您还可以包含占位符文本、默认值和信息Prompts:

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

必需值

如果需要输入值,您可以传递以下required参数:

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

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

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

附加验证

最后,如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

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

闭包将接收已输入的值,并可能返回错误消息,或者null如果验证通过。

或者,你可以利用 Laravel 的验证器功能。为此,请向参数提供一个包含属性名称和所需验证规则的数组validate

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

文本区域

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

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

您还可以包含占位符文本、默认值和信息Prompts:

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

必需值

如果需要输入值,您可以传递以下required参数:

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

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

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

附加验证

最后,如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

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

闭包将接收已输入的值,并可能返回错误消息,或者null如果验证通过。

或者,你可以利用 Laravel 的验证器功能。为此,请向参数提供一个包含属性名称和所需验证规则的数组validate

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

密码

password函数与该函数类似text,但用户在控制台中输入的内容将被屏蔽。这在询问密码等敏感信息时非常有用:

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

您还可以包含占位符文本和信息Prompts:

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

必需值

如果需要输入值,您可以传递以下required参数:

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

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

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

附加验证

最后,如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

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

闭包将接收已输入的值,并可能返回错误消息,或者null如果验证通过。

或者,你可以利用 Laravel 的验证器功能。为此,请向参数提供一个包含属性名称和所需验证规则的数组validate

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

确认

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

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

您还可以包括默认值、“是”和“否”标签的自定义措辞以及信息Prompts:

1$confirmed = confirm(
2 label: 'Do you accept the terms?',
3 default: false,
4 yes: 'I accept',
5 no: 'I decline',
6 hint: 'The terms must be accepted to continue.'
7);

要求“是”

如果有必要,您可以要求用户通过传递required参数来选择“是”:

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

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

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

选择

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

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

您还可以指定默认选择和信息Prompts:

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

您还可以将关联数组传递给options参数以返回所选键而不是其值:

1$role = select(
2 label: 'What role should the user have?',
3 options: [
4 'member' => 'Member',
5 'contributor' => 'Contributor',
6 'owner' => 'Owner',
7 ],
8 default: 'owner'
9);

列表开始滚动之前最多会显示五个选项。您可以通过传递以下scroll参数来自定义:

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

附加验证

与其他Prompts函数不同,该select函数不接受required参数,因为无法选择任何内容。但是,validate如果需要显示选项但阻止其被选中,可以将闭包传递给参数:

1$role = select(
2 label: 'What role should the user have?',
3 options: [
4 'member' => 'Member',
5 'contributor' => 'Contributor',
6 'owner' => 'Owner',
7 ],
8 validate: fn (string $value) =>
9 $value === 'owner' && User::where('role', 'owner')->exists()
10 ? 'An owner already exists.'
11 : null
12);

如果options参数是关联数组,则闭包将接收所选的键,否则将接收所选的值。闭包可能会返回错误消息,或者null返回验证通过的结果。

多选

如果需要用户能够选择多个选项,您可以使用以下multiselect函数:

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

您还可以指定默认选择和信息Prompts:

1use function Laravel\Prompts\multiselect;
2 
3$permissions = multiselect(
4 label: 'What permissions should be assigned?',
5 options: ['Read', 'Create', 'Update', 'Delete'],
6 default: ['Read', 'Create'],
7 hint: 'Permissions may be updated at any time.'
8);

您还可以将关联数组传递给options参数以返回所选选项的键而不是它们的值:

1$permissions = multiselect(
2 label: 'What permissions should be assigned?',
3 options: [
4 'read' => 'Read',
5 'create' => 'Create',
6 'update' => 'Update',
7 'delete' => 'Delete',
8 ],
9 default: ['read', 'create']
10);

列表开始滚动之前最多会显示五个选项。您可以通过传递以下scroll参数来自定义:

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

需要一个值

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

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

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

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

附加验证

validate如果您需要提供一个选项但阻止它被选中,您可以将闭包传递给参数:

1$permissions = multiselect(
2 label: 'What permissions should the user have?',
3 options: [
4 'read' => 'Read',
5 'create' => 'Create',
6 'update' => 'Update',
7 'delete' => 'Delete',
8 ],
9 validate: fn (array $values) => ! in_array('read', $values)
10 ? 'All users require the read permission.'
11 : null
12);

如果options参数是关联数组,则闭包将接收所选的键,否则将接收所选的值。闭包可能会返回错误消息,或者null返回验证通过的结果。

建议

suggest函数可用于为可能的选项提供自动完成功能。无论自动完成Prompts如何,用户仍然可以提供任何答案:

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

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

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

您还可以包含占位符文本、默认值和信息Prompts:

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

必需值

如果需要输入值,您可以传递以下required参数:

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

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

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

附加验证

最后,如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

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

闭包将接收已输入的值,并可能返回错误消息,或者null如果验证通过。

或者,你可以利用 Laravel 的验证器功能。为此,请向参数提供一个包含属性名称和所需验证规则的数组validate

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

如果您有很多选项供用户选择,该search功能允许用户在使用箭头键选择选项之前键入搜索查询来过滤结果:

1use function Laravel\Prompts\search;
2 
3$id = search(
4 label: 'Search for the user that should receive the mail',
5 options: fn (string $value) => strlen($value) > 0
6 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
7 : []
8);

该闭包将接收用户迄今为止输入的文本,并返回一个选项数组。如果返回的是关联数组,则返回所选选项的键,否则返回其值。

当过滤想要返回值的数组时,应该使用array_values函数或valuesCollection 方法来确保数组不会变得关联:

1$names = collect(['Taylor', 'Abigail']);
2 
3$selected = search(
4 label: 'Search for the user that should receive the mail',
5 options: fn (string $value) => $names
6 ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
7 ->values()
8 ->all(),
9);

您还可以包含占位符文本和信息Prompts:

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

列表开始滚动之前最多会显示五个选项。您可以通过传递以下scroll参数来自定义:

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

附加验证

如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

1$id = search(
2 label: 'Search for the user that should receive the mail',
3 options: fn (string $value) => strlen($value) > 0
4 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
5 : [],
6 validate: function (int|string $value) {
7 $user = User::findOrFail($value);
8 
9 if ($user->opted_out) {
10 return 'This user has opted-out of receiving mail.';
11 }
12 }
13);

如果options闭包返回一个关联数组,则闭包将接收所选的键,否则将接收所选的值。闭包可能会返回错误消息,或者null验证通过。

多重搜索

如果您有很多可搜索的选项并且需要用户能够选择多个项目,则该multisearch功能允许用户在使用箭头键和空格键选择选项之前键入搜索查询来过滤结果:

1use function Laravel\Prompts\multisearch;
2 
3$ids = multisearch(
4 'Search for the users that should receive the mail',
5 fn (string $value) => strlen($value) > 0
6 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
7 : []
8);

该闭包将接收用户迄今为止输入的文本,并返回一个选项数组。如果返回的是关联数组,则返回所选选项的键;否则,返回它们的值。

当过滤想要返回值的数组时,应该使用array_values函数或valuesCollection 方法来确保数组不会变得关联:

1$names = collect(['Taylor', 'Abigail']);
2 
3$selected = multisearch(
4 label: 'Search for the users that should receive the mail',
5 options: fn (string $value) => $names
6 ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
7 ->values()
8 ->all(),
9);

您还可以包含占位符文本和信息Prompts:

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

列表开始滚动之前最多会显示五个选项。您可以通过提供以下scroll参数来自定义:

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

需要一个值

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

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

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

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

附加验证

如果您想执行额外的验证逻辑,您可以将闭包传递给validate参数:

1$ids = multisearch(
2 label: 'Search for the users that should receive the mail',
3 options: fn (string $value) => strlen($value) > 0
4 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
5 : [],
6 validate: function (array $values) {
7 $optedOut = User::whereLike('name', '%a%')->findMany($values);
8 
9 if ($optedOut->isNotEmpty()) {
10 return $optedOut->pluck('name')->join(', ', ', and ').' have opted out.';
11 }
12 }
13);

如果options闭包返回一个关联数组,则闭包将接收所选的键;否则,它将接收所选的值。闭包可能会返回错误消息,或者null返回验证通过的结果。

暂停

pause功能可用于向用户显示信息文本,并等待他们按 Enter / Return 键确认是否继续:

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

验证前转换输入

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

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

表格

通常,您会收到多个Prompts,这些Prompts会按顺序显示,以便在执行其他操作之前收集信息。您可以使用该form函数创建一组分组的Prompts供用户完成:

1use function Laravel\Prompts\form;
2 
3$responses = form()
4 ->text('What is your name?', required: true)
5 ->password('What is your password?', validate: ['password' => 'min:8'])
6 ->confirm('Do you accept the terms?')
7 ->submit();

submit方法将返回一个数字索引数组,其中包含表单Prompts的所有响应。但是,您可以通过name参数为每个Prompts指定一个名称。提供名称后,可以通过该名称访问指定Prompts的响应:

1use App\Models\User;
2use function Laravel\Prompts\form;
3 
4$responses = form()
5 ->text('What is your name?', required: true, name: 'name')
6 ->password(
7 label: 'What is your password?',
8 validate: ['password' => 'min:8'],
9 name: 'password'
10 )
11 ->confirm('Do you accept the terms?')
12 ->submit();
13 
14User::create([
15 'name' => $responses['name'],
16 'password' => $responses['password'],
17]);

使用该功能的主要好处form是用户能够使用 返回表单中的上一个PromptsCTRL + U。这样一来,用户无需取消并重新启动整个表单即可修复错误或更改选择。

如果需要对表单中的Prompts进行更精细的控制,可以调用该add方法,而不是直接调用Prompts函数之一。该add方法会传递用户之前提供的所有响应:

1use function Laravel\Prompts\form;
2use function Laravel\Prompts\outro;
3use function Laravel\Prompts\text;
4 
5$responses = form()
6 ->text('What is your name?', required: true, name: 'name')
7 ->add(function ($responses) {
8 return text("How old are you, {$responses['name']}?");
9 }, name: 'age')
10 ->submit();
11 
12outro("Your name is {$responses['name']} and you are {$responses['age']} years old.");

信息性消息

note、、、函数用于显示信息消息infowarningerroralert

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

表格

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

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

旋转

spin函数在执行指定的回调时显示一个旋转图标以及一条可选消息。它用于指示正​​在进行的进程,并在完成后返回回调的结果:

1use function Laravel\Prompts\spin;
2 
3$response = spin(
4 message: 'Fetching response...',
5 callback: fn () => Http::get('http://example.com')
6);

spin函数需要pcntlPHP 扩展程序来为旋转按钮添加动画效果。如果此扩展程序不可用,则会显示静态版本的旋转按钮。

进度条

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

1use function Laravel\Prompts\progress;
2 
3$users = progress(
4 label: 'Updating users',
5 steps: User::all(),
6 callback: fn ($user) => $this->performTask($user)
7);

progress函数的作用类似于映射函数,并将返回一个包含回调每次迭代的返回值的数组。

回调也可以接受Laravel\Prompts\Progress实例,允许您在每次迭代时修改标签和Prompts:

1$users = progress(
2 label: 'Updating users',
3 steps: User::all(),
4 callback: function ($user, $progress) {
5 $progress
6 ->label("Updating {$user->name}")
7 ->hint("Created on {$user->created_at}");
8 
9 return $this->performTask($user);
10 },
11 hint: 'This may take some time.'
12);

有时,您可能需要对进度条的推进方式进行更多手动控制。首先,定义Processes将迭代的总步骤数。然后,advance在处理完每个项目后,通过以下方法推进进度条:

1$progress = progress(label: 'Updating users', steps: 10);
2 
3$users = User::all();
4 
5$progress->start();
6 
7foreach ($users as $user) {
8 $this->performTask($user);
9 
10 $progress->advance();
11}
12 
13$progress->finish();

清理终端

clear函数可用于清除用户终端:

1use function Laravel\Prompts\clear;
2 
3clear();

终端注意事项

端子宽度

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

终端高度

对于任何接受scroll参数的Prompts,配置的值将自动缩小以适合用户终端的高度,包括验证消息的空间。

不受支持的环境和后备

Laravel Prompts 支持 macOS、Linux 和 Windows(需安装 WSL)。由于 Windows 版 PHP 的限制,目前无法在 WSL 之外的 Windows 上使用 Laravel Prompts。

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

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

后备条件

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

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

后备行为

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

1use Laravel\Prompts\TextPrompt;
2use Symfony\Component\Console\Question\Question;
3use Symfony\Component\Console\Style\SymfonyStyle;
4 
5TextPrompt::fallbackUsing(function (TextPrompt $prompt) use ($input, $output) {
6 $question = (new Question($prompt->label, $prompt->default ?: null))
7 ->setValidator(function ($answer) use ($prompt) {
8 if ($prompt->required && $answer === null) {
9 throw new \RuntimeException(
10 is_string($prompt->required) ? $prompt->required : 'Required.'
11 );
12 }
13 
14 if ($prompt->validate) {
15 $error = ($prompt->validate)($answer ?? '');
16 
17 if ($error) {
18 throw new \RuntimeException($error);
19 }
20 }
21 
22 return $answer;
23 });
24 
25 return (new SymfonyStyle($input, $output))
26 ->askQuestion($question);
27});

必须为每个Prompts类单独配置回退。闭包将接收Prompts类的实例,并且必须返回与Prompts类对应的类型。