Console Commands

Giới thiệu

Artisan là giao diện command-line đi theo Laravel. Nó cung cấp một danh sách các câu lệnh hữu ích để sử dụng trong quá trình phát triển sản phẩm. Artisan được phát triển dựa trên component Symfony Console khá mạnh mẽ. Để xem danh sách các câu lệnh được cung cấp, bạn có thể sử dụng câu lệnh list command:

php artisan list

Mỗi câu lệnh đều có kèm theo một màn hình "help" để hiển thị và mô tả những đối số và tuỳ chọn có thể sử dụng. Để xem màn hình help, đơn giản chỉ cần gõ tên câu lệnh kèm theo từ khoá help:

php artisan help migrate

Viết Commands

Ngoài việc sử dụng các câu lệnh được cung cấp sẵn, ban cũng có thể tạo câu lệnh riêng để sử dụng cho ứng dụng của bạn. Bạn có thể lưu trữ các câu lệnh riêng đó trong thư mục app/Console/Commands; tuy nhiên, bạn thoải mái trong việc chọn vị trí lưu đặt mã nguồn các câu lệnh với điều kiện là phải khai báo tự động khởi tạo trong cấu hình của.

Tạo Commands

Để tạo một câu lệnh mới, bạn có thể sử dụng câu lệnh make:command. nó sẽ tạo ra các khung mã nguồn cơ bản để giúp bạn bắt đầu một cách dễ dàng hơn app/Console/Commands. Đừng lo lắng nếu thư mục không có trong project, vì nó sẽ được tạo trong lần chạy đầu tiên make:command. Nó sẽ thêm vào các thuộc tính mặc định và phương thức có mặt trên tất cả các lệnh:

php artisan make:command SendEmails

Cấu trúc command

Khi mà câu lệnh được tạo ra, bạn nên điền vào thông tin của hai thuộc tính signaturedescription trong class, vì chúng sẽ được dùng để hiển thị khi mà câu lệnh list được thực thi. Hàm handle sẽ được gọi khi command được thực thi. Bạn có thể viết logic vào trong hàm này.

Để việc tái sử dụng code được tốt hơn, nó là cách tốt để giữu cho console commands sáng và để cho defer ứng dụng services để thực hiện task của mình. Trong ví dụ bên dưới, chú ý là chúng inject một class service để làm "heavy lifting" gửi e-mails.

Hãy xem một ví vụ command. Chú ý là chúng ta có thể inject bất kỳ dependencies chúng ta cần vàm hàm constructor. Laravel service container sẽ tự động inject tất cả dependencies type-hinted trong constructor:

<?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';

    /**
     * The drip e-mail service.
     *
     * @var  DripEmailer
     */
    protected $drip;

    /**
     * Create a new command instance.
     *
     * @param    DripEmailer  $drip
     * @return  void
     */
    public function __construct(DripEmailer $drip)
    {
        parent::__construct();

        $this->drip = $drip;
    }

    /**
     * Execute the console command.
     *
     * @return  mixed
     */
    public function handle()
    {
        $this->drip->send(User::find($this->argument('user')));
    }
}

Closure Commands

Closure based commands cung cấp một cách định nghĩa khác của console commands như classes. Giống như route Closures là thay thế cho các controllers, nghĩ command Closures như là một thay thể của command classes. Trong hàm commands của file app/Console/Kernel.php, Laravel loads 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 được định nghĩa HTTP routes, nó định nghĩa console based entry points (routes) vào ứng dụng. Bên trong file này, bạn có thể định nghĩa tất cả Closure based routes sử dụng hàm Artisan::command. Hàm command chấp nhận hai tham số: command signature và một Closure nhận tham số và tùy chọn:

Artisan::command('build {project}', function ($project) {
    $this->info("Building {$project}!");
});

The Closure là ràng buộc để các command instance, vì vậy bạn có thể hoàn toàn truy cập các hàm helper bạn muốn truy cập vào một full command class.

Type-Hinting Dependencies

Ngoài ra để nhận tham số command và options, command Closures ngoài ra có thể type-hint thêm các dependencies bạn muốn để giải quyết khỏi service container:

use App\User;
use App\DripEmailer;

Artisan::command('email:send {user}', function (DripEmailer $drip, $user) {
    $drip->send(User::find($user));
});

Miêu tả Closure Command

Khi bạn định nghĩa một Closure based command, bạn có thể sử dụng hàm describe để thêm miêu tả cho command. Miêu tả này sẽ hiện khi chạy lệnh php artisan list hoặc php artisan help:

Artisan::command('build {project}', function ($project) {
    $this->info("Building {$project}!");
})->describe('Build the project');

Defining Input Expectations

Khi viết console commands, người dùng thường thêm input qua . Laravel lamf cho việc đó khá thuận tiện để xác định input bạn muốn từ user sử dụng thuộc tính signature trong commands. Thuộc tính signature cho phép bạn định nghĩa tên, đối số, và tùy chọn cho command trong 1 single, expressive, route-like syntax.

Tham số

Tất cả các đối số và tùy chọn được nằm trong cặp ngoặc nhọn. Trong ví dụ dưới đây, command đị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 tham số tùy chọn và định nghĩa giá trị mặc định cho nó:

// Optional argument...
email:send {user?}

// Optional argument with default value...
email:send {user=foo}

Tùy chọn

Tùy chọn, như tham số, là một dạng input khác của người dùng. Nó được bắt đầu bằng hai dấu gạch nối (--) khi họ xác định chúng trên command line. Có hai kiểu tùy chọn: nhận giá trị và không nhận giá trị. Tùy chọn không nhận giá trị như boolean "switch". Hãy xem ví dụ về kiểu tùy chọn:

/**
 * The name and signature of the console command.
 *
 * @var  string
 */
protected $signature = 'email:send {user} {--queue}';

Trong ví dụ này, --queue switch có thể được chỉ định khi gọi Artisan command. Nếu swith --queue được thông qua, giá trị của tùy chọn là true. Ngược lại, giá trị sẽ là false:

php artisan email:send 1 --queue

Tùy chọn với giá trị

Tiếp theo, hãy xem một ví dụ với tùy chọn có giá trị mong đợi. Nếu người dùng phải chỉ định một giá trị cho tùy chọn, hậu tố của tên tùy chọn là ký hiệ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 giá trị tùy chọn tùy ý như:

php artisan email:send 1 --queue=default

Bạn có thể gán giá trị mặc định cho tùy chọn bởi xác định giá trị mặc định sau tên tùy chọn. Nếu không có giá trị truyền vào bởi user, giá trị mặc định sẽ được sử dụng:

email:send {user} {--queue=default}

Shortcuts của tùy chọn

Để gán một shortcut khi định nghĩa một tùy chọn, bạn có thể chỉ định nó trước tên và sử dụng ký tự | để tách shortcut từ tên đầy đủ của tùy chọn:

email:send {user} {--Q|queue}

Input Arrays

Nếu bạn muốn định nghĩa nhiều tham số thành một mảng, bạn có thể sử dụng ký tự *. Đầu tiên, hãy xem ví dụ có một mảng tham số:

email:send {user*}

Khi gọi hàm này, thma số user có thể được truyền vào trong command line. Ví dụ,command sau sẽ đặt giá trị của user thành ['foo', 'bar']:

php artisan email:send foo bar

WKhi định nghĩa một tùy chọn là một array, mỗi giá trị tùy chọn truyền vào command nên được bắt đầu với tên tùy chọn:

email:send {user} {--id=*}

php artisan email:send --id=1 --id=2

Miêu tả đầu vào

Bạn có thể gán miêu tả tham số đầu vào và tùy chọn bởi tách tham số từ miêu tả sử dụng một dấu hai chấm. Nếu bạn cần thêm định nghĩa command, bạn có thể định nghĩa chúng 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}';

Command I/O

Nhận đầu vào

Khi câu lệnh được thực thi, rõ ràng là chúng ta cần lấy được giá trị của các đối số và tuỳ chọn được nhận vào câu lệnh. Để làm được điều này, bạn cần sử dụng tới phương thức argumentoption:

/**
 * Execute the console command.
 *
 * @return  mixed
 */
public function handle()
{
    $userId = $this->argument('user');

    //
}

Nếu bạn cần lấy tất cả đối số truyền vào dưới dạng một array, gọi hàm arguments:

$arguments = $this->arguments();

Tuỳ chọn có thể nhận thông qua phương thức option. để nhận tất cả tùy chọn như một mảng, gọi hàm options:

// Retrieve a specific option...
$queueName = $this->option('queue');

// Retrieve all options...
$options = $this->options();

Nếu như đối số hay tuỳ chọn không tồn tại, giá trị null sẽ được trả về.

Yêu cần nhập Input

Ngoài việc hiển thị, bạn cũng có thể yêu cầu người dùng nhập dữ liệu trong quá trình thực thi câu lệnh. Phương thức ask sẽ yêu cầu người dùng nhập dữ liệu với câu hỏi được đưa ra, nhận dữ liệu và truyền dữ liệu nhập từ người dùng vào trong câu lệnh:

/**
 * 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ư ask, nhưng dữ liệu mà người dùng nhập khi gõ bàn phím sẽ không được hiển thị lên. Phương thức này rất hữu ích khi yêu cầu các thông tin nhạy cảm như mật khẩu:

$password = $this->secret('What is the password?');

Yêu cần xác nhận

Nếu bạn đơn giản chỉ cần một xác nhận từ người sử dụng, bạn có thể sử dụng phương thức confirm. method. Mặc định thì phương thức này trả lại giá trị false. Tuy nhiên, nếu người dùng nhập vào y thì phương thức sẽ trả về true.

if ($this->confirm('Do you wish to continue? [y|N]')) {
    //
}

Auto-Completion

Phương thức anticipate có thể sử dụng để cung cấp tự động hoàn thành câu lệnh với một danh sách các sự gợi ý có thể. Người dùng vẫn có thể đưa ra câu trả lời riêng không liên quan tới các gợi ý được đưa ra:

$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);

Nhiều sự lựa chọn

Nếu như bạn cần đưa ra một danh sách các sự lựa chọn, bạn có thể dùng choice. Bạn có thể đặt giá trị mặc định được trả về nếu giá trị tùy chọn không được chọn:

$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $default);

Viết Output

Để gửi output ra console, sử dụng line, info, comment, questionerror. Mỗi phương thức sẽ sử dụng màu ANSI tương ứng với mục đích của nó. Ví dụ, hãy định nghĩa một vài thông tin cở bản của user. Thông thường, hàm info sẽ hiển thị chữ màu sinh trên console:

/**
 * Execute the console command.
 *
 * @return  mixed
 */
public function handle()
{
    $this->info('Display this on the screen');
}

Để hiển thị nội dung về lỗi, sử dụng error. Các nội dung lỗi sẽ được hiển thị bằng màu đỏ:

$this->error('Something went wrong!');

Nếu bạn muốn hiển thị nội dung đơn thuẩn, sử dụng hàm line:

$this->line('Display this on the screen');

Table Layouts

Phương thức table sẽ giúp cho việc chỉnh hiển thị các dữ liệu kiểu dòng / cột. Chỉ cần truyền vào headers và các dòng nội dung vào trong phương thức. Chiều rộng vào chiều cao sẽ được tự động tính toán căn chỉnh dựa trên dữ liệu đầu vào:

$headers = ['Name', 'Email'];

$users = App\User::all(['name', 'email'])->toArray();

$this->table($headers, $users);

Progress Bars

Với các tác vụ chạy lâu, thì việc sử dụng một thanh tiến trình khá là hữu ích. Sử dụng output, chúng ta có thể khởi tạo, chạy tiến trình và dừng thanh tiến trình. Bạn có thể khai báo số lượng steps khi bắt đầu tiến trình và thực hiện chạy:

$users = App\User::all();

$bar = $this->output->createProgressBar(count($users));

foreach ($users as $user) {
    $this->performTask($user);

    $bar->advance();
}

$bar->finish();

Tham khảo thêm tại Symfony Progress Bar component documentation.

Đăng ký Commands

Khi mà câu lệnh hoàn thành, bạn cần phải đăng kí với Artisan để câu lệnh có thể sử dụng được. Việc này sẽ thông qua file app/Console/Kernel.php. Trong file này, bạn có thể thấy một danh sách câu lệnh trong thuộc tính commands. Để đăng kí, đơn giản chỉ cần thêm tên class của câu lệnh vào trong danh sách. Khi Artisan khởi tạo, tất cả các câu lệnh trong danh sách này sẽ được duyệt bởi service container và sẽ được đăng kí vào Artisan:

protected $commands = [
    Commands\SendEmails::class
];

Gọi câu lệnh trên mã nguồn

Đôi lúc bạn muốn thực thi một câu lệnh Artisan nằm ngoài CLI. Ví dụ, bạn muốn gọi một câu lệnh Artisan từ một route hay controller. Bạn có thể sử dụng call trong Artisan facade để thực hiện việc này. Phương thức call nhận tên của câu lệnh vào trong đối số đầu tiên, và một mảng danh sách các tham số thực thi câu lệnh ở đối số thứ hai. Mã kết quả thực thi sẽ được trả lại:

Route::get('/foo', function () {
    $exitCode = Artisan::call('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    //
});

Khi sử dụng queue trong Artisan facade, thì bạn của thể thực hiện câu lệnh trên hàng đợi và chúng sẽ được thực hiện ở background bởi queue workers. Trước khi sử dụng hàm này, đảm bảo bạn có cấu hình queue và đang chạy queue listener:

Route::get('/foo', function () {
    Artisan::queue('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    //
});

Nếu bạn cần chỉ rõ giá trị của tuỳ chọn không nhận kiểu string, ví dụ --force trong lệnhmigrate:refresh, bạn có thể truyền vào giá trị boolean true hoặc false:

$exitCode = Artisan::call('migrate:refresh', [
    '--force' => true,
]);

Thực thi câu lệnh từ câu lệnh khác

Đôi lúc bạn muốn thực thi gọi câu lệnh từ một câu lệnh Artisan khác. Bạn có thể sử dụng phương thức call. Hàm call chấp nhận tên của lệnh và một mảng tham số:

/**
 * Execute the console command.
 *
 * @return  mixed
 */
public function handle()
{
    $this->call('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    //
}

Nếu bạn muốn thực thi một câu lệnh và chặn không muốn hiển thị nội dung của nó ra ngoài, bạn có thể dùng callSilent. Hàm callSilent này sử dụng tương tự hàm call:

$this->callSilent('email:send', [
    'user' => 1, '--queue' => 'default'
]);
Nguồn: https://laravel.com/docs/5.3/artisan