laravel
10. 软件包
提示

介绍

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

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

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

安装

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

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

composer require laravel/prompts

可用的提示

文本

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

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 函数。用户可以使用箭头键或按 yn 来选择他们的回答。此函数将返回 truefalse

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 === '所有者' && User::where('role', '所有者')->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('读取', $values)
       ? '所有用户都需要读取权限。'
        : null
);

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

建议

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

use function Laravel\Prompts\suggest;
 
$name = suggest('您的姓名是什么?', ['泰勒', '戴尔']);

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

$name = suggest(
    label: '您的姓名是什么?',
    options: fn ($value) => collect(['泰勒', '戴尔'])
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
)

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

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

要求值

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

$name = suggest(
    label: '您的姓名是什么?',
    options: ['泰勒', '戴尔'],
    required: true
);

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

$name = suggest(
    label: '您的姓名是什么?',
    options: ['泰勒', '戴尔'],
    required: '您的姓名是必填的。'
);

额外验证

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

$name = suggest(
    label: '您的姓名是什么?',
    options: ['泰勒', '戴尔'],
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => '姓名必须至少为 3 个字符。',
        strlen($value) > 255 => '姓名不得超过 255 个字符。',
        default => null
    }
);

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

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

$name = suggest(
    label: '您的姓名是什么?',
    options: ['泰勒', '戴尔'],
    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()
        : []
);

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

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

$id = search(
    label: '搜索应接收邮件的用户',
    placeholder: '例如 泰勒·奥特韦尔',
    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()
        : []
);

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

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

$ids = multisearch(
    label: '搜索应接收邮件的用户',
    placeholder: '例如 泰勒·奥特韦尔',
    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 函数可用于向用户显示信息文本,并等待他们通过按 Enter / Return 键确认是否继续:

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;
 
$responses = form()
    ->text('您的名字是什么?', required: true, name: 'name')
    ->add(function ($responses) {
        return text("您多大了,{$responses['name']}?");
    }, name: 'age')
    ->submit();
 
outro("您的名字是 {$responses['name']},您的年龄是 {$responses['age']} 岁。");

信息消息

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

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(
    message: '正在获取响应...',
    callback: fn () => Http::get('http://example.com')
);

[!WARNING]
spin 函数需要 pcntl PHP 扩展来使旋转器动起来。当此扩展不可用时,将显示一个静态版本的旋转器。

进度条

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

use function Laravel\Prompts\progress;
 
$users = progress(
    label: '正在更新用户',
    steps: User::all(),
    callback: fn ($user) => $this->performTask($user)
);

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

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

终端考虑因素

终端宽度

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

终端高度

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

不支持的环境和回退

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

因此,Laravel Prompts 支持回退到替代实现,例如 Symfony Console Question Helper (opens in a new tab)

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

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