Skip to content

提示

简介

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

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

NOTE

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

安装

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

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

shell
composer require laravel/prompts

可用的提示

文本

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

php
use function Laravel\Prompts\text;

$name = text('你的名字是什么?');

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

php
$name = text(
    label: '你的名字是什么?',
    placeholder: '例如 Taylor Otwell',
    default: $user?->name,
    hint: '这将显示在您的个人资料上。'
);

必需值

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

php
$name = text(
    label: '你的名字是什么?',
    required: true
);

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

php
$name = text(
    label: '你的名字是什么?',
    required: '需要您的名字。'
);

附加验证

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

php
$name = text(
    label: '你的名字是什么?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => '名字必须至少有 3 个字符。',
        strlen($value) > 255 => '名字不能超过 255 个字符。',
        default => null
    }
);

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

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

php
$name = text(
    label: '你的名字是什么?',
    validate: ['name' => 'required|max:255|unique:users']
);

文本域

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

php
use function Laravel\Prompts\textarea;

$story = textarea('告诉我一个故事。');

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

php
$story = textarea(
    label: '告诉我一个故事。',
    placeholder: '这是一个关于...的故事',
    hint: '这将显示在您的个人资料上。'
);

必需值

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

php
$story = textarea(
    label: '告诉我一个故事。',
    required: true
);

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

php
$story = textarea(
    label: '告诉我一个故事。',
    required: '需要一个故事。'
);

附加验证

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

php
$story = textarea(
    label: '告诉我一个故事。',
    validate: fn (string $value) => match (true) {
        strlen($value) < 250 => '故事必须至少有 250 个字符。',
        strlen($value) > 10000 => '故事不能超过 10,000 个字符。',
        default => null
    }
);

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

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

php
$story = textarea(
    label: '告诉我一个故事。',
    validate: ['story' => 'required|max:10000']
);

密码

password 函数类似于 text 函数,但用户在控制台中输入时,其输入将被掩码。这对于询问敏感信息(如密码)非常有用:

php
use function Laravel\Prompts\password;

$password = password('你的密码是什么?');

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

php
$password = password(
    label: '你的密码是什么?',
    placeholder: 'password',
    hint: '至少 8 个字符。'
);

必需值

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

php
$password = password(
    label: '你的密码是什么?',
    required: true
);

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

php
$password = password(
    label: '你的密码是什么?',
    required: '需要密码。'
);

附加验证

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

php
$password = password(
    label: '你的密码是什么?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 8 => '密码必须至少有 8 个字符。',
        default => null
    }
);

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

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

php
$password = password(
    label: '你的密码是什么?',
    validate: ['password' => 'min:8']
);

确认

如果您需要询问用户是否接受某些条件,您可以使用 confirm 函数。用户可以使用箭头键或按 yn 来选择他们的回答。此函数将返回 truefalse

php
use function Laravel\Prompts\confirm;

$confirmed = confirm('你接受这些条件吗?');

您还可以包括默认值、自定义 "是" 和 "否" 标签以及信息性提示:

php
$confirmed = confirm(
    label: '你接受这些条件吗?',
    default: false,
    yes: '我接受',
    no: '我拒绝',
    hint: '必须接受条件才能继续。'
);

要求 "是"

如果需要,您可以要求用户选择 "是" 方案,方法是传递 required 参数:

php
$confirmed = confirm(
    label: '你接受这些条件吗?',
    required: true
);

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

php
$confirmed = confirm(
    label: '你接受这些条件吗?',
    required: '您必须接受这些条件才能继续。'
);

选择

如果您需要用户从预定义的选项集中选择,您可以使用 select 函数:

php
use function Laravel\Prompts\select;

$role = select(
    label: '用户应该有什么角色?',
    options: ['成员', '贡献者', '所有者']
);

您还可以指定默认选择和信息性提示:

php
$role = select(
    label: '用户应该有什么角色?',
    options: ['成员', '贡献者', '所有者'],
    default: '所有者',
    hint: '角色可以随时更改。'
);

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

php
$role = select(
    label: '用户应该有什么角色?',
    options: [
        'member' => '成员',
        'contributor' => '贡献者',
        'owner' => '所有者',
    ],
    default: 'owner'
);

最多会显示五个选项,然后列表开始滚动。您可以通过传递 scroll 参数来自定义此设置:

php
$role = select(
    label: '您想要分配哪个类别?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);

附加验证

与其他提示函数不同,select 函数不接受 required 参数,因为不可能选择 "无"。但是,如果您需要呈现一个选项但阻止用户选择它,您可以将闭包传递给 validate 参数:

php
$role = select(
    label: '用户应该有什么角色?',
    options: [
        'member' => '成员',
        'contributor' => '贡献者',
        'owner' => '所有者',
    ],
    validate: fn (string $value) =>
        $value === 'owner' && User::where('role', 'owner')->exists()
            ? '已经存在一个所有者。'
            : null
);

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

多选

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

php
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: '应该分配哪些权限?',
    options: ['读取', '创建', '更新', '删除']
);

您还可以指定默认选择和信息性提示:

php
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: '应该分配哪些权限?',
    options: ['读取', '创建', '更新', '删除'],
    default: ['读取', '创建'],
    hint: '权限可以随时更新。'
);

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

php
$permissions = multiselect(
    label: '应该分配哪些权限?',
    options: [
        'read' => '读取',
        'create' => '创建',
        'update' => '更新',
        'delete' => '删除',
    ],
    default: ['read', 'create']
);

最多会显示五个选项,然后列表开始滚动。您可以通过传递 scroll 参数来自定义此设置:

php
$categories = multiselect(
    label: '应该分配哪些类别?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);

要求值

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

php
$categories = multiselect(
    label: '应该分配哪些类别?',
    options: Category::pluck('name', 'id'),
    required: true
);

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

php
$categories = multiselect(
    label: '应该分配哪些类别?',
    options: Category::pluck('name', 'id'),
    required: '您必须选择至少一个类别'
);

附加验证

如果您需要呈现一个选项但阻止用户选择它,您可以将闭包传递给 validate 参数:

php
$permissions = multiselect(
    label: '用户应该有哪些权限?',
    options: [
        'read' => '读取',
        'create' => '创建',
        'update' => '更新',
        'delete' => '删除',
    ],
    validate: fn (array $values) => ! in_array('read', $values)
        ? '所有用户都需要读取权限。'
        : null
);

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

建议

suggest 函数可用于为可能的选择提供自动完成。用户仍然可以提供任何答案,而不管自动完成提示如何:

php
use function Laravel\Prompts\suggest;

$name = suggest('你的名字是什么?', ['Taylor', 'Dayle']);

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

php
$name = suggest(
    label: '你的名字是什么?',
    options: fn ($value) => collect(['Taylor', 'Dayle'])
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
)

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

php
$name = suggest(
    label: '你的名字是什么?',
    options: ['Taylor', 'Dayle'],
    placeholder: '例如 Taylor',
    default: $user?->name,
    hint: '这将显示在您的个人资料上。'
);

必需值

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

php
$name = suggest(
    label: '你的名字是什么?',
    options: ['Taylor', 'Dayle'],
    required: true
);

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

php
$name = suggest(
    label: '你的名字是什么?',
    options: ['Taylor', 'Dayle'],
    required: '需要您的名字。'
);

附加验证

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

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

php
$name = suggest(
    label: '你的名字是什么?',
    options: ['Taylor', 'Dayle'],
    validate: ['name' => 'required|min:3|max:255']
);

搜索

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

php
use function Laravel\Prompts\search;

$id = search(
    label: '搜索应该接收邮件的用户',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);

闭包将接收用户到目前为止输入的文本,并必须返回一个选项数组。如果您返回一个关联数组,则所选选项的键将被返回,否则将返回其值。

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

php
$id = search(
    label: '搜索应该接收邮件的用户',
    placeholder: '例如 Taylor Otwell',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    hint: '该用户将立即收到一封电子邮件。'
);

最多会显示五个选项,然后列表开始滚动。您可以通过传递 scroll 参数来自定义此设置:

php
$id = search(
    label: '搜索应该接收邮件的用户',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    scroll: 10
);

附加验证

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

php
$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 函数允许用户在使用箭头键和空格键选择选项之前键入搜索查询来过滤结果:

php
use function Laravel\Prompts\multisearch;

$ids = multisearch(
    '搜索应该接收邮件的用户',
    fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);

闭包将接收用户到目前为止输入的文本,并必须返回一个选项数组。如果您返回一个关联数组,则所选选项的键将被返回,否则将返回其值。

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

php
$ids = multisearch(
    label: '搜索应该接收邮件的用户',
    placeholder: '例如 Taylor Otwell',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    hint: '该用户将立即收到一封电子邮件。'
);

最多会显示五个选项,然后列表开始滚动。您可以通过提供 scroll 参数来自定义此设置:

php
$ids = multisearch(
    label: '搜索应该接收邮件的用户',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    scroll: 10
);

要求值

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

php
$ids = multisearch(
    label: '搜索应该接收邮件的用户',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    required: true
);

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

php
$ids = multisearch(
    label: '搜索应该接收邮件的用户',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : [],
    required: '您必须选择至少一个用户。'
);

附加验证

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

php
$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 函数可用于向用户显示信息性文本,并等待他们按 Enter / Return 键确认他们希望继续:

php
use function Laravel\Prompts\pause;

pause('按 ENTER 键继续。');

在验证前转换输入

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

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

php
use function Laravel\Prompts\form;

$responses = form()
    ->text('你的名字是什么?', required: true)
    ->password('你的密码是什么?', validate: ['password' => 'min:8'])
    ->confirm('你接受这些条件吗?')
    ->submit();

submit 方法将返回一个按数字索引的数组,包含表单提示的所有响应。但是,您可以为每个提示提供一个名称,通过 name 参数。当提供名称时,可以通过该名称访问命名提示的响应:

php
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 方法传递了用户到目前为止提供的所有响应:

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

$responses = form()
    ->text('你的名字是什么?', required: true, name: 'name')
    ->add(function ($responses) {
        return text("你多大了, {$responses['name']}?");
    }, name: 'age')
    ->submit();

outro("你的名字是 {$responses['name']}, 你 {$responses['age']} 岁。");

信息性消息

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 函数需要 PHP 的 pcntl 扩展才能动画旋转器。当此扩展不可用时,将显示旋转器的静态版本。

进度条

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

php
use function Laravel\Prompts\progress;

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

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

回调还可以接受 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();

终端注意事项

终端宽度

如果任何标签、选项或验证消息的长度超过用户终端的 "列" 数,它将被自动截断以适应。如果您的用户可能使用较窄的终端,请尽量将这些字符串的长度最小化。典型的安全最大长度是 74 个字符,以支持 80 个字符的终端。

终端高度

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

不支持的环境和回退

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

因此,Laravel Prompts 支持回退到另一种实现,例如 Symfony Console Question Helper

NOTE

当您在 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);
});

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