Artisan команды Laravel: создание, настройка и выполнение

Раздел: Фреймворки PHP -> Laravel

Artisan - это интерфейс командной строки, встроенный в Laravel. Он предоставляет множество встроенных команд для миграций, кэширования, генерации кода и других задач. Однако разработчики часто создают собственные команды для автоматизации бизнес-логики. Ниже рассматривается основной способ создания команды, а также альтернативные варианты с примерами и разбором типичных проблем.

Создание и настройка Artisan команд

Наиболее эффективное решение: использование генератора make:command и описание логики в методе handle. Этот способ поддерживается всеми версиями Laravel и обеспечивает минимальное количество шаблонного кода.


php artisan make:command SendEmails

После выполнения будет создан файл app/Console/Commands/SendEmails.php. Внутри необходимо задать сигнатуру ($signature) и описание ($description), а в методе handle - бизнес-логику.


<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SendEmails extends Command
{
    protected $signature = 'email:send {user} {--queue}';
    protected $description = 'Отправка email пользователю';

    public function handle()
    {
        $userId = $this->argument('user');
        $useQueue = $this->option('queue');
        $this->info("Отправка письма пользователю {$userId}, очередь: " . ($useQueue ? 'да' : 'нет'));
    }
}

Пояснение шагов: сигнатура email:send {user} {--queue} указывает, что команда называется email:send, принимает обязательный аргумент user и опцию без значения --queue. В методе handle аргумент получается через $this->argument('user'), опция - через $this->option('queue'). Метод info выводит сообщение зелёным цветом.

Типичная ошибка: если в сигнатуре допущена опечатка, команда не будет зарегистрирована и появится ошибка Command "email:send" is not defined. Решение - проверить файл app/Console/Kernel.php (команды регистрируются автоматически, если класс находится в пространстве имён App\Console\Commands).

Как добавить необязательные аргументы и опции со значениями?

Аргумент можно сделать необязательным, добавив вопросительный знак: {user?}. Для опции со значением используется двойное тире и знак равенства: {--timeout=30} (значение по умолчанию 30).


protected $signature = 'report:generate {start?} {--format=html}';

В handle проверяется наличие аргумента через $this->argument('start') - если он не был передан, вернётся null. Опция format вернёт 'html' по умолчанию.

Ошибка: если опция объявлена как {--format} (без значения), то при передаче --format=pdf будет использовано значение true, а не 'pdf'. Для получения значения нужно обязательно указать знак равенства в сигнатуре.

Как выполнить команду из кода приложения?

Использование фасада Artisan позволяет запускать команды внутри контроллеров, очередей или других команд.


use Illuminate\Support\Facades\Artisan;

$exitCode = Artisan::call('email:send', [
    'user' => 42,
    '--queue' => true,
]);

Первый параметр - имя команды, второй - массив аргументов и опций. Возвращается код выхода (0 - успех, 1 - ошибка). Если нужно получить вывод, используется Artisan::output().

Проблема: при вызове команды внутри другой команды могут возникнуть конфликты с буферизацией вывода. Решение - временно отключить буферизацию или использовать Artisan::call с флагом --quiet.

Как использовать интерактивные запросы (ask, confirm)?

Методы ask, confirm, secret, choice позволяют взаимодействовать с пользователем в терминале.


public function handle()
{
    $name = $this->ask('Введите имя пользователя:');
    $notify = $this->confirm('Отправить уведомление?', true);
    $this->info("Имя: $name, уведомление: " . ($notify ? 'да' : 'нет'));
}

Вторым параметром можно задать значение по умолчанию. Метод choice возвращает выбранный элемент из списка.

Распространённая ошибка: если команда запускается в неинтерактивном режиме (например, в CRON), ask и confirm вернут значение по умолчанию, и это может привести к неожиданному поведению. Всегда проверяйте, имеет ли команда интерактивный ввод, или используйте опции вместо запросов.

Как скрыть команду из списка php artisan list?

Установите свойство $hidden = true в классе команды.


protected $hidden = true;

Команда останется доступной для выполнения, но не будет отображаться в общем списке. Это полезно для внутренних служебных команд.

Проблема: если команда скрыта, её всё равно можно найти через php artisan help имя_команды. Для полной изоляции используйте кастомные сервис-провайдеры с условной регистрацией.

Как отобразить прогресс выполнения длительных операций?

Прогресс-бар создаётся через метод createProgressBar объекта вывода.


$bar = $this->output->createProgressBar($totalItems);
$bar->start();
foreach ($items as $item) {
    // обработка
    $bar->advance();
}
$bar->finish();

Можно менять формат и добавлять сообщения.

Ошибка в тестах: прогресс-бар использует поток вывода, который может отсутствовать в тестовом окружении. Решение - проверять наличие вывода или отключать прогресс-бар при APP_ENV=testing.

Расширенные примеры Artisan команд

Пример 1: Команда для импорта CSV с прогресс-баром

Команда принимает путь к файлу и опцию размера чанка, обрабатывает строки и отображает прогресс.

Пример

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class ImportCsv extends Command
{
    protected $signature = 'import:csv {file} {--chunk=100}';
    protected $description = 'Импорт CSV файла в базу данных';

    public function handle()
    {
        $file = $this->argument('file');
        $chunkSize = $this->option('chunk');

        if (!file_exists($file)) {
            $this->error('Файл не найден: ' . $file);
            return 1;
        }

        $rows = array_map('str_getcsv', file($file));
        $header = array_shift($rows);
        $total = count($rows);

        $bar = $this->output->createProgressBar($total);
        $bar->start();

        foreach (array_chunk($rows, $chunkSize) as $chunk) {
            $data = [];
            foreach ($chunk as $row) {
                $data[] = array_combine($header, $row);
            }
            DB::table('imports')->insert($data);
            $bar->advance(count($chunk));
        }

        $bar->finish();
        $this->newLine();
        $this->info("Импорт завершён. Всего строк: $total");
    }
}
> php artisan import:csv data.csv --chunk=50
[============================] 100%
Импорт завершён. Всего строк: 500

Пример 2: Вызов одной команды из другой и обработка вывода

Внутри команды можно запустить другую команду и получить её вывод.

Пример

public function handle()
{
    $this->info('Начинаем миграцию...');
    Artisan::call('migrate', ['--force' => true]);
    $output = Artisan::output();
    $this->line($output);

    // Выполняем сиды
    $exitCode = Artisan::call('db:seed', ['--force' => true]);
    if ($exitCode !== 0) {
        $this->error('Сиды не выполнены');
    }
}
Начинаем миграцию...
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.01 seconds)
...

Важно: для использования Artisan::call внутри команды нужно добавить use Illuminate\Support\Facades\Artisan;.

Пример 3: Команда с табличным выводом

Метод table форматирует данные в виде таблицы.

Пример

public function handle()
{
    $headers = ['ID', 'Имя', 'Email'];
    $users = [
        [1, 'Иван', 'ivan@example.com'],
        [2, 'Мария', 'maria@example.com'],
    ];
    $this->table($headers, $users);
}
+----+-------+------------------+
| ID | Имя   | Email            |
+----+-------+------------------+
| 1  | Иван  | ivan@example.com |
| 2  | Мария | maria@example.com|
+----+-------+------------------+

Пример 4: Использование метода confirm с автоматическим ответом

В неинтерактивном режиме (CRON, тесты) можно передать опцию --force и пропустить подтверждение.

Пример

public function handle()
{
    if (!$this->option('force') && !$this->confirm('Вы уверены?')) {
        $this->error('Операция отменена.');
        return 1;
    }
    // выполнение
}

Теперь команду можно запускать с флагом --force, чтобы пропустить интерактивный запрос.

> php artisan dangerous:operation --force

Artisan команды Laravel - comments

En
Php artisan (php)