- Введение в написание CLI приложения для Joomla 4
- Стандарты Joomla 4
- Плагин для CLI Joomla 4
- Модуль для CLI Joomla 4
- Командная строка CLI Joomla 4
- Крон для CLI Joomla 4
- Проверка 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
Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)