Створення консольних команд

Команда — клас, що успадковує Symfony\Component\Console\Command\Command. Залежності передаються через конструктор (автовайринг контейнера); логіка — у методі execute().

Див. також: Робота з консоллю (провайдери та залежності, DisabledCommand), автовайринг.

Документація Symfony: Creating a Command, Input, Output styling.

Мінімальна команда

<?php declare(strict_types=1);

namespace Concept\App\Console\Commands;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

final class GreetCommand extends Command
{
    protected function configure(): void
    {
        $this->setName('app:greet')
            ->setDescription('Print a greeting');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->success('Hello from Concept Core');

        return Command::SUCCESS;
    }
}

Реєстрація в застосунку

Додайте клас у config/commands.php:

return [
    'commands' => [
        // ...команди ядра
        GreetCommand::class,
    ],
];

ConsoleServiceProvider робить $container->get(GreetCommand::class) і addCommand(). Якщо в конструкторі потрібні сервіси — вони резолвляться автоматично через ReflectionContainer.

Якщо залежність не зареєстрована (немає потрібного провайдера в bootstrap/providers/console.php), замість вашої команди з’явиться DisabledCommand — див. консоль → disabled.

php bin/console.php app:greet

Провайдери для вашої команди

Типові залежності власних команд і що підключити в CLI:

Залежність у конструкторіПровайдер у console.php
UserModel, DatabaseInterfaceDatabaseServiceProvider
ConfigInterfaceConfigServiceProvider
RouterInterfaceHttpServiceProvider
LoggerInterfaceLogServiceProvider
сервіс компонентапровайдер компонента + ComponentsServiceProvider

Команда без конструктора (як GreetCommand) залежить лише від ConsoleServiceProvider. Після зміни console.php перезапустіть php bin/console.php list і переконайтеся, що немає червоного «disabled».

Аргументи та опції

Приклад — UserListCommand (user:list):

use Concept\Components\AuthAdmin\Models\UserModel;
use Symfony\Component\Console\Input\InputOption;

class UserListCommand extends Command
{
    private const string COMMAND_NAME = 'user:list';

    protected function configure(): void
    {
        $this->setName(self::COMMAND_NAME)
            ->setDescription('Display a list of registered users')
            ->addOption('limit', 'l', InputOption::VALUE_OPTIONAL, 'How many users?', 10);
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $limit = is_numeric($input->getOption('limit'))
            ? (int) $input->getOption('limit')
            : 10;

        $users = UserModel::query()->limit($limit)->get()->toArray();
        $io->table(array_keys($users[0] ?? []), $users);

        return Command::SUCCESS;
    }
}

Для узгодженості з моделями через DI краще інжектувати UserModel і викликати $this->userModel->newQuery():

public function __construct(
    private readonly UserModel $userModel,
) {
    parent::__construct();
}

Потрібен DatabaseServiceProvider у CLI — інакше команда стане disabled.

Приклад з ядра: DI в команді

RouteListCommand отримує RouterInterface — потрібен HttpServiceProvider:

use Concept\Core\Http\Routing\Contracts\RouterInterface;

public function __construct(private readonly RouterInterface $router)
{
    parent::__construct();
}

DbMigrateCommandMigrator + MigrationRegistry. Складні команди ядра лежать у Concept\Core\Console\Commands.

Команда в компоненті

  1. Створіть Components/MyModule/Commands/MyCommand.php
  2. Додайте клас у commands() компонента
  3. Переконайтеся, що компонент увімкнено в config/components.php
// AuthAdminComponent::commands()
public function commands(): array
{
    return [UserListCommand::class];
}

Під час ComponentsServiceProvider::boot() команди резолвяться з контейнера і додаються в ConsoleApplication через addCommand(). ConsoleServiceProvider має бути зареєстрований раніше у bootstrap/providers/console.php.

Якщо команда компонента потребує БД або HTTP-сервісів — у CLI все одно потрібні відповідні провайдери ядра (див. таблицю вище).

Вивід і помилки

$io = new SymfonyStyle($input, $output);

$io->title('Import started');
$io->writeln('Processing...');
$io->success('Done');
$io->warning('Partial import');
$io->error('Failed');

// таблиця
$io->table(['Column A', 'Column B'], $rows);

// progress (довгі цикли)
$io->progressStart(count($items));
foreach ($items as $item) {
    // ...
    $io->progressAdvance();
}
$io->progressFinish();

При винятку повертайте Command::FAILURE або дозвольте Symfony вивести stack trace (у dev з Whoops для CLI — plain text).

Чеклист

  1. Унікальне ім’я команди (module:action, наприклад billing:sync)
  2. configure() — name, description, arguments/options; константа COMMAND_NAME як у ядрі
  3. execute() — коротка orchestration, важке в сервісі; Command::SUCCESS / FAILURE
  4. Реєстрація в config/commands.php або в commands() компонента
  5. Провайдери CLI покривають усі залежності конструктора
  6. Перевірка: php bin/console.php list (без «disabled») і help your:command

Не дублюйте HTTP-контролери в CLI: винесіть спільну логіку в Services/ і викличте її і з web, і з command. Команда «disabled» — це майже завжди відсутній провайдер у console.php, а не помилка в execute().