Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

提示 ​

简介 ​

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

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

注意

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

安装 ​

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

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

shell
composer require laravel/prompts

可用提示 ​

文本 ​

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

php
use function Laravel\Prompts\text;

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

你也可以包含占位符文本、默认值和信息性提示:

php
$name = text(
    label: '你叫什么名字?',
    placeholder: '例如 张三',
    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']
);

数字 ​

number 函数将用给定的问题提示用户,接受他们的数字输入,然后返回。number 函数允许用户使用上下箭头键来操纵数字:

php
use function Laravel\Prompts\number;

$number = number('你想要多少份?');

你也可以包含占位符文本、默认值和信息性提示:

php
$name = number(
    label: '你想要多少份?',
    placeholder: '5',
    default: 1,
    hint: '这将决定要创建多少份。'
);

必需值 ​

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

php
$copies = number(
    label: '你想要多少份?',
    required: true
);

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

php
$copies = number(
    label: '你想要多少份?',
    required: '需要输入份数。'
);

额外验证 ​

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

php
$copies = number(
    label: '你想要多少份?',
    validate: fn (?int $value) => match (true) {
        $value < 1 => '至少需要一份。',
        $value > 100 => '你不能创建超过 100 份。',
        default => null
    }
);

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

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

php
$copies = number(
    label: '你想要多少份?',
    validate: ['copies' => 'required|integer|min:1|max:100']
);

密码 ​

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

php
use function Laravel\Prompts\password;

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

你也可以包含占位符文本和信息性提示:

php
$password = password(
    label: '你的密码是什么?',
    placeholder: '密码',
    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 函数。用户可以使用箭头键或按 y 或 n 来选择他们的回应。该函数将返回 true 或 false。

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

辅助信息 ​

info 参数可用于显示关于当前高亮选项的额外信息。当提供一个闭包时,它将接收当前高亮选项的值,并应返回一个字符串或 null:

php
$role = select(
    label: '该用户应拥有什么角色?',
    options: [
        'member' => '成员',
        'contributor' => '贡献者',
        'owner' => '所有者',
    ],
    info: fn (string $value) => match ($value) {
        'member' => '可以查看和评论。',
        'contributor' => '可以查看、评论和编辑。',
        'owner' => '对所有资源的完全访问权限。',
        default => null,
    }
);

如果信息不依赖于高亮的选项,你也可以向 info 参数传递一个静态字符串:

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

额外验证 ​

与其他提示函数不同,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
);

辅助信息 ​

info 参数可用于显示关于当前高亮选项的额外信息。当提供一个闭包时,它将接收当前高亮选项的值,并应返回一个字符串或 null:

php
$permissions = multiselect(
    label: '应该分配哪些权限?',
    options: [
        'read' => '读取',
        'create' => '创建',
        'update' => '更新',
        'delete' => '删除',
    ],
    info: fn (string $value) => match ($value) {
        'read' => '查看资源及其属性。',
        'create' => '创建新资源。',
        'update' => '修改现有资源。',
        'delete' => '永久移除资源。',
        default => null,
    }
);

必需值 ​

默认情况下,用户可以选择零个或多个选项。你可以传递 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('你叫什么名字?', ['张三', '李四']);

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

php
$name = suggest(
    label: '你叫什么名字?',
    options: fn ($value) => collect(['张三', '李四'])
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
);

你也可以包含占位符文本、默认值和信息性提示:

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['张三', '李四'],
    placeholder: '例如 张三',
    default: $user?->name,
    hint: '这将显示在你的个人资料中。'
);

辅助信息 ​

info 参数可用于显示关于当前高亮选项的额外信息。当提供一个闭包时,它将接收当前高亮选项的值,并应返回一个字符串或 null:

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle'],
    info: fn (string $value) => match ($value) {
        'Taylor' => '管理员',
        'Dayle' => '贡献者',
        default => null,
    }
);

必需值 ​

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

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['张三', '李四'],
    required: true
);

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

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['张三', '李四'],
    required: '姓名是必需的。'
);

额外验证 ​

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

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['张三', '李四'],
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => '姓名必须至少 3 个字符。',
        strlen($value) > 255 => '姓名不能超过 255 个字符。',
        default => null
    }
);

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

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

php
$name = suggest(
    label: '你叫什么名字?',
    options: ['张三', '李四'],
    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()
        : []
);

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

在筛选数组时,如果你打算返回值,应使用 array_values 函数或 values Collection 方法,以确保数组不会变成关联数组:

php
$names = collect(['张三', '李四']);

$selected = search(
    label: '搜索应接收邮件的用户',
    options: fn (string $value) => $names
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
        ->values()
        ->all(),
);

你也可以包含占位符文本和信息性提示:

php
$id = search(
    label: '搜索应接收邮件的用户',
    placeholder: '例如 张三',
    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
);

辅助信息 ​

info 参数可用于显示关于当前高亮选项的额外信息。当提供一个闭包时,它将接收当前高亮选项的值,并应返回一个字符串或 null:

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

额外验证 ​

如果你想执行额外的验证逻辑,可以传递一个闭包给 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()
        : []
);

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

在筛选数组时,如果你打算返回值,应使用 array_values 函数或 values Collection 方法,以确保数组不会变成关联数组:

php
$names = collect(['张三', '李四']);

$selected = multisearch(
    label: '搜索应接收邮件的用户',
    options: fn (string $value) => $names
        ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
        ->values()
        ->all(),
);

你也可以包含占位符文本和信息性提示:

php
$ids = multisearch(
    label: '搜索应接收邮件的用户',
    placeholder: '例如 张三',
    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
);

辅助信息 ​

info 参数可用于显示关于当前高亮选项的额外信息。当提供一个闭包时,它将接收当前高亮选项的值,并应返回一个字符串或 null:

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

必需值 ​

默认情况下,用户可以选择零个或多个选项。你可以传递 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 函数可用于向用户显示信息性文本,并等待他们按回车键确认继续的意愿:

php
use function Laravel\Prompts\pause;

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

自动完成 ​

autocomplete 函数可用于为可能的选项提供内联自动完成。当用户输入时,匹配其输入的建议将作为幽灵文本显示,可以通过按 Tab 或右箭头键接受:

php
use function Laravel\Prompts\autocomplete;

$name = autocomplete(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim']
);

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

php
$name = autocomplete(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    placeholder: '例如 Taylor',
    default: $user?->name,
    hint: '使用 Tab 接受,上下键循环选择。'
);

动态选项 ​

您也可以传递一个闭包,根据用户的输入动态生成选项。每次用户输入一个字符时都会调用该闭包,并应返回一个用于自动完成的选项数组:

php
$file = autocomplete(
    label: '哪个文件?',
    options: fn (string $value) => collect($files)
        ->filter(fn ($file) => str_starts_with(strtolower($file), strtolower($value)))
        ->values()
        ->all(),
);

必需值 ​

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

php
$name = autocomplete(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    required: true
);

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

php
$name = autocomplete(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    required: '姓名是必填项。'
);

额外验证 ​

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

php
$name = autocomplete(
    label: '你叫什么名字?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => '姓名必须至少包含 3 个字符。',
        strlen($value) > 255 => '姓名不能超过 255 个字符。',
        default => null
    }
);

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

验证前转换输入 ​

有时你可能希望在验证发生之前转换提示输入。例如,你可能希望从任何提供的字符串中删除空格。为了实现这一点,许多提示函数提供了一个 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;
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 函数可用于显示信息性消息:

php
use function Laravel\Prompts\info;

info('软件包安装成功。');

标注 ​

callout 函数用于显示一个带有标签和内容的框式消息。标注非常适合展示需要突出的重要信息,例如部署摘要、错误详情或状态更新:

php
use function Laravel\Prompts\callout;

callout(
    label: '环境已配置',
    content: '您的应用程序正在生产模式下运行,共 4 个工作进程。',
);

你可以传入 warning 或 error 作为 type 参数,来改变标注的视觉样式:

php
callout(
    label: '弃用通知',
    content: '`--prefer-stable` 标志将在 v4.0 中移除。请改用 `--stability=stable`。',
    type: 'warning',
);

callout(
    label: '数据库连接失败',
    content: '无法连接到 127.0.0.1:3306 上的 MySQL。',
    type: 'error',
);

info 参数会在标注底部添加一行附加信息,非常适合展示元数据,如 ID 或时间戳:

php
callout(
    label: '部署摘要',
    content: '您的应用程序已部署到生产环境。',
    info: 'deploy-id: d4f8a2c',
);

富文本内容 ​

您可以传递一个由字符串和元素组成的数组来构建丰富的、结构化的标注,而无需传递字符串。Element 类提供了用于创建标题、项目符号列表、编号列表、键值列表和链接的工厂方法:

php
use Laravel\Prompts\Elements\Element;

use function Laravel\Prompts\callout;

callout('部署摘要', [
    '您的应用程序已于 2024-03-15 14:32 UTC 部署到生产环境。',
    Element::heading('变更内容'),
    Element::bulletedList([
        '执行了 3 个待处理的数据库迁移',
        '清理并重建了路由缓存',
        '重启了 4 个队列工作进程',
    ]),
    Element::heading('后续步骤'),
    Element::numberedList([
        '验证 /up 健康检查端点',
        '监控接下来 15 分钟的错误率',
        '确认后台任务正在处理中',
    ]),
]);

你也可以使用 Element::keyValueList 来展示带标签的数据:

php
callout('数据库连接失败', [
    '无法连接到数据库服务器。',
    Element::keyValueList([
        '主机' => '127.0.0.1',
        '端口' => '3306',
        '数据库' => 'forge',
        '状态' => '连接被拒绝',
    ]),
], type: 'error');

Element::link 方法会在支持 OSC 8 的终端中创建一个可点击的超链接。您可以仅提供一个 URL,或者提供一个带有自定义标签的 URL:

php
callout('Server Health Check', [
    'Multiple services are reporting degraded performance.',
    Element::heading('Affected Services'),
    'Look here: '.Element::link('https://example.com/health', 'Health Dashboard'),
    Element::link('https://example.com/health'),
]);

如果未提供标签,URL 本身将显示为链接文本。

表格 ​

table 函数使显示多行和多列数据变得容易。你只需要提供列名和数据表:

php
use function Laravel\Prompts\table;

table(
    headers: ['姓名', '邮箱'],
    rows: User::all(['name', 'email'])->toArray()
);

旋转器 ​

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

php
use function Laravel\Prompts\spin;

$response = spin(
    callback: fn () => Http::get('http://example.com'),
    message: '正在获取响应...'
);

警告

spin 函数需要 PCNTL PHP 扩展来动画化旋转器。当此扩展不可用时,将显示一个静态版本的旋转器。

进度条 ​

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

php
use function Laravel\Prompts\progress;

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

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

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

php
$users = progress(
    label: '正在更新用户',
    steps: User::all(),
    callback: function ($user, $progress) {
        $progress
            ->label("正在更新 {$user->name}")
            ->hint("创建于 {$user->created_at}");

        return $this->performTask($user);
    },
    hint: '这可能需要一些时间。'
);

有时,你可能需要更手动地控制进度条的推进方式。首先,定义过程将迭代的总步数。然后,在处理每个项目后通过 advance 方法推进进度条:

php
$progress = progress(label: '正在更新用户', steps: 10);

$users = User::all();

$progress->start();

foreach ($users as $user) {
    $this->performTask($user);

    $progress->advance();
}

$progress->finish();

任务 ​

task 函数在执行给定回调时,会显示一个带有标签、旋转器和滚动实时输出区域的任务。它非常适合包装长时间运行的进程,例如依赖安装或部署脚本,提供正在发生的事情的实时可见性:

php
use function Laravel\Prompts\task;

task(
    label: '正在安装依赖',
    callback: function ($logger) {
        // 长时间运行的进程...
    }
);

回调接收一个 Logger 实例,您可以使用它在任务的输出区域中显示日志行、状态消息和流式文本。

警告

task 函数需要 PCNTL PHP 扩展来运行动画旋转器。当此扩展不可用时,将显示任务的静态版本。

记录日志行 ​

line 方法向任务的滚动输出区域写入单行日志:

php
task(
    label: '正在安装依赖',
    callback: function ($logger) {
        $logger->line('正在解析包...');
        // ...
        $logger->line('正在下载 laravel/framework');
        // ...
    }
);

状态消息 ​

您可以使用 success、warning 和 error 方法来显示状态消息。它们将作为稳定、高亮的消息显示在滚动日志区域上方:

php
task(
    label: '正在部署应用',
    callback: function ($logger) {
        $logger->line('正在拉取最新更改...');
        // ...
        $logger->success('更改已拉取!');

        $logger->line('正在运行迁移...');
        // ...
        $logger->warning('没有新的迁移可运行。');

        $logger->line('正在清除缓存...');
        // ...
        $logger->success('缓存已清除!');
    }
);

更新标签 ​

label 方法允许您在任务运行时更新任务的标签:

php
task(
    label: '开始部署...',
    callback: function ($logger) {
        $logger->label('正在拉取最新更改...');
        // ...
        $logger->label('正在运行迁移...');
        // ...
        $logger->label('正在清除缓存...');
        // ...
    }
);

显示子标签 ​

subLabel 方法会在任务主标签下方显示一行暗淡的文本,适用于传达临时状态,例如当前正在进行的步骤。传入一个空字符串即可清除子标签:

php
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->subLabel('Building assets...');
        // ...
        $logger->subLabel('Running migrations...');
        // ...
        $logger->subLabel('');
    }
);

你还可以通过 subLabel 参数提供一个初始子标签:

php
task(
    label: 'Deploying',
    callback: function ($logger) {
        // ...
    },
    subLabel: 'Preparing...'
);

流式文本 ​

对于增量产生输出的进程,例如 AI 生成的响应,partial 方法允许您逐词或逐块地流式传输文本。流完成后,调用 commitPartial 以完成输出:

php
task(
    label: '正在生成响应...',
    callback: function ($logger) {
        foreach ($words as $word) {
            $logger->partial($word . ' ');
        }

        $logger->commitPartial();
    }
);

自定义输出限制 ​

默认情况下,任务最多显示 10 行滚动输出。您可以通过 limit 参数自定义此值:

php
task(
    label: '正在安装依赖',
    callback: function ($logger) {
        // ...
    },
    limit: 20
);

保留摘要 ​

默认情况下,任务输出在回调函数执行完毕即被清除。如果你希望在任务完成后仍将状态信息保留在屏幕上,可以传入 keepSummary 参数:

php
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->success('Assets built');
        // ...
        $logger->success('Migrations complete');
    },
    keepSummary: true,
);

流 ​

stream 函数显示流式传输到终端的文本,非常适合显示 AI 生成的内容或任何增量到达的文本:

php
use function Laravel\Prompts\stream;

$stream = stream();

foreach ($words as $word) {
    $stream->append($word . ' ');
    usleep(25_000); // 模拟块之间的延迟...
}

$stream->close();

append 方法向流中添加文本,并呈现逐渐淡入的效果。当所有内容流式传输完成后,调用 close 方法以完成输出并恢复光标。

终端标题 ​

title 函数更新用户终端窗口或标签页的标题:

php
use function Laravel\Prompts\title;

title('正在安装依赖');

要将终端标题重置为默认值,传递一个空字符串:

php
title('');

清空终端 ​

clear 函数可用于清空用户的终端:

php
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。

注意

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

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

测试 ​

Laravel 提供了多种方法来测试你的命令是否显示了预期的提示消息:

php
test('报告生成', function () {
    $this->artisan('report:generate')
        ->expectsPromptsInfo('欢迎使用应用程序!')
        ->expectsPromptsWarning('此操作无法撤消')
        ->expectsPromptsError('出错了')
        ->expectsPromptsAlert('重要通知!')
        ->expectsPromptsIntro('正在启动进程...')
        ->expectsPromptsOutro('进程完成!')
        ->expectsPromptsTable(
            headers: ['姓名', '邮箱'],
            rows: [
                ['张三', 'zhangsan@example.com'],
                ['李四', 'lisi@example.com'],
            ]
        )
        ->assertExitCode(0);
});
php
public function test_report_generation(): void
{
    $this->artisan('report:generate')
        ->expectsPromptsInfo('欢迎使用应用程序!')
        ->expectsPromptsWarning('此操作无法撤消')
        ->expectsPromptsError('出错了')
        ->expectsPromptsAlert('重要通知!')
        ->expectsPromptsIntro('正在启动进程...')
        ->expectsPromptsOutro('进程完成!')
        ->expectsPromptsTable(
            headers: ['姓名', '邮箱'],
            rows: [
                ['张三', 'zhangsan@example.com'],
                ['李四', 'lisi@example.com'],
            ]
        )
        ->assertExitCode(0);
}