Artisan 是 Laravel 附带的命令行接口。Artisan 作为 artisan 脚本存在于你的应用根目录,并提供了许多可在构建应用时为你提供帮助的命令。要查看所有可用的 Artisan 命令列表,你可以使用 list 命令:
php artisan list每个命令也包含一个“帮助”屏幕,它显示并描述该命令的可用参数和选项。要查看帮助屏幕,请在命令名称前加上 help:
php artisan help migrate如果您正在使用 Laravel Sail 作为您的本地开发环境,请记住使用 sail 命令行来调用 Artisan 命令。Sail 将在您的应用程序的 Docker 容器中执行您的 Artisan 命令:
./vendor/bin/sail artisan listLaravel Tinker 是 Laravel 框架的一个强大 REPL,由 PsySH 包提供支持。
所有 Laravel 应用程序默认都包含 Tinker。但是,如果你之前从应用程序中移除了它,你可以使用 Composer 来安装 Tinker:
composer require laravel/tinker[!注意]
当您与 Laravel 应用程序交互时,是否正在寻找热重载、多行代码编辑和自动补全功能? 试试 Tinkerwell!
Tinker 允许你与你的整个 Laravel 应用在命令行上交互,包括你的 Eloquent 模型、任务、事件等等。要进入 Tinker 环境,运行 tinker Artisan 命令:
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-compiled、down、env、inspire、migrate、migrate:install、up 和 optimize 命令。如果您想允许更多命令,可以将其添加到 tinker.php 配置文件中的 commands 数组中:
'commands' => [
// App\Console\Commands\ExampleCommand::class,
],通常情况下,Tinker 会在你与 Tinker 交互时自动为类设置别名。然而,你可能希望永远不要为某些类设置别名。你可以通过在你的 tinker.php 配置文件中的 dont_alias 数组中列出这些类来完成此操作:
'dont_alias' => [
App\Models\User::class,
],除了 Artisan 提供的命令之外,你还可以构建自己的自定义命令。命令通常存储在 app/Console/Commands 目录下;但是,只要你指示 Laravel 扫描其他目录以查找 Artisan 命令,就可以自由选择自己的存储位置。
要创建一个新命令, 你可以使用 make:command Artisan 命令. 此命令将在 app/Console/Commands 目录下创建一个新的命令类. 如果此目录在你的应用中不存在, 也无需担心 - 它会在你第一次运行 make:command Artisan 命令时被创建:
php artisan make:command SendEmails生成你的命令后,你应该为类的 signature 和 description 属性定义合适的值。这些属性将在 list 屏幕上显示你的命令时使用。 signature 属性还允许你定义你的命令的输入期望。当你的命令被执行时,handle 方法将被调用。你可以将你的命令逻辑放在此方法中。
让我们来看一个示例命令。注意,我们可以通过命令的handle方法请求任何所需的依赖。Laravel的服务容器将自动注入此方法的签名中类型提示的所有依赖:
<?php
namespace App\Console\Commands;
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Command;
class SendEmails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send {user}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send a marketing email to a user';
/**
* Execute the console command.
*/
public function handle(DripEmailer $drip): void
{
$drip->send(User::find($this->argument('user')));
}
}[!NOTE]
为了实现更高的代码复用,推荐的做法是保持你的控制台命令轻量,并让它们委托给应用服务来完成其任务。在上面的示例中,请注意我们注入了一个服务类来完成发送电子邮件的“繁重工作”。
如果 handle 方法没有返回任何内容且命令执行成功,命令将以 0 退出码退出,表示成功。但是,handle 方法可以选择返回一个整数来手动指定命令的退出码:
$this->error('Something went wrong.');
return 1;如果您希望从命令中的任何方法内“失败”该命令,您可以使用 fail 方法。fail 方法将立即终止命令的执行,并返回退出代码 1:
$this->fail('Something went wrong.');基于闭包的命令提供了一种替代方案,可以将控制台命令定义为类。 就像路由闭包是控制器的替代方案一样,可以将命令闭包视为命令类的替代方案。
尽管 routes/console.php 文件不定义 HTTP 路由,但它定义了基于控制台的入口点(路由)到你的应用中。在此文件中,你可以使用 Artisan::command 方法定义所有基于闭包的控制台命令。command 方法接受两个参数:命令签名 以及一个接收命令参数和选项的闭包:
Artisan::command('mail:send {user}', function (string $user) {
$this->info("Sending email to: {$user}!");
});闭包绑定到底层命令实例,因此你可以完全访问在一个完整命令类上通常能访问的所有辅助方法。
除了接收您的命令的参数和选项,命令闭包还可以类型提示您希望从服务容器中解析的额外依赖:
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Support\Facades\Artisan;
Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) {
$drip->send(User::find($user));
});当定义一个基于闭包的命令时,你可以使用 purpose 方法为该命令添加描述。当你运行 php artisan list 或 php artisan help 命令时,此描述将被显示:
Artisan::command('mail:send {user}', function (string $user) {
// ...
})->purpose('Send a marketing email to a user');[!WARNING]
要使用此功能,您的应用程序必须使用memcached、redis、dynamodb、database、file或array缓存驱动程序作为您的应用程序的默认缓存驱动程序。此外,所有服务器都必须与同一个中心缓存服务器通信。
有时您可能希望确保命令的单个实例只能同时运行。为此,您可以在命令类上实现 Illuminate\Contracts\Console\Isolatable 接口:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
class SendEmails extends Command implements Isolatable
{
// ...
}当你将一个命令标记为 Isolatable 时,Laravel 会自动为该命令提供 --isolated 选项,而无需在命令的选项中明确定义它。当该命令使用此选项调用时,Laravel 会确保该命令没有其他实例正在运行。Laravel 通过尝试使用你应用程序的默认缓存驱动获取一个原子锁来完成此操作。如果该命令的其他实例正在运行,该命令将不会执行;但是,该命令仍将以成功的退出状态码退出:
php artisan mail:send 1 --isolated如果您想指定命令在无法执行时应返回的退出状态码,可以通过 isolated 选项提供所需的退出状态码:
php artisan mail:send 1 --isolated=12默认情况下,Laravel 会使用命令的名称来生成用于在应用程序缓存中获取原子锁的字符串键。但是,你可以通过在你的 Artisan 命令类上定义一个 isolatableId 方法来自定义这个键,允许你将命令的参数或选项整合到这个键中:
/**
* Get the isolatable ID for the command.
*/
public function isolatableId(): string
{
return $this->argument('user');
}默认情况下,隔离锁在命令完成后过期。或者,如果命令被中断且无法完成,锁将在一个小时后过期。但是,您可以通过在命令上定义一个 isolationLockExpiresAt 方法来调整锁的过期时间:
use DateTimeInterface;
use DateInterval;
/**
* Determine when an isolation lock expires for the command.
*/
public function isolationLockExpiresAt(): DateTimeInterface|DateInterval
{
return now()->addMinutes(5);
}在编写控制台命令时,通常会通过参数或选项收集用户的输入。Laravel 通过在命令中使用 signature 属性,使定义你期望从用户那里获得的输入变得非常方便。此 signature 属性允许你以一种单一、富有表现力的路由式语法来定义命令的名称、参数和选项。
所有用户提供的参数和选项都用花括号包裹。在以下示例中,该命令定义了一个必需的参数: user:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send {user}';你也可以使参数变为可选的,或为参数定义默认值:
// Optional argument...
'mail:send {user?}'
// Optional argument with default value...
'mail:send {user=foo}'选项,像参数一样,是另一种用户输入形式。选项在通过命令行提供时,前面会加上两个连字符 (--)。选项有两种类型:接收值的和不接收值的。不接收值的选项用作一个布尔型“开关”。让我们看一下这种类型选项的示例:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue}';在此示例中,在调用 Artisan 命令时可以指定 --queue 开关。如果传递了 --queue 开关,该选项的值将为 true。否则,该值将为 false:
php artisan mail:send 1 --queue接下来,我们来看看一个需要值的选项。如果用户必须为一个选项指定一个值,你应该在选项名称后面加上一个=符号:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue=}';在这个例子中,用户可以像这样为该选项传递一个值。如果在使用命令时没有指定该选项,其值将为 null:
php artisan mail:send 1 --queue=default您可以通过在选项名称后指定默认值来为选项分配默认值。如果用户未传递任何选项值,将使用默认值:
'mail:send {user} {--queue=default}'在定义选项时指定快捷方式,您可以将其指定在选项名称之前,并使用 | 字符作为分隔符,以将快捷方式与完整的选项名称分开:
'mail:send {user} {--Q|queue=}'当在终端上调用命令时,选项快捷方式应以单个连字符作为前缀,并且在为选项指定值时,不应包含 = 字符:
php artisan mail:send 1 -Qdefault如果您想定义参数或选项以接收多个输入值,您可以使用 * 字符。
首先,让我们看一个指定此类参数的例子:
'mail:send {user*}'当运行此命令时,user 参数可能会按顺序传递给命令行。例如,以下命令将把 user 的值设置为一个以 1 和 2 为值的数组:
php artisan mail:send 1 2这个 * 字符可以与一个可选的参数定义结合,以允许某个参数的零个或多个实例:
'mail:send {user?*}'当定义一个需要多个输入值的选项时,传递给命令的每个选项值都应以选项名称为前缀:
'mail:send {--id=*}'这样的命令可以通过传递多个 --id 参数来调用:
php artisan mail:send --id=1 --id=2您可以通过使用冒号将参数名称与描述分开,从而为输入参数和选项分配描述。如果您需要更多空间来定义命令,可以随意将定义分散到多行:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send
{user : The ID of the user}
{--queue : Whether the job should be queued}';如果您的命令包含必需参数,则在未提供时,用户将收到一条错误消息。或者,您可以通过实现 PromptsForMissingInput 接口,将您的命令配置为在缺少必需参数时自动提示用户:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
class SendEmails extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mail:send {user}';
// ...
}如果 Laravel 需要从用户那里收集一个必需的参数,它将通过智能地使用参数名称或描述来措辞问题,从而自动向用户询问该参数。如果您希望自定义用于收集必需参数的问题,您可以实现 promptForMissingArgumentsUsing 方法,返回一个以参数名称为键的问题数组:
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array<string, string>
*/
protected function promptForMissingArgumentsUsing(): array
{
return [
'user' => 'Which user ID should receive the mail?',
];
}您还可以通过使用包含问题和占位符的元组来提供占位符文本:
return [
'user' => ['Which user ID should receive the mail?', 'E.g. 123'],
];如果您希望完全控制提示,您可以提供一个闭包,该闭包应提示用户并返回他们的答案:
use App\Models\User;
use function Laravel\Prompts\search;
// ...
return [
'user' => fn () => search(
label: 'Search for a user:',
placeholder: 'E.g. Taylor Otwell',
options: fn ($value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: []
),
];[!NOTE]
综合的 Laravel Prompts 文档包含了关于可用提示及其用法的额外信息.
如果您希望提示用户选择或输入选项,您可以在命令的 handle 方法中包含提示。但是,如果您只想在用户因缺少参数而被自动提示后才提示,那么您可以实现 afterPromptingForMissingArguments 方法:
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function Laravel\Prompts\confirm;
// ...
/**
* Perform actions after the user was prompted for missing arguments.
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
$input->setOption('queue', confirm(
label: 'Would you like to queue the mail?',
default: $this->option('queue')
));
}当您的命令正在执行时,您可能需要访问您的命令所接受的参数和选项的值。为此,您可以使用 argument 和 option 方法。如果参数或选项不存在,将返回 null:
/**
* Execute the console command.
*/
public function handle(): void
{
$userId = $this->argument('user');
}如果您需要将所有参数作为 数组,调用 arguments 方法:
$arguments = $this->arguments();选项可以使用 option 方法像参数一样轻松地检索。要将所有选项作为数组检索,请调用 options 方法:
// Retrieve a specific option...
$queueName = $this->option('queue');
// Retrieve all options as an array...
$options = $this->options();[!NOTE]
Laravel Prompts 是一个 PHP 包,用于为你的命令行应用添加美观且用户友好的表单,具有类似浏览器的功能,包括占位符文本和验证。
除了显示输出,您还可以在命令执行期间请求用户提供输入。 此 ask 方法将向用户提问,接受他们的输入,然后将用户的输入返回给您的命令:
/**
* Execute the console command.
*/
public function handle(): void
{
$name = $this->ask('What is your name?');
// ...
}该 ask 方法也接受一个可选的第二个参数,用于指定在未提供用户输入时应返回的默认值:
$name = $this->ask('What is your name?', 'Taylor');secret 方法类似于 ask,但用户在控制台中输入时,其输入内容将不可见。此方法在询问敏感信息(如密码)时非常有用:
$password = $this->secret('What is the password?');如果你需要向用户请求一个简单的“是或否”确认,你可以使用 confirm 方法。默认情况下,此方法将返回 false。但是,如果用户在响应提示时输入 y 或 yes,该方法将返回 true。
if ($this->confirm('Do you wish to continue?')) {
// ...
}如有必要,您可以通过将 true 作为第二个参数传递给 confirm 方法,来指定确认提示应默认返回 true:
if ($this->confirm('Do you wish to continue?', true)) {
// ...
}该 anticipate 方法可用于为可能的选择提供自动补全。用户仍可提供任何答案,无论自动补全提示如何:
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);此外,你也可以将一个闭包作为第二个参数传递给 anticipate 方法。该闭包将在用户每次输入一个字符时被调用。该闭包应接受一个包含用户目前为止输入内容的字符串参数,并返回一个用于自动补全的选项数组:
use App\Models\Address;
$name = $this->anticipate('What is your address?', function (string $input) {
return Address::whereLike('name', "{$input}%")
->limit(5)
->pluck('name')
->all();
});如果你需要在提问时给用户一个预定义的选项集,你可以使用 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、newLine、info、comment、question、warn、alert 和 error 方法。这些方法中的每一种都将使用适当的 ANSI 颜色用于各自的目的。例如,让我们向用户显示一些通用信息。通常,info 方法将在控制台中显示为绿色文本:
/**
* Execute the console command.
*/
public function handle(): void
{
// ...
$this->info('The command was successful!');
}要显示错误消息,请使用 error 方法。错误消息文本通常以红色显示:
$this->error('Something went wrong!');您可以使用 line 方法来显示纯文本,不带颜色:
$this->line('Display this on the screen');您可以使用 newLine 方法来显示一个空行:
// Write a single blank line...
$this->newLine();
// Write three blank lines...
$this->newLine(3);table 方法使得正确格式化多行/多列数据变得容易。您只需提供列名和表格数据,Laravel 就会自动为您计算表格的适当宽度和高度:
use App\Models\User;
$this->table(
['Name', 'Email'],
User::all(['name', 'email'])->toArray()
);对于长时间运行的任务,显示一个进度条来告知用户任务的完成度会很有帮助。使用 withProgressBar 方法,Laravel 将显示一个进度条,并对于给定可迭代值的每次迭代推进其进度:
use App\Models\User;
$users = $this->withProgressBar(User::all(), function (User $user) {
$this->performTask($user);
});有时,您可能需要更精细地手动控制进度条的推进方式。首先,定义该过程将迭代的总步数。然后,在处理完每个项目后推进进度条:
$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 进度条组件文档。
默认情况下,Laravel 会自动注册 app/Console/Commands 目录中的所有命令。但是,你可以指示 Laravel 扫描其他目录以查找 Artisan 命令,方法是使用 withCommands 方法,该方法在你应用程序的 bootstrap/app.php 文件中:
->withCommands([
__DIR__.'/../app/Domain/Orders/Commands',
])如果需要,您也可以通过向 withCommands 方法提供命令的类名来手动注册命令:
use App\Domain\Orders\Commands\SendEmails;
->withCommands([
SendEmails::class,
])当 Artisan 启动时,你的应用程序中的所有命令将由服务容器解析并注册到 Artisan。
有时,您可能希望在 CLI 之外执行 Artisan 命令。例如,您可能希望从路由或控制器中执行 Artisan 命令。您可以使用 Artisan facade 上的 call 方法来实现此目的。call 方法接受命令的签名名称或类名作为其第一个参数,以及一个命令参数数组作为第二个参数。退出代码将被返回:
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/user/{user}/mail', function (string $user) {
$exitCode = Artisan::call('mail:send', [
'user' => $user, '--queue' => 'default'
]);
// ...
});或者,您可以将整个 Artisan 命令以字符串形式传递给 call 方法:
Artisan::call('mail:send 1 --queue=default');如果你的命令定义了一个接受数组的选项, 你可以向该选项传递一个值的数组:
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/mail', function () {
$exitCode = Artisan::call('mail:send', [
'--id' => [5, 13]
]);
});如果你需要为一个不接受字符串值的选项指定值,例如 migrate:refresh 命令上的 --force 标志,你应该传入 true 或 false 作为该选项的值:
$exitCode = Artisan::call('migrate:refresh', [
'--force' => true,
]);使用 Artisan 门面上的 queue 方法,你甚至可以将 Artisan 命令排队,以便它们由你的 队列工作器 在后台处理。在使用此方法之前,请确保你已配置好队列并且正在运行队列监听器:
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/user/{user}/mail', function (string $user) {
Artisan::queue('mail:send', [
'user' => $user, '--queue' => 'default'
]);
// ...
});使用 onConnection 和 onQueue 方法,您可以指定 Artisan 命令应分派到的连接或队列:
Artisan::queue('mail:send', [
'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');有时您可能希望从现有的 Artisan 命令中调用其他命令。您可以使用 call 方法来实现。这个 call 方法接受命令名称和一个命令参数/选项数组:
/**
* Execute the console command.
*/
public function handle(): void
{
$this->call('mail:send', [
'user' => 1, '--queue' => 'default'
]);
// ...
}如果您希望调用另一个控制台命令并抑制其所有输出,您可以使用 callSilently 方法。 callSilently 方法具有与 call 方法相同的签名:
$this->callSilently('mail:send', [
'user' => 1, '--queue' => 'default'
]);正如你可能知道的,操作系统允许向运行中的进程发送信号。例如,SIGTERM 信号是操作系统请求程序终止的方式。如果你希望在 Artisan 控制台命令中监听信号并在信号发生时执行代码,你可以使用 trap 方法:
/**
* Execute the console command.
*/
public function handle(): void
{
$this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false);
while ($this->shouldKeepRunning) {
// ...
}
}为了同时监听多个信号,你可以向 trap 方法提供一个信号数组:
$this->trap([SIGTERM, SIGQUIT], function (int $signal) {
$this->shouldKeepRunning = false;
dump($signal); // SIGTERM / SIGQUIT
});Artisan 控制台的 make 命令用于创建各种类,例如控制器、作业、迁移和测试。这些类是使用“stub”文件生成的,这些文件会根据你的输入填充值。然而,你可能希望对 Artisan 生成的文件进行少量更改。为了实现此目的,你可以使用 stub:publish 命令将最常用的 stub 文件发布到你的应用中,以便你可以自定义它们:
php artisan stub:publish已发布的存根文件将位于你的应用程序根目录下的 stubs 目录中。你对这些存根文件进行的任何修改,都将在你使用 Artisan 的 make 命令生成它们对应的类时体现出来。
Artisan 在运行命令时会分发三个事件:Illuminate\Console\Events\ArtisanStarting, Illuminate\Console\Events\CommandStarting, 和Illuminate\Console\Events\CommandFinished。ArtisanStarting 事件会在 Artisan 启动运行时立即分发。接下来, CommandStarting 事件会在命令运行前立即分发。最后, CommandFinished 事件会在命令执行完成后分发。