Знакомство с созданием консольных команд: cron.

05.05.2022 в 16:51
6435
+271

Знакомство с командами

Когда вы только познакомились с фреймворком Symfony (или это был Laravel?), вы могли заметить, как много всего можно сделать в проекте, используя консольные команды. Вы можете создать базу, сущность, контроллер, форму, вотер (о которых в ближайших уроках будет рассказано), целый CRUD, можете обновить таблицы, загрузить фикстуры, запустить встроенный веб-сервер, очистить кэш, продебажить контейнер, любой другой компонент фреймворка и многое другое. И это даже не все, что может Symfony. Но самое интересное - это то, что фреймворк позволяет расширять пул консольных команд вашими собственными. Давайте напишем собственную команду, а также запустим крон, который будет выполнять ее каждые 2 минуты.

Чтобы создать консольную команду в Symfony, вам нужно всего лишь создать собственный класс, который будет наследовать класс Command из Symfony\Component\Console\Command. Также вам нужно определить метод execute(). Это именно тот метод, который будет запускаться после вызова вашей команды. А чтобы команду зарегистрировать, вам нужно реализовать метод configure(), где указать имя, описание (опционально) и аргументы (опционально). Итак, простая реализация будет выглядеть следующим образом:

<?php

declare(strict_types=1);

namespace App\Application\Command;

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

class UserCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('user:create')
            ->setDescription('Create a test user.');
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
    }
}

Если вы выполните в консоли команду php bin/console, то увидите следующее:

В самом низу вы можете увидеть нашу команду и описание к ней. Давайте сделаем нашу команду умнее:

<?php

declare(strict_types=1);

namespace App\Application\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class UserCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('user:create')
            ->setDescription('Create a test user.')
            ->addArgument('email', InputArgument::REQUIRED)
            ->addArgument('password', InputArgument::REQUIRED)
            ->addArgument('name', InputArgument::OPTIONAL);
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        $email = $input->getArgument('email');
        $password = $input->getArgument('password');
        $name = $input->getArgument('name');

        $output->writeln(
            sprintf('Электронный адрес - %s, Пароль - %s, Имя - %s', $email, $password, $name ?? 'default')
        );
    }
}

Чтобы наша команда начала принимать какие-то аргументы, вы должны это определить в методе конфигурации, добавив вызов методов addArgument(), куда передав название аргумента и вторым параметром тип принимаемого аргументы - обязательный, опциональный или массив. В методе execute() мы достаем эти параметры и показываем их пользователю обратно. Чтобы посмотреть на результат выполнения этой команды, выполните в консоли следующее:

php bin/console user:create [email protected] password123, yourname 

Именно так, по порядку, вы передаете аргументы нашей команде. Однако незнакомому с нашей командой человеку может быть непонятно, в каком порядке передавать аргументы, или же вы сами забудете порядок. В этом случае можно делать подсказки пользователю, когда и что вводить. Например, теперь наш код будет таким:

<?php

declare(strict_types=1);

namespace App\Application\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;

class UserCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('user:create')
            ->setDescription('Create a test user.');
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        $questionHelper = $this->getHelper('question');

        $email = $questionHelper->ask($input, $output, new Question('<info>Email: </info>'));
        $password = $questionHelper->ask($input, $output, new Question('<info>Password: </info>'));
        $name = $questionHelper->ask($input, $output, new Question('<info>Name: </info>'));

        $output->writeln(
            sprintf('<comment>Имя пользователя - %s, электронный адрес - %s, пароль - %s</comment>',
                $name, $email, $password
            )
        );

    }
}

С помощью метода getHelper() мы достали один из таких хелперов, а именно - question. С помощью команды ask он позволяет спрашивать у пользователя данные. Вы могли заметить, что мы использовали разметку <info> и <comment>, благодаря им код будет подсвечиваться в консоли, как пример ниже:

Cron

С помощью композера скачайте следующий пакет:

composer require mybuilder/cronos-bundle

Когда бандл скачается, в папке config создайте файл cronos.yaml и сохраните там следующие настройки:

my_builder_cronos:
  exporter:
    key: unique-key
    mailto: [email protected]
    path: /bin:/usr/local/bin
    executor: /usr/bin/php
    console: /var/www/html/phpzone/bin/console
    shell: /bin/bash

Что нас здесь интересует, так это директивы executor и console, в которых мы указываем путь до нашего php, которым вы обычно запускаете консольные команды Symfony, и путь до консоли текущего проекта. То есть при запуске крона команда будет выглядеть следующим образом:

/usr/bin/php /var/www/html/phpzone/bin/console user:create

Чтобы Symfony увидела настройки крона, импортируйте их в файл services.yaml в самом начале:

Чтобы ваша текущая команда стала крон-командой, вам всего лишь нужно определить это в аннотации над классом:

/**
 * @Cron(minute="/2", noLogs=true)
 */
class UserCommand extends Command

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

php bin/console cronos:dump

Вы должны получить примерно следующее:

Однако это не значит, что теперь крон заработал. Чтобы этот пакет запускал наши команды в установленное время, нам нужно иметь рабочий crontab. Если на данный момент у вас нет ни одного кронтаба, создайте его командой crontab -e и добавьте туда совершенно любой крон. Чтобы проверить работу крона, создадим простой менеджер, который просто будет записывать в какую-нибудь таблицу (назовем ее events) сообщение о том, что команда выполнилась:

<?php

declare(strict_types=1);

namespace App\Application\Command;

use App\Application\Manager\AbstractManager;

class UserManager extends AbstractManager
{
    public function recordEvent(string $username, string $data)
    {
        $sql = '
            INSERT INTO
                events (username, data, isRead)
            VALUES
                (:username, :data, 0)
        ';

        $this->getConnection()
            ->prepare($sql)
            ->execute([
                'username' => $username,
                'data' => $data
            ]);
    }
}

Теперь прокинем этот менеджер через конструктор в команду и в метод execute() вызовем recordEvent:

<?php

declare(strict_types=1);

namespace App\Application\Command;

use MyBuilder\Bundle\CronosBundle\Annotation\Cron;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @Cron(minute="/2", noLogs=true)
 */
class UserCommand extends Command
{
    /**
     * @var UserManager
     */
    private $userManager;

    public function __construct(UserManager $userManager)
    {
        parent::__construct();
        $this->userManager = $userManager;
    }

    protected function configure()
    {
        $this
            ->setName('user:create')
            ->setDescription('Create a test user.');
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        $this->userManager->recordEvent(
            'User',
            'Событие произошло'
        );
    }
}

Теперь когда у нас готовы и команда, и крон, выполните команду, чтобы наш пакет заменил существующий кронтаб нужными нам командами:

php bin/console cronos:replace

Вы должны увидеть примерно следующее:

Если вы не допустили никаких ошибок, через 2 минуты вы увидите в таблице новую запись.

Выводы

Итак, на этом уроке мы познакомились и с консольными командами, и с тем, как с их помощью выполнять отложенные задачи. Вы можете с помощью это команды доставать письма из базы и делать по ним рассылку, очищать спам и многое другое.

loader
05.05.2022 в 16:51
6435
+271
Логические задачи с собеседований