Artisan 命令行 简介 Artisan 是 Laravel 自带的命令行接口, 它提供了相当多的命令来帮助你构建 Laravel 应用。 你可以通过 list 命令查看所有可用的 Artisan 命令: ``` php artisan list ``` 每个命令都包含了「帮助」界面,它会显示和概述命令的可用参数及选项。只需要在命令前加上 help 即可查看命令帮助界面: ``` php artisan help migrate ``` Tinker 命令 (REPL) 所有 Laravel 应用都包含了 Tinker,一个由 PsySH 包提供支持的 REPL。 安装 所有 Laravel 应用都默认包含了 Tinker。不过,你也可以使用 Composer 在你需要时手动安装: ``` composer require laravel/tinker ``` 提示:正在寻找一个能与 Laravel 交互的图形用户界面吗? 试试 Tinkerwell! 用法 Tinker 让你可以在命令行与你的整个 Laravel 应用进行交互。包括但不限于 Eloquent ORM、任务、事件等。通过运行 Artisan 命令 tinker 进入 Tinker 环境。 ``` php artisan tinker ``` 你可以通过 vendor:publish 命令发布 Tinker 配置文件: ``` php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" ``` 注意:Dispatchable 类中的 dispatch 辅助函数和 dispatch 方法已被弃用以将任务添加至队列中。因此,当你使用 Tinker 时,请使用 Bus::dispatch 或 Queue::push 来分发任务。 命令白名单 Tinker 采用白名单来确定允许哪些 Artisan 命令可以在 shell 中允许。默认情况下,你可以运行 clear-compiler、down、env、inspire、migrate、optimize 和 up 命令。如果你想将命令添加到白名单,请将该命令添加到 tinker.php 配置文件中的 commands 数组: ``` 'commands' => [ // App\Console\Commands\ExampleCommand::class, ], ``` 别名黑名单 大多数情况下,Tinker 会在你引入类时自动为其添加别名。然而,你可能不希望为某些类添加别名。你可以在 tinker.php 配置文件中的 dont_alias 数组里列举这些类来完成此操作: ``` 'dont_alias' => [ App\Models\User::class, ], ``` 编写命令 除 Artisan 提供的命令外,你也可以编写自己的自定义命令。命令在多数情况下位于 app/Console/Commands 目录中;不过,只要你的命令可以由 Composer 加载,你就可以自由选择自己的存储位置。 生成命令 你可以使用 Artisan 命令 make:command 来创建一个新的命令。make:command 命令会在 app/Console/Commands 目录中创建一个新的命令类。如果该目录不存在,它会在你第一次运行 make:command 命令时自动创建。生成的命令将包含所有命令中默认存在的属性和方法。 ``` php artisan make:command SendEmails ``` 命令结构 创建一个新命令后,你应该先修改 signature 和 description 属性以便你在使用 Artisan 命令 list 时能够清楚知道该命令的用法。执行命令时会调用 handle 方法,你可以在此方法中放置命令逻辑。 技巧:为了实现更好的代码复用,请尽量让你的命令类保持轻量并且能够延迟到应用服务中完成。在下面的例子中,我们将注入一个服务类来完成发送邮件的重任。 来看一个简单的例子。在命令类的 handle 方法中,我们可以注入任何我们需要的依赖项。Laravel 服务容器 将会自动注入所有在构造函数中带类型约束的依赖。 ``` <?php namespace App\Console\Commands; use App\Models\User; use App\Support\DripEmailer; use Illuminate\Console\Command; class SendEmails extends Command { /** * 命令名称及签名 * * @var string */ protected $signature = 'email:send {user}'; /** * 命令描述 * * @var string */ protected $description = 'Send drip e-mails to a user'; /** * 创建命令 * * @return void */ public function __construct() { parent::__construct(); } /** * 执行命令 * * @param \App\Support\DripEmailer $drip * @return mixed */ public function handle(DripEmailer $drip) { $drip->send(User::find($this->argument('user'))); } } ``` 闭包命令 基于闭包的命令提供了一个用类代替定义控制台命令的方法。同理,闭包路由也是控制器的一种替代方法,而闭包命令可以说是命令类的替代方法。在 app/Console/Kernel.php 文件中的 commands 方法中,Laravel 加载了 routes/console.php 文件: ``` /** * 注册闭包命令 * * @return void */ protected function commands() { require base_path('routes/console.php'); } ``` 尽管该文件没有定义 HTTP 路由,但它定义了进入应用程序的基于控制台的入口(routes)。 在这个文件中,你可以使用 Artisan::command 方法定义所有的闭包路由。 command 方法接受两个参数: 命令名称 和可调用的闭包(可以接受命令参数及选项): ``` Artisan::command('build {project}', function ($project) { $this->info("Building {$project}!"); }); ``` 闭包绑定了底层的命令实例,因此你可以访问能够在命令类中使用的所有辅助方法。 类型约束依赖 除了接受命令参数及选项外,命令闭包也可以使用类型约束从 服务容器 中解析其他的依赖关系: ``` use App\Models\User; use App\Support\DripEmailer; Artisan::command('email:send {user}', function (DripEmailer $drip, $user) { $drip->send(User::find($user)); }); ``` 闭包命令描述 在你定义一个闭包命令时,你可以通过 describe 方法来为命令添加描述。这个描述会在你执行 php artisan list 或 php artisan help 命令时显示: ``` Artisan::command('build {project}', function ($project) { $this->info("Building {$project}!"); })->describe('Build the project'); ``` 定义输入期望 在编写控制台命令时,通常是通过参数和选项来收集用户输入的。Laravel 让你可以非常方便地在 signature 属性中定义你期望用户输入的内容。signature 属性允许使用单一且可读性高,类似路由的语法来定义命令的名称、参数和选项。 参数 所有用户提供的参数和选项都被包含在花括号中。在下方的例子中,这个命令定义了一个 必填 的参数:user: ``` /** * 命令的名称及其签名 * * @var string */ protected $signature = 'email:send {user}'; ``` 您亦可创建可选参数或为参数定义默认值: ``` // 可选参数 email:send {user?} // 带有默认值的可选参数 email:send {user=foo} ``` 选项 选项类似于参数,是用户输入的另一种形式。在命令行中指定选项的时候,它们以两个短横线(--)作为前缀。这有两种类型的选项:接收值和不接受值。不接收值的选项就像是一个布尔「开关」。让我们看一下这种类型的选项的例子: ``` /** * 命令的名称及其签名 * * @var string */ protected $signature = 'email:send {user} {--queue}'; ``` 在这个例子中,在调用 Artisan 命令时可以指定 --queue 的开关。如果传递了 --queue 选项,该选项的值将会是 true 。否则,其值将会是 false : ``` php artisan email:send 1 --queue ``` 带值的选项 接下来,让我们来看一下需要带值的选项。如果用户需要为一个选项指定一个值,则需要在选项名称的末尾追加一个等号 = : ``` /** * 命名名称及其签名 * * @var string */ protected $signature = 'email:send {user} {--queue=}'; ``` 在这个例子中,用户可以像如下所时的方式传递该选项的值: ``` php artisan email:send 1 --queue=default ``` 您可以在选项名称后指定其默认值。如果用户没有传递值给选项,将使用默认的值: ``` email:send {user} {--queue=default} ``` 选项简写 要在定义选项的时候指定一个简写,您可以在选项名前面使用 | 分隔符将选项名称与其简写分隔开来: ``` email:send {user} {--Q|queue} ``` 输入数组 如果您想要接收数组的参数或者选项,您可以使用 * 字符。首先,让我们看一下指定了一个数组参数的例子: ``` email:send {user*} ``` 当调用这个方法的时候,user 参数的输入参数将按顺序传递给命令。例如,以下命令将会设置 user 的值为 ['foo', 'bar'] : ``` php artisan email:send foo bar ``` 当定义一个接收数组的选项的时候,传递给命令的每一个选项值都应该添加选项名称为前缀: ``` email:send {user} {--id=*} php artisan email:send --id=1 --id=2 ``` 输入描述 您可以使用冒号将参数和描述开来,以实现为输入参数和选项指定描述。如果您需要一些额外的空间来定义您的命令,可以将其随意分开到多行: ``` /** * 命令的名称及其信号 * * @var string */ protected $signature = 'email:send {user : The ID of the user} {--queue= : Whether the job should be queued}'; ``` 命令 I/O 解析输入 在执行命令时,显然您需要获取命令接收到的参数或选项的值。您可以使用 argument 和 option 方法来实现之: ``` /** * 执行命令。 * * @return mixed */ public function handle() { $userId = $this->argument('user'); // } 您可以使用 arguments 方法以数组的形式解析参数: $arguments = $this->arguments(); 和获取参数类似,使用 option 方法可以很容易的获取选项的值。您亦可调用 options 方法来以数组的形式解析所有的选项: // 解析特定的选项... $queueName = $this->option('queue'); // 解析所有选项... $options = $this->options(); ``` 如果参数或选项不存在,将会返回 null 。 交互式输入 除了显示输出以外,您亦可在执行命令的时候通过询问用户以提供输入。ask 方法将询问用户指定的问题来接收用户输入,然后用户输入将会传到您的命令中: ``` /** * 执行命令 * * @return mixed */ public function handle() { $name = $this->ask('What is your name?'); } ``` secret 方法与 ask 相似,但是用户在输入的时候,用户输入将不可见。这个方法在询问一些注入密码之类的敏感信息时是非常有用的: ``` $password = $this->secret('What is the password?'); ``` 请求确认 如果您需要请求用户进行一个简单的确认,您可以使用 confirm 方法来实现。默认情况下,这个方法会返回 false 。当然,如果用户输入 y 或 yes ,这个方法将会返回 true 。 ``` if ($this->confirm('Do you wish to continue?')) { // } ``` 自动补全 anticipate 方法可用于为可能的选项提供自动补全功能。用户依然可以忽略自动补全的提示,进行任意回答: ``` $name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']); ``` 或者,您可以将一个闭包作为第二个参数传递给 anticipate 方法。每当用户键入字符时,闭包函数都会被调用。闭包函数应该接受一个包含用户输入的字符串形式的参数,并返回一个可供自动补全的选项的数组: ``` $name = $this->anticipate('What is your name?', function ($input) { // 返回自动补全的选项... }); ``` 多选问题 您可以使用 choice 方法给用户提供一些预设的选择。您亦可设置默认值的数组索引,以处理用户没有选择的情况: ``` $name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex); ``` 此外,choice 方法接收可选的第四个和第五个参数,用于确定选择一个有效的响应的可尝试的最大次数以及是否可以选择多个答案: ``` $name = $this->choice( 'What is your name?', ['Taylor', 'Dayle'], $defaultIndex, $maxAttempts = null, $allowMultipleSelections = false ); ``` 编写输出 要发送输出到控制台,请使用 line,info,comment,question 和 error 方法。每一个方法都使用适当的 ANSI 颜色以表明其目的。例如,让我们为用户显示一些普通信息。通常情况下,info 方法将会在控制台中显示绿色文本: ``` /** * 执行控制台命令 * * @return mixed */ public function handle() { $this->info('Display this on the screen'); } ``` 您可以使用 error 方法来显示错误信息。错误信息通常以红色文本显示: ``` $this->error('Something went wrong!'); ``` 如果您想要显示没有着色的输出,请使用 line 方法: ``` $this->line('Display this on the screen'); ``` 表格布局 table 方法让多行多列数据的格式化输出变得非常轻松。它将基于表头和行数据,动态的计算宽高: ``` $headers = ['Name', 'Email']; $users = App\Models\User::all(['name', 'email'])->toArray(); $this->table($headers, $users); ``` 进度条 在执行耗时任务的时候,提供一个进度条时很有必要的。我们可以使用输出对象来创建、加载以及停止进度条。首先,定义好任务的总步数,然后,在每次任务执行的时候加载进度条: ``` $users = App\Models\User::all(); $bar = $this->output->createProgressBar(count($users)); $bar->start(); foreach ($users as $user) { $this->performTask($user); $bar->advance(); } $bar->finish(); ``` 您可以参阅 Symfony 进度条组件文档 以获取进度条的更多高级用法。 注册命令 由于控制台内核的 commands 方法调用了 load 方法,位于 app/Console/Commands 目录中的所有命令都将自动注册。事实上,您亦可随时调用 load 方法来扫描其他目录中的 Artisan 命令: ``` /** * 注册应用命令 * * @return void */ protected function commands() { $this->load(__DIR__.'/Commands'); $this->load(__DIR__.'/MoreCommands'); // ... } ``` 您亦可在 app/Console/Kernel.php 文件的 $commands 属性中手动注册命令的类名。当 Artisan 启动时,该属性里列出的所有命令都将被 服务容器 解析并通过 Artisan 注册: ``` protected $commands = [ Commands\SendEmails::class ]; ``` 程序调用命令 有时,你可能想要在 CLI 以外运行 Artisan 命令。例如,您可能在一个路由或控制器中运行一个 Artisan 命令。您可以使用 Artisan 门面的 call 方法来实现之。call 方法的第一个参数为命令的名称或类名,第二个参数为数组形式的命令参数。该方法将会返回退出码: ``` Route::get('/foo', function () { $exitCode = Artisan::call('email:send', [ 'user' => 1, '--queue' => 'default' ]); // }); ``` 此外,您可以以字符串的形式传递完整的 Artisan 命令到 call 方法中: ``` Artisan::call('email:send 1 --queue=default'); ``` 您甚至可以使用 Artisan 门面的 queue 方法来将 Artisan 命令队列化,此举能够让 队列工作进程 将其置于后台处理。在使用该方法前,请确保您已经配置了队列并运行了队列***: ``` Route::get('/foo', function () { Artisan::queue('email:send', [ 'user' => 1, '--queue' => 'default' ]); // }); ``` 您亦可指定 Artisan 命令派发的连接或队列: ``` Artisan::queue('email:send', [ 'user' => 1, '--queue' => 'default' ])->onConnection('redis')->onQueue('commands'); ``` 传递数组值 如果您的命令定义了一个接受数组的选项,那您便可直接传递数组到该选项: ``` Route::get('/foo', function () { $exitCode = Artisan::call('email:send', [ 'user' => 1, '--id' => [5, 13] ]); }); ``` 传递布尔值 如果您需要指定一个没有选项值的选项时,如 migrate:refresh 命令中的 --force 标识,您便可传递 true 或 false : ``` $exitCode = Artisan::call('migrate:refresh', [ '--force' => true, ]); ``` 命令的相互调用 您可以使用 call 方法来满足在一个已经存在的 Artisan 命令中调用其他命令的需求。call 方法接收命令名称和数组形式的参数: ``` /** * 执行命令 * * @return mixed */ public function handle() { $this->call('email:send', [ 'user' => 1, '--queue' => 'default' ]); // } ``` 您可以使用 callSilent 方法来实现调用另一个命令且抑制所有的输出。callSilent 方法的用法和 call 方法相同: ``` $this->callSilent('email:send', [ 'user' => 1, '--queue' => 'default' ]); ``` Stub 定制 Artisan 的 make 命令可用于创建多种多样的类,如:控制器、任务、迁移和测试。这些类是使用 stub 文件生成的,它将根据您的输入来填充值。有时,您可能想要对 Artisan 命令生成的文件进行一些很小的改动。您可以使用 stub:publish 命令来发布最常见的定制 stub 来实现之: ``` php artisan stub:publish ``` 发布的 stub 将存放于您的应用根目录下的 stubs 目录中。对这些 stub 进行任何改动都将在您使用 Artisan make 命令生成相应的类的时候反映出来。