Пример написания CLI - Onoffbydate для Joomla 4

  1. Введение в написание CLI приложения для Joomla 4
  2. Стандарты Joomla 4
  3. Плагин для CLI Joomla 4
    1. onoffbydate.xml
    2. onoffbydate.php
    3. services/provider.php
    4. src/Extension/Onoffbydate.php
    5. Языковые файлы
    6. Командный файл: src/Console/OnoffbydateCommand.php
  4. Модуль для CLI Joomla 4
  5. Командная строка CLI Joomla 4
  6. Крон для CLI Joomla 4
  7. Проверка CLI Joomla 4


Введение в написание CLI приложения для Joomla 4.

Бывают случаи, когда сайту необходимо отображать или скрывать модуль в зависимости от даты. Одним из примеров может быть пользовательский html-модуль, отображающий сообщение в период зимних праздников. Другим примером может быть чередование пользовательских модулей в зависимости от дня недели. Например, один для будних дней, а другой для выходных. Представленный ниже пример использует плагин, команду cli и cron для достижения желаемого эффекта.

Код доступен на github: https://github.com/ceford/j4xdemos-plg-onoffbydate.

Стандарты Joomla 4.

В ранних версиях Joomla система плагинов использовала реализацию паттерна Observable/Observer. В результате, каждый загружаемый плагин немедленно регистрировал все свои публичные методы как наблюдатели. Это могло привести к проблемам.

Joomla 4 использует пакет Joomla Framework Event для обработки событий плагинов. Это обеспечивает лучшую производительность и безопасность. На практике это означает, что файловая структура плагина Joomla 4 отличается от структуры плагина Legacy более ранних версий. А макет веб-приложения, скорее всего, будет отличаться от cli приложения, как показано ниже.

Плагин для CLI Joomla 4.

Плагин назван onoffbydate, потому что именно это он и делает. На данный момент плагин используется как системный (system), но со временем он, вероятно, заслуживает нового типа cli. Файлы:

onoffbydate.xml

Это установочный файл - стандартный элемент для любого расширения Joomla 4.

<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
        <name>plg_system_onoffbydate</name>
        <author>Clifford E Ford</author>
        <creationDate>October 2021</creationDate>
        <copyright>(C) Clifford E Ford</copyright>
        <license>GNU General Public License version 3 or later</license>
        <authorEmail>cliff@ford.myzen.co.uk</authorEmail>
        <version>0.2.0</version>
        <description>PLG_SYSTEM_ONOFFBYDATE_DESCRIPTION</description>
        <namespace path="src">Joomla\Plugin\System\Onoffbydate</namespace>
        <files>
                <filename plugin="onoffbydate">onoffbydate.php</filename>
                <folder>services</folder>
                <folder>src</folder>
        </files>
        <languages>
                <language tag="en-GB">language/en-GB/plg_system_onoffbydate.ini</language>
                <language tag="en-GB">language/en-GB/plg_system_onoffbydate.sys.ini</language>
        </languages>
        <config>
        </config>
</extension>

Обратите внимание, в частности, на строку namespace. Она указывает Joomla 4, где найти код с пространством имен для этого плагина.

onoffbydate.php

Это стандартная точка входа для плагина Legacy, но она совсем не используется для плагина Joomla 4. Однако он должен присутствовать в установщике Joomla. Без него установка невозможна.

<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.onoffbydate
 *
 * @copyright   (C) 2021 Clifford E Ford.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;

/**
 * Этот класс не используется плагином, но должен присутствовать
 * чтобы удовлетворить программу установки.
 * Без него плагин не будет установлен.
 */

class Plgsystemonoffbydate extends CMSPlugin
{

}

services/provider.php

Это точка входа для кода плагина.

<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.onoffbydate
 *
 * @copyright   (C) 2021 Clifford E Ford.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') || die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\System\Onoffbydate\Extension\Onoffbydate;

return new class implements ServiceProviderInterface {
        /**
         * Регистрирует поставщика услуг в контейнере DI.
         *
         * @param   Container  $container  Контейнер DI.
         *
         * @return  void
         *
         * @since   4.0.0
         */
        public function register(Container $container)
        {
                $container->set(
                                PluginInterface::class,

                                function (Container $container) {
                                        $subject = $container->get(DispatcherInterface::class);
                                        $config  = (array) PluginHelper::getPlugin('system', 'onoffbydate');

                                        return new Onoffbydate($subject, $config);
                                }
                                );

        }
};

Обратите внимание на вызов для создания нового класса Onoffbydate new Onoffbydate. Он находится в папке src/Extension - стандартное место для загрузки расширения Joomla 4.

src/Extension/Onoffbydate.php

Это место, где находится код инициализации плагина.

<?php
/**
 * @package     Joomla.Console
 * @subpackage  Onoffbydate
 *
 * @copyright   Copyright (C) 2005 - 2021 Clifford E Ford. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Plugin\System\Onoffbydate\Extension;

\defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Plugin\System\Onoffbydate\Console\OnoffbydateCommand;

class Onoffbydate extends CMSPlugin
{

        protected $app;

        public function __construct(&$subject, $config = [])
        {
                parent::__construct($subject, $config);

                if (!$this->app->isClient('cli'))
                {
                        return;
                }

                $this->registerCLICommands();
        }

        public static function getSubscribedEvents(): array
        {
                if ($this->app->isClient('cli'))
                {
                        return [
                                Joomla\Application\ApplicationEvents\ApplicationEvents::BEFORE_EXECUTE => 'registerCLICommands',
                        ];
                }
        }

        public function registerCLICommands()
        {
                $commandObject = new OnoffbydateCommand;
                $this->app->addCommand($commandObject);
        }
}

Обратите внимание на вызов для создания нового класса OnoffbydateCommand new OnoffbydateCommand. Он находится в папке src/Console. Консоль - это личный выбор - встроенные в Joomla cli команды находятся в папке Console. Проверка на isClient('cli') необходима, иначе этот плагин убьет сайт. Если это произойдет, найдите его в таблице #__extensions с помощью phpMyAdmin и установите значение enabled равным 0.

Языковые файлы.

Во время установки и настройки плагина используются два языковых файла. Они не рассматриваются здесь. Пожалуйста, посмотрите код на github.

Заметьте также, что вывод команды виден только вам на вашей установке Joomla, поэтому, вероятно, не стоит делать вывод переводимым. Похоже, так обстоит дело с большинством команд cli ядра Joomla 4.

Командный файл: src/Console/OnoffbydateCommand.php

<?php
/**
 * @package     Joomla.Console
 * @subpackage  Onoffbydate
 *
 * @copyright   Copyright (C) 2005 - 2021 Clifford E Ford. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Plugin\System\Onoffbydate\Console;

\defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;

class OnoffbydateCommand extends AbstractCommand
{
        /**
         * Имя команды по умолчанию
         *
         * @var    string
         *
         * @since  4.0.0
         */
        protected static $defaultName = 'onoffbydate:action';

        /**
         * @var InputInterface
         * @since version
         */
        private $cliInput;

        /**
         * SymfonyStyle Object
         * @var SymfonyStyle
         * @since 4.0.0
         */
        private $ioStyle;

        /**
         * Создание команды.
         *
         * @since   4.0.0
         */
        public function __construct()
        {
                parent::__construct();
        }

        /**
         * Конфигурирует вход-выход
         *
         * @param   InputInterface   $input   Консольный ввод
         * @param   OutputInterface  $output  Консольный вывод
         *
         * @return void
         *
         * @since 4.0.0
         *
         */
        private function configureIO(InputInterface $input, OutputInterface $output)
        {
                $this->cliInput = $input;
                $this->ioStyle = new SymfonyStyle($input, $output);
        }

        /**
         * Инициализация команды.
         *
         * @return  void
         *
         * @since   4.0.0
         */
        protected function configure(): void
        {
                $this->addArgument('action',
                                InputArgument::REQUIRED,
                                'name of action');

                $this->addArgument('module_id',
                                InputArgument::REQUIRED,
                                'module id');

                $help = "<info>%command.name%</info> Toggles module Enabled/Disabled state
                        \nUsage: <info>php %command.full_name% action_id module_id
                        \nwhere action_id is one of winter or weekend</info>";

                $this->setDescription('Called by cron to set the enabled state of a module.');
                $this->setHelp($help);

        }

        /**
         * Внутренняя функция для выполнения команды.
         *
         * @param   InputInterface   $input   Входные данные для введения в команду.
         * @param   OutputInterface  $output  Вывод для вставки в команду.
         *
         * @return  integer  Код возврата команды
         *
         * @since   4.0.0
         */
        protected function doExecute(InputInterface $input, OutputInterface $output): int
        {
                $this->configureIO($input, $output);

                $action = $this->cliInput->getArgument('action');
                $module_id = $this->cliInput->getArgument('module_id');

                switch ($action) {
                        case 'winter' :
                                $result = $this->winter($module_id);
                                break;
                        case 'weekend' :
                                $result = $this->weekend($module_id);
                                break;
                        default:
                                $this->ioStyle->error("Unknwon action: {$action}");
                                return 0;
                }

                return 1;
        }

        protected function weekend($module_id)
        {
                // узнать день недели
                $day = date('w');
                if (in_array($day, array(0,6)))
                {
                        $msg = "Today is a weekend.";
                        $published = 1;
                }
                else
                {
                        $msg = "Today is not a weekend.";
                        $published = 0;
                }

                $this->publish($module_id, $published);

                $state = empty($published) ? 'Unpublished' : 'Published';

                $this->ioStyle->success("That seemed to work. {$msg} Module {$module_id} has been {$state}");

        }

        protected function winter($module_id)
        {
                // получить номер месяца
                $month = date('n');
                if (in_array($month, array(1,2,11,12)))
                {
                        $msg = "Today is in winter.";
                        $published = 0;
                }
                else
                {
                        $msg = "Today is not in winter.";
                        $published = 1;
                }

                $this->publish($module_id, $published);

                $state = empty($published) ? 'Unpublished' : 'Published';

                $this->ioStyle->success("That seemed to work. {$msg} Module {$module_id} has been {$state}");

        }

        protected function publish ($module_id, $published)
        {
                $db = Factory::getDbo();
                $query = $db->getQuery(true);
                $query->update('#__modules')
                ->set('published = ' . $published)
                ->where('id = ' . $module_id);
                $db->setQuery($query);
                $db->execute();
        }
}

Функция configure устанавливает, что требуется два аргумента командной строки:

  • Ключевое слово действия
  • ID модуля

Функция doExecute - это место, где выполняется работа. Проиллюстрированы два варианта действий:

  • winter вызывает функцию для публикации модуля в зимние месяцы.
  • weekend вызывает функцию для публикации модуля, если дата выпадает на выходной день.

Очевидно, что вы можете добавить столько действий, связанных с датой, сколько вам нужно.

Модуль для CLI Joomla 4.

Этот код просто изменяет опубликованное состояние модуля в зависимости от некоторой функции даты. Итак, вам нужен идентификатор модуля, как показано в правой колонке страницы списка Modules (Site).

код просто изменяет опубликованное состояние модуля в зависимости от некоторой функции даты

Командная строка CLI Joomla 4.

Если код работает, вы увидите onoffbydate среди списка доступных команд. Откройте окно терминала и введите:

php cli/joomla.php list

Если на этом этапе что-то пойдет не так, проверьте, что вызванная версия php - это версия командной строки, а не версия, используемая веб-сервером. Если среди списка команд вы видите onoffbydate, вы можете вызвать справку, чтобы узнать, как ее следует использовать:

php cli/joomla.php onoffbydate:action --help     
Usage:
  onoffbydate:action <action> <module_id>

Arguments:
  action                name of action
  module_id             module id

Options:
  -h, --help            Display the help information
  -q, --quiet           Flag indicating that all output should be silenced
  -V, --version         Displays the application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Flag to disable interacting with the user
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  onoffbydate:action Toggles module Enabled/Disabled state
                                
  Usage: php cli/joomla.php onoffbydate:action action_id module_id
                                
  where action_id is one of winter or weekend

И затем просто попробуйте:И затем просто попробуйте:

php cli/joomla.php onoffbydate:action winter 130


 [OK] That seemed to work. Today is not in winter. Module 130 has been Published

Крон для CLI Joomla 4.

Команду можно проверить в окне терминала, но вы, вероятно, захотите использовать ее из cron. Опция winter может быть запущена в первый день каждого месяца. Опция oddday будет выполняться ежедневно. Важным моментом является то, что вы можете иметь столько кронов, сколько вам нужно, чтобы изменять опубликованное состояние любого количества модулей через любые подходящие промежутки времени. Один и тот же код работает для всех.

На хостинге вам необходимо указать полные пути к исполняемому файлу php и команде joomla cli. Пример:

/usr/local/bin/php /home/username/public_html/pathtojoomla/cli/joomla.php onoffbydate:action winter 130

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

Проверка CLI Joomla 4.

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

Clifford E Ford
Перевод с английского официальной документации Joomla 4:
https://docs.joomla.org/J4_CLI_example_-_Onoffbydate

Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)

Старт! Горячий старт на просторы интернета
Старт! Горячий старт на просторы интернета
Старт! Меню