Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Консольные команды

Импорт данных, рассылки, очистку старых записей запускают из терминала или по расписанию. Для таких задач пишут собственные команды bin/console. Команда — тоже сервис: ей доступны репозитории, почта и всё остальное.

Создание

php bin/console make:command app:send-report
// src/Command/SendReportCommand.php
namespace App\Command;
 
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
 
#[AsCommand(name: 'app:send-report', description: 'Отправляет недельный отчёт')]
class SendReportCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->success('Отчёт отправлен.');
 
        return Command::SUCCESS;
    }
}
php bin/console app:send-report

Имена своих команд принято начинать с app:.

Аргументы и опции

Их описывают в методе configure():

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
 
protected function configure(): void
{
    $this
        ->addArgument('format', InputArgument::REQUIRED, 'Формат файла')
        ->addOption('limit', null, InputOption::VALUE_REQUIRED, 'Сколько записей', 100)
        ->addOption('active', null, InputOption::VALUE_NONE, 'Только активные');
}
 
protected function execute(InputInterface $input, OutputInterface $output): int
{
    $format = $input->getArgument('format');
    $limit = (int) $input->getOption('limit');
    $onlyActive = $input->getOption('active');
 
    return Command::SUCCESS;
}
php bin/console app:export-users csv --limit=500 --active

Оформление вывода

Класс SymfonyStyle даёт готовые элементы:

$io = new SymfonyStyle($input, $output);
 
$io->title('Экспорт пользователей');
$io->text('Обычная строка');
$io->warning('Часть записей пропущена');
$io->error('Файл не найден');
$io->table(['Имя', 'Почта'], [['Анна', 'anna@example.com']]);

Вопросы

$name = $io->ask('Как назвать файл?', 'users.csv');
 
if (!$io->confirm('Удалить старые записи?', false)) {
    return Command::SUCCESS;
}

Индикатор выполнения

foreach ($io->progressIterate($users) as $user) {
    $this->export($user);
}

Сервисы в команде

Зависимости передают через конструктор:

public function __construct(
    private UserRepository $users,
    private MailerInterface $mailer,
) {
    parent::__construct();
}

Запуск по расписанию

На сервере команду добавляют в планировщик cron:

0 9 * * 1 php /var/www/blog/bin/console app:send-report

Эта строка запускает отчёт каждый понедельник в 9:00.

Частая ошибка

Собственный конструктор команды обязан вызвать parent::__construct(). Без этого команда не получит имя, и консоль завершится ошибкой. И не забывайте возвращать Command::FAILURE при сбое: по коду завершения cron и системы размещения понимают, что операция не удалась.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.