Artisan là một giao diện dòng lệnh đi kèm với Laravel. Nó cung cấp một số lệnh hữu ích có thể hỗ trợ bạn trong khi bạn xây dựng application. Để xem danh sách tất cả các lệnh Artisan có sẵn, bạn có thể sử dụng lệnh list
:
php artisan list
Mỗi lệnh cũng chứa một lệnh "help" để hiển thị và mô tả các tùy chọn và các tham số dành cho lệnh đó. Để xem lệnh help, hãy set help
vào trước tên của command:
php artisan help migrate
Tất cả các application của Laravel đều chứa Tinker, một REPL cung cấp bởi package PsySH. Tinker cho phép bạn tương tác trực tiếp với toàn bộ application Laravel của bạn trên command line, bao gồm ORM Eloquent, job, event, vv... Để vào được môi trường Tinker, hãy chạy lệnh Artisan tinker
:
php artisan tinker
Bạn có thể export file cấu hình của Tinker bằng lệnh vendor:publish
:
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"
Tinker có sử dụng một danh sách trắng để xác định các lệnh Artisan nào được phép chạy. Mặc định, bạn có thể chạy các lệnh clear-compiled
, down
, env
, inspire
, migrate
, optimize
, và up
. Nếu bạn muốn thêm các lệnh khác, bạn có thể thêm chúng vào mảng commands
trong file cấu hình tinker.php
của bạn:
'commands' => [
// App\Console\Commands\ExampleCommand::class,
],
Thông thường, Tinker sẽ tự động đặt bí danh cho các class khi bạn require chúng vào trong Tinker. Tuy nhiên, bạn có thể muốn không đặt bí danh cho một số class. Bạn có thể thực hiện điều này bằng cách thêm các class đó vào trong mảng dont_alias
của file cấu hình tinker.php
của bạn:
'dont_alias' => [
App\User::class,
],
Ngoài các lệnh được cung cấp với Artisan, bạn cũng có thể tự xây dựng các lệnh của riêng bạn. Các lệnh thường được lưu trữ trong thư mục app/Console/Commands
; tuy nhiên, bạn cũng có thể thoải mái chọn vị trí lưu trữ mà bạn muốn, miễn là các lệnh của bạn có thể load được bởi Composer.
Để tạo một lệnh mới, sử dụng lệnh Artisan make:command
. Lệnh này sẽ tạo một class command mới trong thư mục app/Console/Commands
. Đừng lo lắng nếu thư mục này không tồn tại trong application của bạn, vì nó sẽ được tạo vào lần đầu tiên bạn chạy lệnh Artisan make:command
. Command được tạo ra sẽ có mặc định các thuộc tính và các phương thức mà đều có trên mỗi command:
php artisan make:command SendEmails
Sau khi đã tạo xong command, bạn hãy thay đổi các thuộc tính signature
và description
, command sẽ được sử dụng các thuộc tính đó để hiển thị thông tin command của bạn trên màn hình list
. Phương thức handle
sẽ được gọi khi lệnh của bạn được thực thi. Bạn có thể cài đặt logic của bạn vào trong phương thức này.
{tip} Để code của bạn có thể tái sử dụng tốt hơn, thì cách tốt nhất là giữ cho các command của bạn được "nhẹ" và hãy để các application service hoàn thành nhiệm vụ đó cho bạn. Trong ví dụ dưới đây, hãy chú ý rằng chúng ta sẽ inject một service class để thực hiện một "công việc nặng" như việc gửi e-mail.
Chúng ta hãy xem một ví dụ về command. Lưu ý rằng chúng ta có thể inject bất kỳ service nào mà chúng ta muốn vào hàm handle
của command. Laravel service container sẽ tự động inject tất cả các phụ thuộc đã được khai báo có trong phương thức đó:
<?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')));
}
}
Các command được tạo dựa trên closure sẽ cung cấp thêm một giải pháp để định nghĩa các command. Giống như cách mà các closure route làm, là tạo thêm một cách định nghĩa cho controller, bạn hãy nghĩ các closure command này như là một cách định nghĩa khác cho các class command, thay vì phải tạo ra một file command mới. Trong phương thức commands
ở trong file app/Console/Kernel.php
của bạn, Laravel sẽ load sẵn file routes/console.php
:
/**
* Register the Closure based commands for the application.
*
* @return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
Mặc dù file này không định nghĩa các HTTP route, nhưng nó định nghĩa các closure dựa theo format của route vào trong application của bạn. Trong file này, bạn có thể định nghĩa tất cả các closure dựa trên route của bạn bằng phương thức Artisan::command
. Phương thức command
chấp nhận hai tham số: một là một command signature và hai là một closure để nhận vào các tham số và các option của command:
Artisan::command('build {project}', function ($project) {
$this->info("Building {$project}!");
});
Closure sẽ được liên kết với một instance command cơ bản, nên bạn có toàn quyền truy cập vào tất cả các phương thức helper mà bạn thường dùng trên một class command cơ bản.
Ngoài việc nhận vào các tham số và các option của command, Closure command cũng có thể khai báo thêm các phụ thuộc mà bạn muốn resolve từ service container:
use App\User;
use App\DripEmailer;
Artisan::command('email:send {user}', function (DripEmailer $drip, $user) {
$drip->send(User::find($user));
});
Khi định nghĩa một command dựa trên Closure, bạn có thể sử dụng phương thức describe
để thêm mô tả cho command. Mô tả này sẽ được hiển thị khi bạn chạy lệnh php artisan list
hoặc lệnh php artisan help
:
Artisan::command('build {project}', function ($project) {
$this->info("Building {$project}!");
})->describe('Build the project');
Khi viết một lệnh console, thường thu nhận các dữ liệu đầu vào từ người dùng thông qua các tham số hoặc các option. Laravel làm cho nó rất thuận tiện để xác định đầu vào mà bạn mong muốn từ người dùng bằng cách sử dụng thuộc tính signature
trên mỗi lệnh của bạn. Thuộc tính signature
cho phép bạn định nghĩa tên, tham số và các option cho lệnh theo một cú pháp đơn giản, dễ hiểu, giống như cú pháp trên route.
Tất cả các tham số và các tùy chọn do người dùng cung cấp được wrap trong một dấu ngoặc nhọn. Trong ví dụ sau, lệnh sẽ định nghĩa một tham số bắt buộc: user
:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'email:send {user}';
Bạn cũng có thể tạo ra tham số tùy chọn và định nghĩa giá trị mặc định cho các tham số đó:
// Optional argument...
email:send {user?}
// Optional argument with default value...
email:send {user=foo}
Tùy chọn, giống như một tham số, là một dạng khác của input user. Các tùy chọn sẽ được gán tiền tố với hai dấu gạch nối (--
) khi chúng được định nghĩa trên dòng lệnh. Có hai loại tùy chọn: loại tùy chọn nhận một giá trị và loại tuỳ chọn không nhận giá trị nào. Các tùy chọn không nhận giá trị đóng vai trò như là một "switch" boolean. Chúng ta hãy xem một ví dụ về loại tùy chọn này:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'email:send {user} {--queue}';
Trong ví dụ này, switch --queue
có thể được chỉ định khi gọi lệnh Artisan. Nếu switch --queue
được thông qua, giá trị của tùy chọn sẽ là true
. Nếu không, giá trị sẽ là false
:
php artisan email:send 1 --queue
Tiếp theo, chúng ta hãy xem một tùy chọn nhận một giá trị. Nếu người dùng phải chỉ định một giá trị cho một tùy chọn, thì hãy thêm hậu tố vào tên của tùy chọn đó bằng dấu =
:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'email:send {user} {--queue=}';
Trong ví dụ này, người dùng có thể truyền một giá trị cho tùy chọn đó như sau:
php artisan email:send 1 --queue=default
Bạn cũng có thể gán một giá trị mặc định cho các tùy chọn này bằng cách chỉ định giá trị mặc định sau tên mỗi tùy chọn. Nếu không có giá trị tùy chọn nào được người dùng truyền vào, thì giá trị mặc định sẽ được sử dụng:
email:send {user} {--queue=default}
Để gán một shortcut khi định nghĩa một tùy chọn, bạn có thể chỉ định nó vào phía trước tên của một tùy chọn và sử dụng một dấu | để tách shortcut khỏi toàn bộ tên tùy chọn:
email:send {user} {--Q|queue}
Nếu bạn muốn định nghĩa các tham số hoặc tùy chọn để nhận vào một mảng, bạn có thể sử dụng ký tự *
. Đầu tiên, chúng ta hãy xem một ví dụ định nghĩa một tham số là một mảng:
email:send {user*}
Khi gọi phương thức này, các tham số user
có thể được truyền theo dòng lệnh. Ví dụ: lệnh sau sẽ set giá trị của user
thành ['foo', 'bar']
:
php artisan email:send foo bar
Khi định nghĩa một tùy chọn nhận vào một mảng, thì mỗi giá trị tùy chọn được truyền vào cho lệnh nên thêm một tiền tố cho mỗi tên tùy chọn:
email:send {user} {--id=*}
php artisan email:send --id=1 --id=2
Bạn có thể gán một mô tả cho các input đầu vào như tham số hoặc tùy chọn bằng cách tách tham số đó ra khỏi mô tả bằng dấu hai chấm. Nếu bạn cần thêm một chút chỗ trống để định nghĩa thêm cho lệnh của mình, hãy định nghĩa nó trên nhiều dòng:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'email:send
{user : The ID of the user}
{--queue= : Whether the job should be queued}';
Trong khi lệnh của bạn đang thực thi, rõ ràng bạn sẽ cần truy cập vào các giá trị của các tham số và các tùy chọn đã được khai báo trong lệnh của bạn. Để làm như vậy, bạn có thể sử dụng các phương thức argument
và option
:
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$userId = $this->argument('user');
//
}
Nếu bạn cần lấy ra tất cả các tham số dưới dạng một array
, hãy gọi phương thức arguments
:
$arguments = $this->arguments();
Các tùy chọn có thể được lấy ra dễ dàng như các tham số bằng cách sử dụng phương thức option
. Để lấy tất cả các tùy chọn dưới dạng một mảng, hãy gọi phương thức options
:
// Retrieve a specific option...
$queueName = $this->option('queue');
// Retrieve all options...
$options = $this->options();
Nếu tham số hoặc tùy chọn không tồn tại, null
sẽ được trả về.
Ngoài việc hiển thị output, bạn cũng có thể yêu cầu người dùng cung cấp thêm thông tin trong quá trình đang thực thi lệnh. Phương thức ask
sẽ hỏi người dùng với một câu hỏi có sẵn, chấp nhận thông tin nhập thêm của người dùng và sau đó truyền lại thông tin mới nhập thêm đó cho lệnh của bạn:
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$name = $this->ask('What is your name?');
}
Phương thức secret
tương tự như phương thức ask
, nhưng đầu vào của người dùng sẽ không được hiển thị cho họ khi họ gõ vào console. Phương thức này hữu ích khi yêu cầu thông tin nhạy cảm như mật khẩu:
$password = $this->secret('What is the password?');
Nếu bạn cần yêu cầu người dùng xác nhận, bạn có thể sử dụng phương thức confirm
. Mặc định, phương thức này sẽ trả về false
. Tuy nhiên, nếu người dùng nhập y
hoặc yes
để trả lời confirm, phương thức sẽ trả về true
.
if ($this->confirm('Do you wish to continue?')) {
//
}
Phương thức anticipate
có thể được sử dụng để cung cấp một auto-completion cho các lựa chọn. Người dùng vẫn có thể chọn bất kỳ câu trả lời nào, cho dù có gợi ý auto-completion:
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
Nếu bạn cần cung cấp cho người dùng một danh sách các lựa chọn để người dùng chọn, thì bạn có thể sử dụng phương thức choice
. Bạn có thể set giá trị mặc định cho phương thức này thông qua index của mảng, và nó sẽ được trả về nếu người dùng không chọn bất kỳ tuỳ chọn nào của bạn:
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
Để gửi một output đến console, hãy sử dụng các phương thức line
, info
, comment
, question
và error
. Mỗi phương thức này sẽ sử dụng một màu ANSI thích hợp cho mục đích của chúng. Ví dụ: Để hiển thị một thông tin chung cho người dùng. Thì thông thường, phương thức info
sẽ hiển thị trong console dưới dạng màu xanh lá cây:
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Display this on the screen');
}
Để hiển thị một thông báo lỗi, sử dụng phương thức error
. Thông báo lỗi đó sẽ được hiển thị màu đỏ:
$this->error('Something went wrong!');
Nếu bạn muốn hiển thị giao diện output đơn giản không màu, hãy sử dụng phương thức line
:
$this->line('Display this on the screen');
Phương thức table
sẽ giúp bạn dễ dàng định dạng chính xác những dữ liệu mà có nhiều hàng hoặc nhiều cột. Chỉ cần truyền vào các tiêu đề và các dòng dữ liệu cho phương thức. Chiều rộng và chiều cao sẽ được tính toán linh hoạt dựa trên dữ liệu được đưa vào:
$headers = ['Name', 'Email'];
$users = App\User::all(['name', 'email'])->toArray();
$this->table($headers, $users);
Đối với các tác vụ chạy dài, bạn có thể cần hiển thị một tiến trình phần trăm. Sử dụng đối tượng output, chúng ta có thể bắt đầu, tiến và dừng thanh tiến trình. Đầu tiên, định nghĩa tổng số các bước mà tiến trình sẽ lặp. Sau đó, tiến thanh tiến trình sau khi xử lý xong từng bước:
$users = App\User::all();
$bar = $this->output->createProgressBar(count($users));
$bar->start();
foreach ($users as $user) {
$this->performTask($user);
$bar->advance();
}
$bar->finish();
Để biết các tùy chọn nâng cao, hãy xem Tài liệu component Symfony Progress Bar.
Bởi vì phương thức load
đã được gọi trong phương thức commands
trong console kernel của bạn, nên tất cả các command trong thư mục app/Console/Commands
sẽ được tự động đăng ký với Artisan. Trong thực tế, bạn có thể thoải mái thực hiện gọi thêm các phương thức load
để quét các thư mục khác cho các command Artisan mà bạn đã tạo:
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__.'/MoreCommands');
// ...
}
Bạn cũng có thể tự đăng ký các command của bạn bằng cách thêm tên class của command đó vào thuộc tính $commands
trong file app/Console/Kernel.php
. Khi Artisan khởi động, tất cả các lệnh được liệt kê trong thuộc tính này sẽ được resolve bằng service container và được đăng ký với Artisan:
protected $commands = [
Commands\SendEmails::class
];
Thỉnh thoảng bạn có thể muốn chạy một command Artisan bên ngoài CLI. Ví dụ: bạn có thể chạy một command Artisan từ route hoặc controller. Bạn có thể sử dụng phương thức call
trên facade Artisan
để thực hiện điều này. Phương thức call
chấp nhận tên một command hoặc tên một class làm tham số đầu tiên và một mảng các tham số của command đó làm tham số thứ hai. Exit code sẽ được trả về:
Route::get('/foo', function () {
$exitCode = Artisan::call('email:send', [
'user' => 1, '--queue' => 'default'
]);
//
});
Ngoài ra, bạn có thể truyền toàn bộ lệnh Artisan sang phương thức call
dưới dạng một chuỗi:
Artisan::call('email:send 1 --queue=default');
Sử dụng phương thức queue
trên facade Artisan
, bạn thậm chí có thể dùng queue cho các command Artisan để chúng được xử lý trong background, bởi queue workers của bạn. Trước khi sử dụng phương thức này, hãy đảm bảo rằng bạn đã cấu hình queue và đang chạy queue listener:
Route::get('/foo', function () {
Artisan::queue('email:send', [
'user' => 1, '--queue' => 'default'
]);
//
});
Bạn cũng có thể định nghĩa các kết nối hoặc queue mà các command Artisan sẽ được gửi tới:
Artisan::queue('email:send', [
'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');
Nếu command của bạn định nghĩa một tùy chọn là một mảng, bạn có thể truyền một mảng các giá trị cho tùy chọn đó:
Route::get('/foo', function () {
$exitCode = Artisan::call('email:send', [
'user' => 1, '--id' => [5, 13]
]);
});
Nếu bạn cần định nghĩa một giá trị cho một tùy chọn không nhận giá trị, chẳng hạn như một flag --force
trong lệnh migrate:refresh
, bạn có thể truyền true
hoặc false
:
$exitCode = Artisan::call('migrate:refresh', [
'--force' => true,
]);
Thỉnh thoảng bạn có thể muốn gọi các lệnh khác từ một lệnh Artisan hiện có. Bạn có thể làm như vậy bằng cách sử dụng phương thức call
. Phương thức call
này nhận vào tên của command và một mảng các tham số của command đó:
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->call('email:send', [
'user' => 1, '--queue' => 'default'
]);
//
}
Nếu bạn muốn gọi một command khác và xoá đi tất cả các output của nó, bạn có thể sử dụng phương thức callSilent
. Phương thức callSilent
có cùng cách khai báo với phương thức call
:
$this->callSilent('email:send', [
'user' => 1, '--queue' => 'default'
]);
entry