Artisan 是 Laravel 自带的命令行接口。它提供了许多有用的命令,可以在构建应用时为您提供帮助。要查看所有可用的 Artisan 命令列表,可以使用 list
命令:
php artisan list
每个命令还包括一个「帮助」页面,显示和描述命令的可用参数和选项。要查看帮助页面,可以在命令名前面加上 help
:
php artisan help migrate
所有 Laravel 应用都包含了 Tinker,一个由 PsySH 扩展包提供的 REPL。Tinker 允许您使用命令行和整个 Laravel 应用进行交互,包括 Eloquent ORM,任务,事件等。要进入 Tinker 环境,请运行 Artisan 命令 tinker
:
php artisan tinker
除了 Artisan 提供的命令外,您还可以编写自己的自定义命令。命令通常存储在 app/Console/Commands
目录中;不过,您可以自由选择存储位置只要命令可以被 Composer 加载:
要创建一个新的命令,使用 Artisan 命令 make:command
。此命令会在 app/Console/Commands
目录中新建一个命令类。如果该目录在应用中不存在也别担心,因为它会在第一次运行 Artisan 命令 make:command
时自动创建。生成的命令会包含所有命令中都默认存在的属性和方法:
php artisan make:command SendEmails
生成命令后,应该填写类的 signature
和 description
属性,它们会在 list
界面显示命令时使用。handle
方法会在命令执行时调用。您可以将命令逻辑放在此方法中。
为了更好地复用代码,一个好的开发实践是保持终端命令代码轻量,将相关任务放到应用服务中完成。在如下示例中,注意我们注入了一个服务类来进行发送邮件的「繁重的工作」。
我们来看一个示例命令。注意我们可以在命令的构造函数或 handle
方法中注入任何我们需要的依赖。Laravel 的 服务容器 会自动注入构造函数或 handle
方法中所有使用类型提示的依赖:
namespace App\Console\Commands;
use App\User;
use App\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';
/**
* Drip 邮件服务
*
* @var DripEmailer
*/
protected $drip;
/**
* 创建新的命令实例
*
* @param DripEmailer $drip
* @return void
*/
public function __construct(DripEmailer $drip)
{
parent::__construct();
$this->drip = $drip;
}
/**
* 执行终端命令
*
* @return mixed
*/
public function handle()
{
$this->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 理由,但它依然定义了基于终端的应用的入口点(路由)。在此文件中,可以使用 Artisan::command
方法定义所有闭包路由。command
方法接收两个参数:命令参数 和一个接收命令参数和选项的闭包:
Artisan::command('build {project}', function ($project) {
$this->info("Building {$project}!");
});
闭包会绑定到底层的命令实例,因此可以完全访问通常能在完整的命令类上访问的所有辅助方法。
除了接收命令参数和选项外,命令闭包也可以使用类型提示从 服务容器 中解析其它依赖:
use App\User;
use App\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}';
当命令执行时,显然要获取命令接收的参数和选项值。为此,可以使用 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']);
如果要为用户预设一组选择,可以使用 choice
方法。可以设置没有选项选择时返回的数组的默认索引值:
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
要发送输出给终端,可以使用 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\User::all(['name', 'email'])->toArray();
$this->table($headers, $users);
对于长时间运行的任务,显示进度条会很有用。使用输出对象,我们可以开始、前进和停止进度条。首先,定义进程要迭代的总步数。然后,在处理完每项后前进进度条:
$users = App\User::all();
$bar = $this->output->createProgressBar(count($users));
foreach ($users as $user) {
$this->performTask($user);
$bar->advance();
}
$bar->finish();
更多高级选项,请查看 Symfony 进度条组件文档。
由于在终端内核的 commands
方法中调用了 load
方法,因此 Artisan 会自动注册所有 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
Facade 的 call
方法实现此目的。call
方法接收命令名称或类作为第一个参数,以及命令参数数组作为第二个参数。将返回退出码:
Route::get('/foo', function () {
$exitCode = Artisan::call('email:send', [
'user' => 1, '--queue' => 'default'
]);
//
});
使用 Artisan
Facade 的 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,
]);
有时可能希望在一个现有的 Artisan 命令中调用其它命令,可以使用 call
方法完成该操作。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'
]);