Laravel Prompts 是一个 PHP 包,用于为命令行应用程序添加美观且用户友好的表单,具有类似浏览器的功能,包括占位符文本和验证。
Laravel Prompts 非常适合在你的 Artisan 控制台命令中接受用户输入,但它也可以用于任何命令行 PHP 项目。
[!NOTE]
Laravel Prompts 支持 macOS、Linux 和带有 WSL 的 Windows。有关更多信息,请参阅我们关于不支持的环境和回退的文档。
Laravel Prompts 已经包含在最新版本的 Laravel 中。
你也可以通过 Composer 包管理器在其他 PHP 项目中安装 Laravel Prompts:
composer require laravel/promptstext 函数将向用户显示给定问题,接受其输入,然后返回它:
use function Laravel\Prompts\text;
$name = text('你的名字是什么?');你也可以包含占位符文本、默认值和提示信息:
$name = text(
label: '你的名字是什么?',
placeholder: '例如:Taylor Otwell',
default: $user?->name,
hint: '这将在你的个人资料上显示。'
);如果你需要输入一个值,你可以传递 required 参数:
$name = text(
label: '你的名字是什么?',
required: true
);如果你想自定义验证消息,你也可以传递一个字符串:
$name = text(
label: '你的名字是什么?',
required: '你的名字是必填项。'
);最后,如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$name = text(
label: '你的名字是什么?',
validate: fn (string $value) => match (true) {
strlen($value) < 3 => '姓名必须至少包含 3 个字符。',
strlen($value) > 255 => '姓名不能超过 255 个字符。',
default => null
}
);该闭包将接收已输入的值,并可返回错误消息,如果验证通过则返回 null。
或者,你可以利用 Laravel 验证器的强大功能。为此,请向 validate 参数提供一个包含属性名称和所需验证规则的数组:
$name = text(
label: '你的名字是什么?',
validate: ['name' => 'required|max:255|unique:users']
);textarea 函数将向用户显示给定问题,通过多行文本域接受其输入,然后返回它:
use function Laravel\Prompts\textarea;
$story = textarea('给我讲个故事。');你也可以包含占位符文本、默认值和提示信息:
$story = textarea(
label: '给我讲个故事。',
placeholder: '这是一个关于...',
hint: '这将在你的个人资料上显示。'
);如果你需要输入一个值,你可以传递 required 参数:
$story = textarea(
label: '给我讲个故事。',
required: true
);如果你想自定义验证消息,你也可以传递一个字符串:
$story = textarea(
label: '给我讲个故事。',
required: '故事是必填项。'
);最后,如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$story = textarea(
label: '给我讲个故事。',
validate: fn (string $value) => match (true) {
strlen($value) < 250 => '故事必须至少包含 250 个字符。',
strlen($value) > 10000 => '故事不能超过 10,000 个字符。',
default => null
}
);该闭包将接收已输入的值,并可返回错误消息,如果验证通过则返回 null。
或者,你可以利用 Laravel 验证器的强大功能。为此,请向 validate 参数提供一个包含属性名称和所需验证规则的数组:
$story = textarea(
label: '给我讲个故事。',
validate: ['story' => 'required|max:10000']
);password 函数类似于 text 函数,但用户的输入在控制台中会被遮盖。这在请求密码等敏感信息时很有用:
use function Laravel\Prompts\password;
$password = password('你的密码是什么?');你也可以包含占位符文本和提示信息:
$password = password(
label: '你的密码是什么?',
placeholder: '密码',
hint: '最少 8 个字符。'
);如果你需要输入一个值,你可以传递 required 参数:
$password = password(
label: '你的密码是什么?',
required: true
);如果你想自定义验证消息,你也可以传递一个字符串:
$password = password(
label: '你的密码是什么?',
required: '密码是必填项。'
);最后,如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$password = password(
label: '你的密码是什么?',
validate: fn (string $value) => match (true) {
strlen($value) < 8 => '密码必须至少包含 8 个字符。',
default => null
}
);该闭包将接收已输入的值,并可返回错误消息,如果验证通过则返回 null。
或者,你可以利用 Laravel 验证器的强大功能。为此,请向 validate 参数提供一个包含属性名称和所需验证规则的数组:
$password = password(
label: '你的密码是什么?',
validate: ['password' => 'min:8']
);如果你需要向用户请求“是或否”的确认,你可以使用 confirm 函数。用户可以使用箭头键或按 y 或 n 来选择他们的响应。此函数将返回 true 或 false。
use function Laravel\Prompts\confirm;
$confirmed = confirm('你接受条款吗?');你也可以包含默认值、自定义的“是”和“否”标签措辞以及提示信息:
$confirmed = confirm(
label: '你接受条款吗?',
default: false,
yes: '我接受',
no: '我拒绝',
hint: '必须接受条款才能继续。'
);如有必要,你可以通过传递 required 参数来要求用户选择“是”:
$confirmed = confirm(
label: '你接受条款吗?',
required: true
);如果你想自定义验证消息,你也可以传递一个字符串:
$confirmed = confirm(
label: '你接受条款吗?',
required: '你必须接受条款才能继续。'
);如果你需要用户从预定义的一组选项中进行选择,你可以使用 select 函数:
use function Laravel\Prompts\select;
$role = select(
label: '用户应该拥有什么角色?',
options: ['成员', '贡献者', '所有者']
);你也可以指定默认选项和提示信息:
$role = select(
label: '用户应该拥有什么角色?',
options: ['成员', '贡献者', '所有者'],
default: '所有者',
hint: '角色可以随时更改。'
);你也可以向 options 参数传递一个关联数组,以返回所选键而不是其值:
$role = select(
label: '用户应该拥有什么角色?',
options: [
'member' => '成员',
'contributor' => '贡献者',
'owner' => '所有者',
],
default: 'owner'
);列表开始滚动前最多显示五个选项。你可以通过传递 scroll 参数来自定义:
$role = select(
label: '你想分配哪个类别?',
options: Category::pluck('name', 'id'),
scroll: 10
);与其他提示函数不同,select 函数不接受 required 参数,因为它不可能不选择任何内容。但是,如果你需要提供一个选项但阻止它被选中,你可以向 validate 参数传递一个闭包:
$role = select(
label: '用户应该拥有什么角色?',
options: [
'member' => '成员',
'contributor' => '贡献者',
'owner' => '所有者',
],
validate: fn (string $value) =>
$value === 'owner' && User::where('role', 'owner')->exists()
? '所有者已存在。'
: null
);如果 options 参数是关联数组,则闭包将接收所选键;否则,它将接收所选值。闭包可以返回错误消息,如果验证通过则返回 null。
如果你需要用户能够选择多个选项,你可以使用 multiselect 函数:
use function Laravel\Prompts\multiselect;
$permissions = multiselect(
label: '应该分配哪些权限?',
options: ['读取', '创建', '更新', '删除']
);你也可以指定默认选项和提示信息:
use function Laravel\Prompts\multiselect;
$permissions = multiselect(
label: '应该分配哪些权限?',
options: ['读取', '创建', '更新', '删除'],
default: ['读取', '创建'],
hint: '权限可以随时更新。'
);你也可以向 options 参数传递一个关联数组,以返回所选选项的键而不是其值:
$permissions = multiselect(
label: '应该分配哪些权限?',
options: [
'read' => '读取',
'create' => '创建',
'update' => '更新',
'delete' => '删除',
],
default: ['read', 'create']
);列表开始滚动前最多显示五个选项。你可以通过传递 scroll 参数来自定义:
$categories = multiselect(
label: '应该分配哪些类别?',
options: Category::pluck('name', 'id'),
scroll: 10
);默认情况下,用户可以选择零个或多个选项。你可以传递 required 参数来强制选择一个或多个选项:
$categories = multiselect(
label: '应该分配哪些类别?',
options: Category::pluck('name', 'id'),
required: true
);如果你想自定义验证消息,你可以向 required 参数提供一个字符串:
$categories = multiselect(
label: '应该分配哪些类别?',
options: Category::pluck('name', 'id'),
required: '你必须至少选择一个类别'
);如果你需要提供一个选项但阻止它被选中,你可以向 validate 参数传递一个闭包:
$permissions = multiselect(
label: '用户应该拥有哪些权限?',
options: [
'read' => '读取',
'create' => '创建',
'update' => '更新',
'delete' => '删除',
],
validate: fn (array $values) => ! in_array('read', $values)
? '所有用户都需要读取权限。'
: null
);如果 options 参数是关联数组,则闭包将接收所选键;否则,它将接收所选值。闭包可以返回错误消息,如果验证通过则返回 null。
suggest 函数可用于为可能的选项提供自动补全。用户仍然可以提供任何答案,无论自动补全提示如何:
use function Laravel\Prompts\suggest;
$name = suggest('你的名字是什么?', ['Taylor', 'Dayle']);或者,你可以将闭包作为第二个参数传递给 suggest 函数。每次用户输入字符时都会调用该闭包。闭包应接受一个包含用户目前输入的字符串参数,并返回一个用于自动补全的选项数组:
$name = suggest(
label: '你的名字是什么?',
options: fn ($value) => collect(['Taylor', 'Dayle'])
->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
)你也可以包含占位符文本、默认值和提示信息:
$name = suggest(
label: '你的名字是什么?',
options: ['Taylor', 'Dayle'],
placeholder: '例如:Taylor',
default: $user?->name,
hint: '这将在你的个人资料上显示。'
);如果你需要输入一个值,你可以传递 required 参数:
$name = suggest(
label: '你的名字是什么?',
options: ['Taylor', 'Dayle'],
required: true
);如果你想自定义验证消息,你也可以传递一个字符串:
$name = suggest(
label: '你的名字是什么?',
options: ['Taylor', 'Dayle'],
required: '你的名字是必填项。'
);最后,如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$name = suggest(
label: '你的名字是什么?',
options: ['Taylor', 'Dayle'],
validate: fn (string $value) => match (true) {
strlen($value) < 3 => '姓名必须至少包含 3 个字符。',
strlen($value) > 255 => '姓名不能超过 255 个字符。',
default => null
}
);该闭包将接收已输入的值,并可返回错误消息,如果验证通过则返回 null。
或者,你可以利用 Laravel 验证器的强大功能。为此,请向 validate 参数提供一个包含属性名称和所需验证规则的数组:
$name = suggest(
label: '你的名字是什么?',
options: ['Taylor', 'Dayle'],
validate: ['name' => 'required|min:3|max:255']
);如果你的用户有很多选项可供选择,search 函数允许用户输入搜索查询以筛选结果,然后使用箭头键选择一个选项:
use function Laravel\Prompts\search;
$id = search(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: []
);该闭包将接收用户目前输入的文本,并且必须返回一个选项数组。如果返回关联数组,则将返回所选选项的键,否则将返回其值。
当筛选你打算返回值的数组时,你应该使用 array_values 函数或 values 集合方法来确保数组不会变为关联数组:
$names = collect(['Taylor', 'Abigail']);
$selected = search(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => $names
->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
->values()
->all(),
);你也可以包含占位符文本和提示信息:
$id = search(
label: '搜索应该收到邮件的用户',
placeholder: '例如:Taylor Otwell',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
hint: '该用户将立即收到一封电子邮件。'
);列表开始滚动前最多显示五个选项。你可以通过传递 scroll 参数来自定义:
$id = search(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
scroll: 10
);如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$id = search(
label: '搜索应该收到邮件的用户',
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 '该用户已选择不接收邮件。';
}
}
);如果 options 闭包返回关联数组,则闭包将接收所选键;否则,它将接收所选值。闭包可以返回错误消息,如果验证通过则返回 null。
如果你有很多可搜索的选项,并且需要用户能够选择多个项目,multisearch 函数允许用户输入搜索查询以筛选结果,然后使用箭头键和空格键选择选项:
use function Laravel\Prompts\multisearch;
$ids = multisearch(
'搜索应该收到邮件的用户',
fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: []
);该闭包将接收用户目前输入的文本,并且必须返回一个选项数组。如果返回关联数组,则将返回所选选项的键;否则,将返回其值。
当筛选你打算返回值的数组时,你应该使用 array_values 函数或 values 集合方法来确保数组不会变为关联数组:
$names = collect(['Taylor', 'Abigail']);
$selected = multisearch(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => $names
->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
->values()
->all(),
);你也可以包含占位符文本和提示信息:
$ids = multisearch(
label: '搜索应该收到邮件的用户',
placeholder: '例如:Taylor Otwell',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
hint: '该用户将立即收到一封电子邮件。'
);列表开始滚动前最多显示五个选项。你可以通过提供 scroll 参数来自定义:
$ids = multisearch(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
scroll: 10
);默认情况下,用户可以选择零个或多个选项。你可以传递 required 参数来强制选择一个或多个选项:
$ids = multisearch(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
required: true
);如果你想自定义验证消息,你也可以向 required 参数提供一个字符串:
$ids = multisearch(
label: '搜索应该收到邮件的用户',
options: fn (string $value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: [],
required: '你必须至少选择一个用户。'
);如果你想执行额外的验证逻辑,你可以向 validate 参数传递一个闭包:
$ids = multisearch(
label: '搜索应该收到邮件的用户',
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 ').' 已选择不接收邮件。';
}
}
);如果 options 闭包返回关联数组,则闭包将接收所选键;否则,它将接收所选值。闭包可以返回错误消息,如果验证通过则返回 null。
pause 函数可用于向用户显示提示信息,并等待他们按下回车/返回键确认继续:
use function Laravel\Prompts\pause;
pause('按 ENTER 键继续。');有时你可能希望在验证发生之前转换提示输入。例如,你可能希望从提供的任何字符串中删除空格。为了实现这一点,许多提示函数都提供了一个 transform 参数,它接受一个闭包:
$name = text(
label: '你的名字是什么?',
transform: fn (string $value) => trim($value),
validate: fn (string $value) => match (true) {
strlen($value) < 3 => '姓名必须至少包含 3 个字符。',
strlen($value) > 255 => '姓名不能超过 255 个字符。',
default => null
}
);通常,你会有多个提示按顺序显示,以收集信息,然后执行额外的操作。你可以使用 form 函数为用户创建一组分组的提示,供其完成:
use function Laravel\Prompts\form;
$responses = form()
->text('你的名字是什么?', required: true)
->password('你的密码是什么?', validate: ['password' => 'min:8'])
->confirm('你接受条款吗?')
->submit();submit 方法将返回一个包含表单所有提示响应的数字索引数组。但是,你可以通过 name 参数为每个提示提供一个名称。提供名称后,可以通过该名称访问命名提示的响应:
use App\Models\User;
use function Laravel\Prompts\form;
$responses = form()
->text('你的名字是什么?', required: true, name: 'name')
->password(
label: '你的密码是什么?',
validate: ['password' => 'min:8'],
name: 'password'
)
->confirm('你接受条款吗?')
->submit();
User::create([
'name' => $responses['name'],
'password' => $responses['password'],
]);使用 form 函数的主要好处是用户可以使用 CTRL + U 返回表单中的上一个提示。这允许用户纠正错误或更改选择,而无需取消并重新启动整个表单。
如果你需要对表单中的提示进行更精细的控制,你可以调用 add 方法而不是直接调用其中一个提示函数。add 方法将接收用户提供的所有以前的响应:
use function Laravel\Prompts\form;
use function Laravel\Prompts\outro;
use function Laravel\Prompts\text;
$responses = form()
->text('你的名字是什么?', required: true, name: 'name')
->add(function ($responses) {
return text("你多大了,{$responses['name']}?");
}, name: 'age')
->submit();
outro("你的名字是 {$responses['name']},你 {$responses['age']} 岁。");note、info、warning、error 和 alert 函数可用于显示信息消息:
use function Laravel\Prompts\info;
info('软件包安装成功。');table 函数使得显示多行多列数据变得容易。你只需要提供列名和表格数据即可:
use function Laravel\Prompts\table;
table(
headers: ['姓名', '电子邮件'],
rows: User::all(['name', 'email'])->toArray()
);spin 函数在执行指定的回调时显示一个旋转器和可选消息。它用于指示正在进行的过程,并在完成后返回回调的结果:
use function Laravel\Prompts\spin;
$response = spin(
callback: fn () => Http::get('http://example.com'),
message: '正在获取响应...'
);[!WARNING]
spin函数需要 PCNTL PHP 扩展来动画化旋转器。当此扩展不可用时,将显示一个静态版本的旋转器。
对于长时间运行的任务,显示进度条以告知用户任务完成度会很有帮助。使用 progress 函数,Laravel 将显示进度条并为给定可迭代值的每次迭代推进其进度:
use function Laravel\Prompts\progress;
$users = progress(
label: '正在更新用户',
steps: User::all(),
callback: fn ($user) => $this->performTask($user)
);progress 函数的作用类似于 map 函数,它将返回一个数组,其中包含回调每次迭代的返回值。
回调也可以接受 Laravel\Prompts\Progress 实例,允许你在每次迭代时修改标签和提示信息:
$users = progress(
label: '正在更新用户',
steps: User::all(),
callback: function ($user, $progress) {
$progress
->label("正在更新 {$user->name}")
->hint("创建于 {$user->created_at}");
return $this->performTask($user);
},
hint: '这可能需要一些时间。'
);有时,你可能需要更手动地控制进度条的推进方式。首先,定义进程将迭代的总步数。然后,在处理完每个项目后,通过 advance 方法推进进度条:
$progress = progress(label: '正在更新用户', 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。
[!NOTE]
在使用 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 : '必填项。'
);
}
if ($prompt->validate) {
$error = ($prompt->validate)($answer ?? '');
if ($error) {
throw new \RuntimeException($error);
}
}
return $answer;
});
return (new SymfonyStyle($input, $output))
->askQuestion($question);
});回退必须为每个提示类单独配置。闭包将接收提示类的实例,并且必须返回适用于该提示的适当类型。
Laravel 提供了多种方法来测试你的命令是否显示了预期的提示消息:
test('report generation', function () {
$this->artisan('report:generate')
->expectsPromptsInfo('欢迎使用该应用程序!')
->expectsPromptsWarning('此操作无法撤销')
->expectsPromptsError('发生了一些错误')
->expectsPromptsAlert('重要通知!')
->expectsPromptsIntro('正在启动进程...')
->expectsPromptsOutro('进程完成!')
->expectsPromptsTable(
headers: ['姓名', '电子邮件'],
rows: [
['Taylor Otwell', 'taylor@example.com'],
['Jason Beggs', 'jason@example.com'],
]
)
->assertExitCode(0);
});public function test_report_generation(): void
{
$this->artisan('report:generate')
->expectsPromptsInfo('欢迎使用该应用程序!')
->expectsPromptsWarning('此操作无法撤销')
->expectsPromptsError('发生了一些错误')
->expectsPromptsAlert('重要通知!')
->expectsPromptsIntro('正在启动进程...')
->expectsPromptsOutro('进程完成!')
->expectsPromptsTable(
headers: ['姓名', '电子邮件'],
rows: [
['Taylor Otwell', 'taylor@example.com'],
['Jason Beggs', 'jason@example.com'],
]
)
->assertExitCode(0);
}