Artisan 命令列
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
Artisan 命令列
簡介
Artisan 是 Laravel 包含的命令列介面。Artisan 以 artisan 腳本的形式存在於您應用程式的根目錄,並提供了許多有用的指令,可以在您建構應用程式時提供協助。要查看所有可用 Artisan 指令的列表,您可以使用 list 指令:
php artisan list
每個指令還包含一個「說明」畫面,顯示並描述該指令可用的參數和選項。要查看說明畫面,請在指令名稱前加上 help:
php artisan help migrate
Laravel Sail
如果您使用 Laravel Sail 作為您的本地開發環境,請記住使用 sail 命令列來呼叫 Artisan 指令。Sail 將在您應用程式的 Docker 容器中執行您的 Artisan 指令:
./vendor/bin/sail artisan list
Tinker (REPL)
Laravel Tinker 是一個強大的 Laravel 框架 REPL,由 PsySH 套件提供支援。
安裝
所有 Laravel 應用程式預設都包含 Tinker。然而,如果您先前已將其從應用程式中移除,您可以使用 Composer 安裝 Tinker:
composer require laravel/tinker
[!NOTE] 在與您的 Laravel 應用程式互動時尋找熱重載、多行程式碼編輯和自動完成嗎?請查看 Tinkerwell!
使用方式
Tinker 允許您在命令列上與整個 Laravel 應用程式互動,包括您的 Eloquent 模型、任務、事件等。要進入 Tinker 環境,請執行 tinker Artisan 指令:
php artisan tinker
您可以使用 vendor:publish 指令來發佈 Tinker 的設定檔:
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"
[!WARNING]
dispatch輔助函數和Dispatchable類別上的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.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 屬性來定義指令的簽章和描述。Signature 屬性還允許您定義指令的輸入預期。當您的指令被執行時,handle 方法將被呼叫。您可以將指令邏輯放在此方法中。
讓我們來看看一個範例指令。請注意,我們可以透過指令的 handle 方法請求任何我們需要的依賴項。Laravel 服務容器將自動注入在此方法簽章中型別提示的所有依賴項:
<?php
namespace App\Console\Commands;
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
#[Signature('mail:send {user}')]
#[Description('Send a marketing email to a user')]
class SendEmails extends Command
{
/**
* 執行控制台指令。
*/
public function handle(DripEmailer $drip): void
{
$drip->send(User::find($this->argument('user')));
}
}
[!NOTE] 為了更好的程式碼重用,最佳實踐是保持控制台指令簡潔,並讓它們委託給應用程式服務來完成任務。在上面的範例中,請注意我們注入了一個服務類別來完成發送電子郵件的「繁重工作」。
退出代碼
如果 handle 方法沒有回傳任何內容且指令成功執行,指令將以 0 退出代碼退出,表示成功。然而,handle 方法可以選擇性地回傳一個整數來手動指定指令的退出代碼:
$this->error('發生了某些錯誤。');
return 1;
如果您想要從指令中的任何方法「失敗」該指令,您可以使用 fail 方法。fail 方法將立即終止指令的執行並回傳退出代碼 1:
$this->fail('發生了某些錯誤。');
閉包指令
基於閉包的指令提供了將控制台指令定義為類別的替代方案。就像路由閉包是控制器的替代方案一樣,您可以將指令閉包視為指令類別的替代方案。
即使 routes/console.php 檔案沒有定義 HTTP 路由,它也定義了基於控制台的進入點(路由)。在此檔案中,您可以使用 Artisan::command 方法定義所有基於閉包的控制台指令。command 方法接受兩個參數:指令簽章和一個閉包,該閉包接收指令的參數和選項:
Artisan::command('mail:send {user}', function (string $user) {
$this->info("正在將電子郵件發送至:{$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('向使用者發送行銷電子郵件');
可隔離指令
[!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
鎖 ID
預設情況下,Laravel 將使用指令的名稱來生成用於在應用程式的快取中獲取原子鎖的字串鍵。然而,您可以透過在 Artisan 指令類別上定義 isolatableId 方法來自訂此鍵,允許您將指令的參數或選項整合到鍵中:
/**
* 取得指令的可隔離 ID。
*/
public function isolatableId(): string
{
return $this->argument('user');
}
鎖過期時間
預設情況下,隔離鎖在指令完成後過期。或者,如果指令被中斷且無法完成,鎖將在一小時後過期。然而,您可以透過在指令上定義 isolationLockExpiresAt 方法來調整鎖過期時間:
use DateTimeInterface;
use DateInterval;
/**
* 決定指令的隔離鎖何時過期。
*/
public function isolationLockExpiresAt(): DateTimeInterface|DateInterval
{
return now()->plus(minutes: 5);
}
定義輸入預期
編寫控制台指令時,通常需要透過參數或選項從使用者那裡收集輸入。Laravel 使得使用指令上的 signature 屬性來定義您從使用者那裡預期的輸入變得非常方便。signature 屬性允許您在單個表達力強的、類路由的語法中定義指令的名稱、參數和選項。
參數
所有使用者提供的參數和選項都包裹在花括號中。在以下範例中,指令定義了一個必需參數:user:
/**
* 控制台指令的名稱和簽章。
*
* @var string
*/
protected $signature = 'mail:send {user}';
您還可以使參數可選或為參數定義預設值:
// 可選參數...
'mail:send {user?}'
// 帶有預設值的可選參數...
'mail:send {user=foo}'
選項
選項與參數一樣,是使用者輸入的另一種形式。當透過命令列提供選項時,它們以兩個連字元(--)為前綴。有兩種類型的選項:接受值的選項和不接受值的選項。不接受值的選項充當布林「開關」。讓我們來看看這種類型選項的範例:
/**
* 控制台指令的名稱和簽章。
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue}';
在此範例中,可以在呼叫 Artisan 指令時指定 --queue 開關。如果傳遞了 --queue 開關,選項的值將為 true。否則,值將為 false:
php artisan mail:send 1 --queue
帶有值的選項
接下來,讓我們來看看一個期望值的選項。如果使用者必須為選項指定值,您應該在選項名稱後加上 = 號:
/**
* 控制台指令的名稱和簽章。
*
* @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
輸入描述
您可以透過使用冒號將參數名稱與描述分隔開來,為輸入參數和選項分配描述。如果您需要更多空間來定義指令,請隨時將定義分散在多行中:
/**
* 控制台指令的名稱和簽章。
*
* @var string
*/
protected $signature = 'mail:send
{user : 使用者的 ID}
{--queue : 任務是否應被佇列化}';
提示缺少的輸入
如果您的指令包含必需參數,當未提供時使用者將收到錯誤訊息。或者,您可以透過實現 PromptsForMissingInput 介面來設定您的指令在缺少必需參數時自動提示使用者:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
class SendEmails extends Command implements PromptsForMissingInput
{
/**
* 控制台指令的名稱和簽章。
*
* @var string
*/
protected $signature = 'mail:send {user}';
// ...
}
如果 Laravel 需要從使用者那裡收集必需參數,它將透過使用參數名稱或描述智能地措辭問題來自動向使用者詢問參數。如果您想要自訂用於收集必需參數的問題,您可以實現 promptForMissingArgumentsUsing 方法,回傳一個以參數名稱為鍵的問題陣列:
/**
* 使用回傳的問題提示缺少的輸入參數。
*
* @return array<string, string>
*/
protected function promptForMissingArgumentsUsing(): array
{
return [
'user' => '哪個使用者 ID 應該收到郵件?',
];
}
您還可以透過使用包含問題和佔位符的元組來提供佔位符文本:
return [
'user' => ['哪個使用者 ID 應該收到郵件?', '例如 123'],
];
如果您想要完全控制提示,您可以提供一個應該提示使用者並回傳其答案的閉包:
use App\Models\User;
use function Laravel\Prompts\search;
// ...
return [
'user' => fn () => search(
label: '搜尋使用者:',
placeholder: '例如 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;
// ...
/**
* 在使用者被提示缺少參數後執行操作。
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
$input->setOption('queue', confirm(
label: '您想要佇列化郵件嗎?',
default: $this->option('queue')
));
}
指令 I/O
檢索輸入
當您的指令執行時,您通常需要存取指令接受的參數和選項的值。為此,您可以使用 argument 和 option 方法。如果參數或選項不存在,將回傳 null:
/**
* 執行控制台指令。
*/
public function handle(): void
{
$userId = $this->argument('user');
}
如果您需要將所有參數作為 array 檢索,請呼叫 arguments 方法:
$arguments = $this->arguments();
選項可以像參數一樣輕鬆地使用 option 方法檢索。要將所有選項作為陣列檢索,請呼叫 options 方法:
// 檢索特定選項...
$queueName = $this->option('queue');
// 將所有選項作為陣列檢索...
$options = $this->options();
提示輸入
[!NOTE] Laravel Prompts 是一個 PHP 套件,用於為您的命令列應用程式新增美麗且易於使用的表單,具有類瀏覽器的功能,包括佔位符文本和驗證。
除了顯示輸出外,您還可以在指令執行期間要求使用者提供輸入。ask 方法將提示使用者輸入給定的問題,接受他們的輸入,然後將使用者的輸入回傳給您的指令:
/**
* 執行控制台指令。
*/
public function handle(): void
{
$name = $this->ask('您的名字是什麼?');
// ...
}
ask 方法還接受一個可選的第二個參數,該參數指定如果未提供使用者輸入時應回傳的預設值:
$name = $this->ask('您的名字是什麼?', 'Taylor');
secret 方法類似於 ask,但使用者的輸入在他們在控制台中輸入時對他們不可見。此方法在詢問敏感資訊(如密碼)時非常有用:
$password = $this->secret('密碼是什麼?');
請求確認
如果您需要向使用者詢問簡單的「是或否」確認,您可以使用 confirm 方法。預設情況下,此方法將回傳 false。然而,如果使用者輸入 y 或 yes 作為對提示的回應,該方法將回傳 true。
if ($this->confirm('您想要繼續嗎?')) {
// ...
}
如有必要,您可以透過將 true 作為第二個參數傳遞給 confirm 方法來指定確認提示應預設回傳 true:
if ($this->confirm('您想要繼續嗎?', true)) {
// ...
}
自動完成
anticipate 方法可用於為可能的選擇提供自動完成。使用者仍然可以提供任何答案,無論自動完成提示如何:
$name = $this->anticipate('您的名字是什麼?', ['Taylor', 'Dayle']);
或者,您可以將閉包作為第二個參數傳遞給 anticipate 方法。每次使用者輸入一個輸入字元時,都會呼叫該閉包。閉包應接受一個包含使用者到目前為止的輸入的字串參數,並回傳一個用於自動完成的選項陣列:
use App\Models\Address;
$name = $this->anticipate('您的地址是什麼?', function (string $input) {
return Address::whereLike('name', "{$input}%")
->limit(5)
->pluck('name')
->all();
});
選擇題
如果您需要在提問時給使用者一組預定義的選擇,您可以使用 choice 方法。您可以透過將索引作為第三個參數傳遞給方法來設定如果未選擇選項時應回傳的預設值的陣列索引:
$name = $this->choice(
'您的名字是什麼?',
['Taylor', 'Dayle'],
$defaultIndex
);
此外,choice 方法接受可選的第四和第五個參數,用於確定選擇有效回應的最大嘗試次數以及是否允許多選:
$name = $this->choice(
'您的名字是什麼?',
['Taylor', 'Dayle'],
$defaultIndex,
$maxAttempts = null,
$allowMultipleSelections = false
);
寫入輸出
要將輸出傳送到控制台,您可以使用 line、newLine、info、comment、question、warn、alert 和 error 方法。這些方法中的每一個都將使用適當的 ANSI 顏色。例如,讓我們向使用者顯示一些一般資訊。通常,info 方法將在控制台中以綠色文本顯示:
/**
* 執行控制台指令。
*/
public function handle(): void
{
// ...
$this->info('指令執行成功!');
}
要顯示錯誤訊息,請使用 error 方法。錯誤訊息文本通常以紅色顯示:
$this->error('發生了某些錯誤!');
您可以使用 line 方法來顯示純色、無色文本:
$this->line('在螢幕上顯示此內容');
您可以使用 newLine 方法來顯示空白行:
// 寫入一行空白行...
$this->newLine();
// 寫入三行空白行...
$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();
[!NOTE] 有關更多進階選項,請查看 Symfony 進度條元件文件。
註冊指令
預設情況下,Laravel 會自動註冊 app/Console/Commands 目錄中的所有指令。但是,您可以使用應用程式 bootstrap/app.php 檔案中的 withCommands 方法指示 Laravel 掃描其他目錄中的 Artisan 指令:
->withCommands([
__DIR__.'/../app/Domain/Orders/Commands',
])
如有必要,您還可以透過將指令的類別名稱提供給 withCommands 方法來手動註冊指令:
use App\Domain\Orders\Commands\SendEmails;
->withCommands([
SendEmails::class,
])
當 Artisan 啟動時,應用程式中的所有指令都將由服務容器解析並註冊到 Artisan。
程式化執行指令
有時您可能希望在 CLI 之外執行 Artisan 指令。例如,您可能希望從路由或控制器執行 Artisan 指令。您可以使用 Artisan 外觀上的 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 指令
使用 Artisan 外觀上的 queue 方法,您甚至可以將 Artisan 指令佇列化,以便它們在背景中由佇列 Worker處理。使用此方法之前,請確保您已設定佇列並正在運行佇列監聽器:
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 方法接受指令名稱和指令參數/選項陣列:
/**
* 執行控制台指令。
*/
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 方法:
/**
* 執行控制台指令。
*/
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
});
Stub 自訂
Artisan 控制台的 make 指令用於建立各種類別,例如控制器、任務、遷移和測試。這些類別是使用「stub」檔案生成的,這些檔案根據您的輸入填充值。但是,您可能想要對 Artisan 生成的檔案進行小的更改。為此,您可以使用 stub:publish 指令將最常見的 stub 發佈到您的應用程式,以便您可以自訂它們:
php artisan stub:publish
已發佈的 stub 將位於您應用程式根目錄中的 stubs 目錄中。您對這些 stub 所做的任何更改都將在您使用 Artisan 的 make 指令生成相應類別時反映出來。
事件
Artisan 在運行指令時會分派三個事件:Illuminate\Console\Events\ArtisanStarting、Illuminate\Console\Events\CommandStarting 和 Illuminate\Console\Events\CommandFinished。ArtisanStarting 事件在 Artisan 開始運行時立即分派。接下來,CommandStarting 事件在指令運行之前立即分派。最後,CommandFinished 事件在指令完成執行後分派。