Artisan 命令行
Artisan 命令行
Artisan 命令行
簡(jiǎn)介
Artisan 是 Laravel 自帶的命令行接口,他提供了許多使用的命令來幫助你構(gòu)建 Laravel 應(yīng)用 。要查看所有可用的 Artisan 命令的列表,可以使用 list
命令:
php artisan list
每個(gè)命令都包含了「幫助」 界面 ,它會(huì)顯示和概述命令的可用參數(shù)及選項(xiàng)。只需要在命令前加上 help
即可查看命令幫助界面 :
php artisan help migrate
Tinker 命令 (REPL)
所有 Laravel 應(yīng)用都包含了 Tinker,一個(gè)基于 PsySH 包提供支持的 REPL 。Tinker 讓你可以在命令行中與你整個(gè)的 Laravel 應(yīng)用進(jìn)行交互 。 包括 Eloquent ORM、任務(wù)、事件等等。運(yùn)行 Artisan 命令 tinker
進(jìn)入 Tinker 環(huán)境:
php artisan tinker
你可以通過使用 vendor:publish
命令發(fā)布 Tinker 配置文件:
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"
命令白名單
Tinker 通過采用白名單的方式來確定允許哪些 Artisan 命令可以在 shell 中運(yùn)行。默認(rèn)情況下,你可以運(yùn)行 clear-compiled
、down
、env
、 inspire
、migrate
、 optimize
、和 up
命令。如果你想要添加更多的白名單命令,可以將它們添加到 tinker.php
配置文件中的 commands
數(shù)組里:
'commands' => [ // App\Console\Commands\ExampleCommand::class, ],
黑名單別名
通常,Tinker 會(huì)在 Tinker 中根據(jù)你的需要自動(dòng)為類添加別名。然而,你可能不希望為某些類添加別名。你可以在 tinker.php
配置文件中的 dont_alias
數(shù)組里列舉這些類來完成此操作:
'dont_alias' => [ App\User::class, ],
編寫命令
除 Artisan 提供的命令外,你還可以構(gòu)建自己的自定義命令。 命令通常存儲(chǔ)在 app/Console/Commands
目錄中;不過,只要你的命令可以由 Composer 加載,你就可以自由選擇自己的存儲(chǔ)位置。
生成命令
要?jiǎng)?chuàng)建一個(gè)新的命令,可以使用 Artisan 命令 make:command
。這個(gè)命令會(huì)在 app/Console/Commands
目錄中創(chuàng)建一個(gè)新的命令類。 不必?fù)?dān)心應(yīng)用中不存在這個(gè)目錄,因?yàn)樗鼤?huì)在你第一次運(yùn)行 Artisan 命令 make:command
時(shí)創(chuàng)建。生成的命令會(huì)包括所有命令中默認(rèn)存在的屬性和方法:
php artisan make:command SendEmails
命令結(jié)構(gòu)
命令運(yùn)行后,你應(yīng)該先填寫 signature
和 description
屬性以便你在輸入 php artisan list
時(shí)能夠清楚知道用法。執(zhí)行命令時(shí)會(huì)調(diào)用 handle
方法,你可以在這個(gè)方法中放置命令邏輯。
{tip} 為了代碼更好的復(fù)用,最好保持你的控制臺(tái)代碼輕量并且能夠延遲到應(yīng)用服務(wù)中完成。在下面的例子中,我們將注入一個(gè)服務(wù)類來完成發(fā)送郵件的重任。
來看一個(gè)簡(jiǎn)單的例子。我們可以在 handle
方法中注入我們需要的任何依賴項(xiàng)。 Laravel 服務(wù)容器 將會(huì)自動(dòng)注入所有在構(gòu)造函數(shù)中帶類型約束的依賴:
<?php namespace App\Console\Commands; use App\User;use App\DripEmailer; use Illuminate\Console\Command; class SendEmails extends Command{ /** * The name and signature of the console command. * * @var string */ protected $signature = 'email:send {user}'; /** * The console command description. * * @var string */ protected $description = 'Send drip e-mails to a user'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @param \App\DripEmailer $drip * @return mixed */ public function handle(DripEmailer $drip) { $drip->send(User::find($this->argument('user'))); } }
閉包命令
基于閉包的命令提供了一個(gè)用類代替定義控制臺(tái)命令的方法。同理,閉包路由是控制器的一種替代方法,而閉包命令則可以認(rèn)為是命令類的替代方法。在 app/Console/Kernel.php
文件的 commands
方法中,Laravel 加載了 routes/console.php
file:
/** * Register the Closure based commands for the application. * * @return void */ protected function commands(){ require base_path('routes/console.php'); }
雖然這個(gè)文件沒有定義 HTTP 路由,但是它將基于控制臺(tái)的入口點(diǎn)(路由)定義到了應(yīng)用中。在這個(gè)文件,你可以使用 Artisan::command
方法定義所有的閉包路由。 command
方法接受兩個(gè)參數(shù): 命令名稱 和一個(gè)接受命令參數(shù)及選項(xiàng)的閉包:
Artisan::command('build {project}', function ($project) { $this->info("Building {$project}!"); });
閉包綁定底層的命令實(shí)例,因?yàn)槟憧梢栽L問所有能夠在完整命令類中的所有輔助方法。
類型提示依賴
除了接收命令的參數(shù)和選項(xiàng)外,命令閉包也可以使用類型提示從 服務(wù)容器 中解析其他的依賴關(guān)系:
use App\User; use App\DripEmailer;Artisan::command('email:send {user}', function (DripEmailer $drip, $user) { $drip->send(User::find($user)); });
閉包命令描述
當(dāng)你定義一個(gè)閉包命令,你應(yīng)當(dāng)使用 describe
方法來為命令添加描述。這個(gè)描述會(huì)在你運(yùn)行 php artisan list
或者 php artisan help
命令時(shí)顯示:
Artisan::command('build {project}', function ($project) { $this->info("Building {$project}!"); })->describe('Build the project');
定義輸出期望
在編寫控制臺(tái)命令時(shí),通常是通過參數(shù)和選項(xiàng)來收集用戶輸入的。Laravel 可以通過 signature
屬性非常方便的定義你期望用戶輸入的內(nèi)容。signature
允許使用單一且可讀性高,類似路由的語法定義命令的名稱、參數(shù)和選項(xiàng)。
參數(shù)
所有用戶提供的參數(shù)及選項(xiàng)都被包含在花括號(hào)中。在下面的例子中,這個(gè)命令定義了一個(gè) 必須的參數(shù): user
:
/** * The name and signature of the console command. * * @var string */ protected $signature = 'email:send {user}';
你也可以創(chuàng)建可選參數(shù),并定義參數(shù)的默認(rèn)值:
// 可選參數(shù)... email:send {user?} // 帶有默認(rèn)值的可選參數(shù)... email:send {user=foo}
選項(xiàng)
選項(xiàng),類似于參數(shù),是用戶輸入的另一種格式。當(dāng)命令行指定選項(xiàng)時(shí),它們以兩個(gè)連字符 ( --
) 作為前綴。有兩種類型的選項(xiàng):接收值和不接收值。不接收值的選項(xiàng)就像是一個(gè)布爾值的「開關(guān)」。讓我們看一下這種類型的選項(xiàng)的例子:
/** * 命令行的名稱及簽名。 * * @var string */ protected $signature = 'email:send {user} {--queue}';
在這個(gè)例子中,可以在調(diào)用 Artisan 命令時(shí)指定 --queue
開關(guān)。如果 --queue
開關(guān)被傳遞,該選項(xiàng)的值為 true
, 否則為 false
:
php artisan email:send 1 --queue
帶值的選項(xiàng)
接下來,讓我們來看一個(gè)帶值的選項(xiàng)。如果想讓用戶必須為選項(xiàng)指定一個(gè)值,則需要在選項(xiàng)名稱末尾追加一個(gè)等號(hào) =
作為的后綴:
/** * 命令行的名稱及簽名。 * * @var string */ protected $signature = 'email:send {user} {--queue=}';
在這個(gè)例子中, 用戶可以傳遞該選項(xiàng)的值,如下所示:
php artisan email:send 1 --queue=default
你也可以通過在選項(xiàng)名稱后面指定默認(rèn)值來設(shè)定該選項(xiàng)的默認(rèn)值。如果用戶沒有傳遞選項(xiàng)值,將使用設(shè)定的默認(rèn)值:
email:send {user} {--queue=default}
選項(xiàng)簡(jiǎn)寫
要在定義選項(xiàng)時(shí)指定簡(jiǎn)寫,你可以在選項(xiàng)名稱前指定它,并用 |
分隔符將簡(jiǎn)寫與完整選項(xiàng)名稱分隔開:
email:send {user} {--Q|queue}
輸入數(shù)組
如果你想定義接收數(shù)組輸入的參數(shù)或選項(xiàng),你可以使用 *
符號(hào)。首先,我們先看一個(gè)數(shù)組參數(shù)的實(shí)例:
email:send {user*}
調(diào)用此方法時(shí),該 user
參數(shù)的輸入?yún)?shù)可按順序傳遞給命令行。例如,以下命令會(huì)設(shè)置 user 的值為 ['foo', 'bar']
:
php artisan email:send foo bar
當(dāng)定義接收數(shù)組的選項(xiàng)時(shí),傳遞給命令行的每個(gè)選項(xiàng)值都應(yīng)以選項(xiàng)名稱為前綴:
email:send {user} {--id=*} php artisan email:send --id=1 --id=2
輸入說明
你可以通過使用冒號(hào)來為參數(shù)和選項(xiàng)添加說明,并使其將他們隔開。如果你需要一點(diǎn)額外的空間來定義你的命令,可以隨意分開在多個(gè)行里:
/** * 命令行的名稱及簽名。 * * @var string */ protected $signature = 'email:send {user : The ID of the user} {--queue= : Whether the job should be queued}';
Command I/O
獲取輸入
在命令執(zhí)行時(shí),顯然你需要獲取命令接收到的參數(shù)和選項(xiàng)的值。你可以用 argument
和 option
方法來達(dá)到目的:
/** * 執(zhí)行命令。 * * @return mixed */ public function handle(){ $userId = $this->argument('user'); // }
如果你想所有的參數(shù)以 array
數(shù)組獲取,可以調(diào)用 arguments
方法:
$arguments = $this->arguments();
和獲取參數(shù)類似,option
方法可以非常容易的獲取選項(xiàng)的值。要將所有的選項(xiàng)以數(shù)組獲取,使用 options
方法:
// 獲取一個(gè)指定的選項(xiàng)值 $queueName = $this->option('queue'); // 獲取所有的選項(xiàng)值 $options = $this->options();
如果參數(shù)或選項(xiàng)不存在,則返回 null
。
交互式輸入
除了顯示輸出外,你還可以要求用戶在命令執(zhí)行時(shí)提供輸入。 ask
方法將提示用戶輸入并接收,然后用戶的輸入將會(huì)傳入你的命令:
/** * 執(zhí)行命令。 * * @return mixed */ public function handle(){ $name = $this->ask('What is your name?'); }
secret
方法和 ask
方法類似,但當(dāng)用戶在控制臺(tái)輸入時(shí)他們的輸入內(nèi)容是不可見的。這個(gè)方法適用于需要用戶輸入像密碼這樣的敏感信息的時(shí)候:
$password = $this->secret('What is the password?');
請(qǐng)求確認(rèn)
如果你想要尋求用戶確認(rèn)一些簡(jiǎn)單的信息,你可以使用 confirm
方法。默認(rèn)情況下,該方法將返回 false
。但如果用戶在回復(fù)中輸入 y
或者 yes
則會(huì)返回 true
。
if ($this->confirm('Do you wish to continue?')) { // }
自動(dòng)補(bǔ)全
anticipate
方法可用于為可能的選擇提供自動(dòng)補(bǔ)全功能。用戶仍然可以忽略自動(dòng)補(bǔ)全的提示,作任意回答:
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
多重選擇
如果你要給用戶提供一些預(yù)設(shè)的選擇,可以使用 choice
方法。你也可以設(shè)置默認(rèn)值的索引,用以應(yīng)對(duì)用戶沒有選擇的情景:
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
編寫輸出
可以使用 line
、 info
、 comment
、 question
和 error
方法來將輸出發(fā)送到終端。每個(gè)方法都使用適當(dāng)?shù)?nbsp;ANSI
顏色表明其目的。例如,讓我們向用戶顯示一些普通信息,通常來說,最好使用 info
方法,它會(huì)在控制臺(tái)將輸出的內(nèi)容顯示為綠色:
/** * 執(zhí)行命令。 * * @return mixed */ public function handle(){ $this->info('Display this on the screen'); }
顯示錯(cuò)誤信息, 使用 error
方法。 錯(cuò)誤信息則會(huì)顯示為紅色:
$this->error('Something went wrong!');
如果你想在控制臺(tái)顯示無顏色設(shè)置的輸出,請(qǐng)使用 line
方法:
$this->line('Display this on the screen');
表格布局
對(duì)于多行列數(shù)據(jù)的格式化輸出,table
方法處理起來更輕松?;趥魅氲谋眍^和行數(shù)據(jù),它會(huì)動(dòng)態(tài)計(jì)算寬高:
$headers = ['Name', 'Email']; $users = App\User::all(['name', 'email'])->toArray(); $this->table($headers, $users);
進(jìn)度條
對(duì)于耗時(shí)任務(wù),提示進(jìn)度非常有必要。使用 output 對(duì)象就可以創(chuàng)建、加載以及停止進(jìn)度條。首先,定義好任務(wù)總步數(shù),然后,在每次任務(wù)執(zhí)行時(shí)加載進(jìn)度條:
$users = App\User::all(); $bar = $this->output->createProgressBar(count($users)); $bar->start();foreach ($users as $user) { $this->performTask($user); $bar->advance(); }$bar->finish();
參閱 Symfony Progress Bar component documentation 獲取更多高級(jí)用法。
注冊(cè)命令
app/Console/Commands
目錄下的命令都會(huì)被注冊(cè),這是由于控制臺(tái)內(nèi)核的 commands
方法調(diào)用了 load
。實(shí)際上,可隨意調(diào)用 load
來掃描其他目錄下的 Artisan 命令:
/** * 注冊(cè)應(yīng)用的命令 * * @return void */ protected function commands(){ $this->load(__DIR__.'/Commands'); $this->load(__DIR__.'/MoreCommands'); // ... }
也可以在 app/Console/Kernel.php
文件的 $commands
屬性中手動(dòng)注冊(cè)命令的類名。Artisan 啟動(dòng)時(shí),這個(gè)屬性列出的命令都將由 服務(wù)容器 解析并通過 Artisan 進(jìn)行注冊(cè):
protected $commands = [ Commands\SendEmails::class ];
程序調(diào)用命令
有時(shí)需要在 CLI 之外執(zhí)行 Artisan 命令,例如,在路由或控制器里觸發(fā) Artisan 命令。要實(shí)現(xiàn)調(diào)用,可以使用 Artisan
門面的 call
方法。call
方法的第一個(gè)參數(shù)接受命令名,第二個(gè)參數(shù)接受數(shù)組形式的命令參數(shù)。退出碼將返回:
Route::get('/foo', function () { $exitCode = Artisan::call('email:send', [ 'user' => 1, '--queue' => 'default' ]); // });
另外,你可以將整個(gè) Artisan 命令作為字符串傳遞給 call
方法:
Artisan::call('email:send 1 --queue=default');
Artisan
門面的 queue
方法可以將 Artisan 命令隊(duì)列化,交由 隊(duì)列工作進(jìn)程 進(jìn)行后臺(tái)處理。使用此方法之前,務(wù)必配置好隊(duì)列以及運(yùn)行隊(duì)列監(jiān)聽器:
Route::get('/foo', function () { Artisan::queue('email:send', [ 'user' => 1, '--queue' => 'default' ]); // });
你也可以指定 Artisan 命令派發(fā)的連接或任務(wù):
Artisan::queue('email:send', [ 'user' => 1, '--queue' => 'default' ])->onConnection('redis')->onQueue('commands');
傳遞數(shù)組值
如果定義了接受數(shù)組的選項(xiàng),可以直接傳遞數(shù)組到該選項(xiàng):
Route::get('/foo', function () { $exitCode = Artisan::call('email:send', [ 'user' => 1, '--id' => [5, 13] ]); });
傳遞布爾值
需要指定沒有選項(xiàng)值的選項(xiàng)時(shí),例如,migrate:refresh
命令的 --force
選項(xiàng),就可以傳入 true
或 false
:
$exitCode = Artisan::call('migrate:refresh', [ '--force' => true, ]);
命令的互相調(diào)用
call
方法可以實(shí)現(xiàn)調(diào)用其它 Artisan 命令。call
方法接受命令名和數(shù)組形式的選項(xiàng):
/** * 執(zhí)行控制臺(tái)命令 * * @return mixed */ public function handle(){ $this->call('email:send', [ 'user' => 1, '--queue' => 'default' ]); // }
如果要抑制控制臺(tái)命令的所有輸出,可以使用 callSilent
方法。callSilent
的使用方法同 call
:
$this->callSilent('email:send', [ 'user' => 1, '--queue' => 'default' ]);