Artisan Console

简介

Artisan 是 Laravel 自带的命令行接口, 它提供了相当多的命令来帮助你构建 Laravel 应用。 你可以通过 list 命令查看所有可用的 Artisan 命令:

  1. php artisan list

每个命令都包含了「帮助」界面,它会显示和概述命令的可用参数及选项。只需要在命令前加上 help 即可查看命令帮助界面:

  1. php artisan help migrate

Tinker 命令 (REPL)

所有 Laravel 应用都包含了 Tinker,一个由 PsySH 包提供支持的 REPL。

安装

所有 Laravel 应用都默认包含了 Tinker。不过,你也可以使用 Composer 在你需要时手动安装:

  1. composer require laravel/tinker

用法

Tinker 让你可用在命令行于你的整个 Laravel 应用进行交互。包括但不限于 Eloquent ORM、任务、事件等。通过运行 Artisan 命令 tinker 进入 Tinker 环境。

  1. php artisan tinker

你可以通过 vendor:publish 命令发布 Tinker 配置文件:

  1. php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"

注意:Dispatchable 类中的 dispatch 辅助函数和 dispatch 方法已被弃用以将任务添加至队列中。因此,当你使用 Tinker 时,请使用 Bus::dispatchQueue::push 来分发任务。

命令白名单

Tinker 采用白名单来确定允许哪些 Artisan 命令可以在 shell 中允许。默认情况下,你可以运行 clear-compilerdownenvinspiremigrateoptimizeup 命令。如果你想将命令添加到白名单,请将该命令添加到 tinker.php 配置文件中的 commands 数组:

  1. 'commands' => [
  2. // App\Console\Commands\ExampleCommand::class,
  3. ],

黑名单别名

大多数情况下,Tinker 会在你引入类时自动为其添加别名。然而,你可能不希望为某些类添加别名。你可以在 tinker.php 配置文件中的 dont_alias 数组里列举这些类来完成此操作:

  1. 'dont_alias' => [
  2. App\User::class,
  3. ],

编写命令

除 Artisan 提供的命令外,你也可以编写自己的自定义命令。命令在多数情况下位于 app/Console/Commands 目录中;不过,只要你的命令可以由 Composer 加载,你就可以自由选择自己的存储位置。

生成命令

你可以使用 Artisan 命令 make:command 来创建一个新的命令。make:command 命令会在 app/Console/Commands 目录中创建一个新的命令类。如果该目录不存在,它会在你第一次运行 make:command 命令时自动创建。生成的命令将包含所有命令中默认存在的属性和方法。

  1. php artisan make:command SendEmails

命令结构

创建一个新命令后,你应该先修改 signaturedescription 属性以便你在使用 Artisan 命令 list 时能够清楚知道该命令的用法。执行命令时会调用 handle 方法,你可以在此方法中放置命令逻辑。

Tip:为了实现更好的代码复用,请尽量让你的命令类保持轻量并且能够延迟到应用服务中完成。在下面的例子中,我们将注入一个服务类来完成发送邮件的重任。

来看一个简单的例子。在命令类的 handle 方法中,我们可以注入任何我们需要的依赖项。Laravel 服务容器 将会自动注入所有在构造函数中带类型约束的依赖。

  1. <?php
  2. namespace App\Console\Commands;
  3. use App\DripEmailer;
  4. use App\User;
  5. use Illuminate\Console\Command;
  6. class SendEmails extends Command
  7. {
  8. /**
  9. * 命令名称及签名
  10. *
  11. * @var string
  12. */
  13. protected $signature = 'email:send {user}';
  14. /**
  15. * 命令描述
  16. *
  17. * @var string
  18. */
  19. protected $description = 'Send drip e-mails to a user';
  20. /**
  21. * 创建命令
  22. *
  23. * @return void
  24. */
  25. public function __construct()
  26. {
  27. parent::__construct();
  28. }
  29. /**
  30. * 执行命令
  31. *
  32. * @param \App\DripEmailer $drip
  33. * @return mixed
  34. */
  35. public function handle(DripEmailer $drip)
  36. {
  37. $drip->send(User::find($this->argument('user')));
  38. }
  39. }

闭包命令

基于闭包的命令提供了一个用类代替定义控制台命令的方法。同理,闭包路由也是控制器的一种替代方法,而闭包命令可以说是命令类的替代方法。在 app/Console/Kernel.php 文件中的 commands 方法中,Laravel 加载了 routes/console.php 文件:

  1. /**
  2. * 注册闭包命令
  3. *
  4. * @return void
  5. */
  6. protected function commands()
  7. {
  8. require base_path('routes/console.php');
  9. }

虽然这个文件没有定义 HTTP 路由,但它将基于控制台的入口(路由)定义到了应用中。在这个文件中,你可以使用 Artisan::command 方法定义所有的闭包路由。command 方法接受两个参数:命令名称 和可调用的闭包(可以接受命令参数及选项):

  1. Artisan::command('build {project}', function ($project) {
  2. $this->info("Building {$project}!");
  3. });

闭包绑定了底层的命令实例,因此你可以访问能够在命令类中使用的所有辅助方法。

类型约束依赖

除了接受命令参数及选项外,命令闭包也可以使用类型约束从 服务容器 中解析其他的依赖关系:

  1. use App\DripEmailer;
  2. use App\User;
  3. Artisan::command('email:send {user}', function (DripEmailer $drip, $user) {
  4. $drip->send(User::find($user));
  5. });

闭包命令描述

在你定义一个闭包命令时,你可以通过 describe 方法来为命令添加描述。这个描述会在你执行 php artisan list 或者 php artisan help 命令时显示:

  1. Artisan::command('build {project}', function ($project) {
  2. $this->info("Building {$project}!");
  3. })->describe('Build the project');

定义输入期望

在编写控制台命令时,通常是通过参数和选项来收集用户输入的。Laravel 让你可用非常方便地在 signature 属性中定义你期望用户输入的内容。signature 属性允许使用单一且可读性高,类似路由的语法来定义命令的名称、参数和选项。

参数

所有用户提供的参数及选项都被包含在花括号中。在下面的例子中,这个命令定义了一个 必须 的参数:user

  1. /**
  2. * 命令行的名称及签名
  3. *
  4. * @var string
  5. */
  6. protected $signature = 'email:send {user}';

你也可以创建可选参数,并定义参数的默认值:

  1. // 可选参数...
  2. email:send {user?}
  3. // 带有默认值的可选参数...
  4. email:send {user=foo}

选项

选项类似于参数,是用户输入的另一种格式。在命令行输入选项时,它们以两个连字符()作为前缀。有两种类型的选项:接收值和不接收值。不接收值的选项就像是一个布尔值的「开关」。让我们看一下这种类型选项的例子:

  1. /**
  2. * 命令名称及签名
  3. *
  4. * @var string
  5. */
  6. protected $signature = 'email:send {user} {--queue}';

在该例子中,可以在调用 Artisan 命令时指定 —queue 开关。如果 —queue 开关被传递,该选项的值为 true,否则为 false

  1. php artisan email:send 1 --queue

带值的选项

接下来,让我们来看一个带值的选项。如果想让用户必须为选项指定一个值,则需要在选项名称末尾追加一个等号 = 作为选项的后缀:

  1. /**
  2. * 命令名称及签名
  3. *
  4. * @var string
  5. */
  6. protected $signature = 'email:send {user} {--queue=}';

在这个例子中, 用户可以传递该选项的值,如下所示:

  1. php artisan email:send 1 --queue=default

你也可以通过在选项名称后面指定默认值来设定该选项的默认值。如果用户没有传递选项值,将使用设定的默认值:

  1. email:send {user} {--queue=default}

选项简写

要在定义选项时指定简写,你可以在选项名称前指定它,并用 | 分隔符将简写与完整选项名称分隔开:

  1. email:send {user} {--Q|queue}

输入数组

如果你想定义接收数组输入的参数或选项,你可以使用 * 符号。首先,我们先看一个数组参数的实例:

  1. email:send {user*}

调用此方法时,user 参数的输入参数将按顺序传递给命令。例如,以下命令会设置 user 的值为 ['foo', 'bar']

  1. php artisan email:send foo bar

当定义接收数组的选项时,传递给命令行的每个选项值都应以选项名称为前缀:

  1. email:send {user} {--id=*}
  2. php artisan email:send --id=1 --id=2

输入说明

你可以通过使用冒号来为参数和选项添加说明,并使其将他们隔开。如果你需要一点额外的空间来定义你的命令,可以随意分开在多个行里:

  1. /**
  2. * 命令名称及签名
  3. *
  4. * @var string
  5. */
  6. protected $signature = 'email:send
  7. {user : 用户ID}
  8. {--queue= : 是否将任务放入队列}';

命令 I/O

检索输入

在命令执行时,显然你需要获取命令接收到的参数和选项的值。你可以用 argumentoption 方法来达到目的:

  1. /**
  2. * 执行命令
  3. *
  4. * @return mixed
  5. */
  6. public function handle()
  7. {
  8. $userId = $this->argument('user');
  9. //
  10. }

如果你想所有的参数以 array 数组获取,可以调用 arguments 方法:

  1. $arguments = $this->arguments();

和获取参数类似,option 方法可以非常容易的获取选项的值。要将所有的选项以数组获取,使用 options 方法:

  1. // 获取一个指定的选项值...
  2. $queueName = $this->option('queue');
  3. // 获取所有选项值...
  4. $options = $this->options();

如果参数或选项不存在,则返回 null 。

交互式输入

除了显示输出外,你还可以要求用户在命令执行时提供输入。ask 方法将提示用户输入并接收,然后用户的输入将会传入你的命令:

  1. /**
  2. * 执行命令行命令。
  3. *
  4. * @return mixed
  5. */
  6. public function handle()
  7. {
  8. $name = $this->ask('What is your name?');
  9. }

secret 方法和 ask 方法类似,但当用户在控制台输入时他们的输入内容是不可见的。这个方法适用于需要用户输入像密码这样的敏感信息的时候:

  1. $password = $this->secret('What is the password?');

请求确认

如果你想要寻求用户确认一些简单的信息,你可以使用 confirm 方法。默认情况下,该方法将返回 false 。但如果用户在回复中输入y或者 yes 则会返回 true

  1. if ($this->confirm('Do you wish to continue?')) {
  2. //
  3. }

自动补全

anticipate 方法可用于为可能的选择提供自动补全功能。用户仍然可以忽略自动补全的提示,作任意回答:

  1. $name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);

或者,您可以将Closure作为第二个参数传递给anticipate方法。 每当用户键入输入字符时,都会调用Closure。 该闭包函数会接受一个包含用户输入的字符串参数,并返回一个可供自动补全的选项数组:

  1. $name = $this->anticipate('What is your name?', function ($input) {
  2. // 返回自动补全的选项...
  3. });

多选问题

如果你要给用户提供一些预设的选择,可以使用 choice 方法。你也可以设置默认值的索引,用以应对用户没有选择的情景:

  1. $name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);

另外, choice 方法的第四个和第五个参数是可选的,分别用于确定选择一个合法的响应的可尝试的次数以及是否可以选择多个答案。

  1. $name = $this->choice(
  2. 'What is your name?',
  3. ['Taylor', 'Dayle'],
  4. $defaultIndex,
  5. $maxAttempts = null,
  6. $allowMultipleSelections = false
  7. );

编写输出

可以使用 lineinfocommentquestionerror 方法来将输出发送到终端。每个方法都使用适当的 ANSI 颜色表明其目的。例如,让我们向用户显示一些普通信息,通常来说,最好使用 info 方法,它会在控制台将输出的内容显示为绿色

  1. /**
  2. * 执行命令行命令。
  3. *
  4. * @return mixed
  5. */
  6. public function handle()
  7. {
  8. $this->info('Display this on the screen');
  9. }

显示错误信息, 使用 error 方法。 错误信息则会显示为红色:

  1. $this->error('Something went wrong!');

如果你想在控制台显示无颜色设置的输出,请使用 line 方法:

  1. $this->line('Display this on the screen');

表格布局

对于多行列数据的格式化输出,table 方法处理起来更轻松。基于传入的表头和行数据,它会动态计算宽高:

  1. $headers = ['Name', 'Email'];
  2. $users = App\User::all(['name', 'email'])->toArray();
  3. $this->table($headers, $users);

进度条

对于耗时任务,提示进度非常有必要。使用 output 对象就可以创建、加载以及停止进度条。首先,定义好任务总步数,然后,在每次任务执行时加载进度条:

  1. $users = App\User::all();
  2. $bar = $this->output->createProgressBar(count($users));
  3. $bar->start();
  4. foreach ($users as $user) {
  5. $this->performTask($user);
  6. $bar->advance();
  7. }
  8. $bar->finish();

参阅 Symfony Progress Bar component documentation 获取更多高级用法。

注册命令

app/Console/Commands 目录下的命令都会被注册,这是由于控制台内核的 commands 方法调用了 load。实际上,可随意调用 load 来扫描其他目录下的 Artisan 命令:

  1. /**
  2. * 注册命令
  3. *
  4. * @return void
  5. */
  6. protected function commands()
  7. {
  8. $this->load(__DIR__.'/Commands');
  9. $this->load(__DIR__.'/MoreCommands');
  10. // ...
  11. }

也可以在 app/Console/Kernel.php 文件的 $commands 属性中手动注册命令的类名。Artisan 启动时,这个属性列出的命令都将由 服务容器 解析并通过 Artisan 进行注册:

  1. protected $commands = [
  2. Commands\SendEmails::class
  3. ];

程序调用命令

有时需要在 CLI 之外执行 Artisan 命令,例如,在路由或控制器里触发 Artisan 命令。要实现调用,可以使用 Artisan 门面的 call 方法。call 方法的第一个参数接受命令名,第二个参数接受数组形式的命令参数。退出码将返回:

  1. Route::get('/foo', function () {
  2. $exitCode = Artisan::call('email:send', [
  3. 'user' => 1, '--queue' => 'default'
  4. ]);
  5. //
  6. });

另外,你可以将整个 Artisan 命令作为字符串传递给 call 方法:

  1. Artisan::call('email:send 1 --queue=default');

Artisan 门面的 queue 方法可以将 Artisan 命令队列化,交由 队列工作进程 进行后台处理。使用此方法之前,务必配置好队列以及运行队列监听器:

  1. Route::get('/foo', function () {
  2. Artisan::queue('email:send', [
  3. 'user' => 1, '--queue' => 'default'
  4. ]);
  5. //
  6. });

你也可以指定 Artisan 命令派发的连接或任务:

  1. Artisan::queue('email:send', [
  2. 'user' => 1, '--queue' => 'default'
  3. ])->onConnection('redis')->onQueue('commands');

传递数组值

如果定义了接受数组的选项,可以直接传递数组到该选项:

  1. Route::get('/foo', function () {
  2. $exitCode = Artisan::call('email:send', [
  3. 'user' => 1, '--id' => [5, 13]
  4. ]);
  5. });

传递布尔值

需要指定没有选项值的选项时,例如,migrate:refresh 命令的 —force 选项,就可以传入 truefalse

  1. $exitCode = Artisan::call('migrate:refresh', [
  2. '--force' => true,
  3. ]);

命令的互相调用

call 方法可以实现调用其它 Artisan 命令。call 方法接受命令名和数组形式的选项:

  1. /**
  2. * 执行命令
  3. *
  4. * @return mixed
  5. */
  6. public function handle()
  7. {
  8. $this->call('email:send', [
  9. 'user' => 1, '--queue' => 'default'
  10. ]);
  11. //
  12. }

如果要抑制控制台命令的所有输出,可以使用 callSilent 方法。callSilent 的使用方法同 call

  1. $this->callSilent('email:send', [
  2. 'user' => 1, '--queue' => 'default'
  3. ]);

Stub 定制

Artisan console 的 make 命令用于创建各种类,如控制器、作业、迁移和测试。 这些类是使用 『stub』 文件生成的,stub 文件根据你的输入填充值。但是,有时可能你希望对 Artisan 生成的文件进行小的更改。为了实现这一点,你可以使用 stub:publish 命令来发布最常见的定制 stub :

  1. php artisan stub:publish

发布的 stub 将位于应用程序根目录中的 stubs 目录中。对这些 stub 所做的任何更改都将在使用 Artisan make 命令生成相应的类时反映出来。

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

Laravel China 社区:https://learnku.com/docs/laravel/7.x/artisan/7480