Использование «фабрики» в своих расширениях для Joomla

от автора

Мы будем использовать фабрику для создания объектов и установки в них зависимостей.

Какие интерфейсы фабрик есть в Joomla 5 и какие задачи они выполняют?

  • Joomla\CMS\Cache\CacheControllerFactoryInterface

    • Создание кэш-контроллера;

  • Joomla\CMS\Categories\CategoryFactoryInterface

    • Создание категории;

  • Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface

    • Создание диспечера;

  • Joomla\CMS\Dispatcher\ModuleDispatcherFactoryInterface

    • Создание диспечер модуля;

  • Joomla\CMS\Document\FactoryInterface

    • Создание документа;

    • Создание рендерера документа;

  • Joomla\CMS\Form\FormFactoryInterface

    • Создание формы;

  • Joomla\CMS\Helper\HelperFactoryInterface

    • Получение хелпера;

  • Joomla\CMS\Language\LanguageFactoryInterface

    • Создание языка;

  • Joomla\CMS\Mail\MailerFactoryInterface

    • Создание почтового сервиса;

  • Joomla\CMS\Menu\MenuFactoryInterface

    • Создание меню;

  • Joomla\CMS\MVC\Factory\MVCFactoryInterface

    • Создание контроллера;

    • Создание модели;

    • Создание представления;

    • Создание таблицы;

  • Joomla\CMS\Toolbar\ToolbarFactoryInterface

    • Создание кнопки;

    • Создание тулбара;

  • Joomla\CMS\User\UserFactoryInterface

    • Загружает пользователя по Id;

    • Загружает пользователя по имени;

  • Joomla\CMS\Component\Router\RouterFactoryInterface

    • Создание роутера;

  • Joomla\Plugin\Authentication\Ldap\Factory\LdapFactory

    • Создание Ldap клиента.

Это не полный список фабрик, но из него видим что задача фабрик — «создавать».

Как сделать свою фабрику для своего типа объектов?

Рассмотрим пример создания класса-фабрики для АПИ-клиента службы доставки Сдэк — CdekClientV2Factory. В результате, в моделях компонента и плагинах мы сможем получать АПИ-клиент следующим способом:

$apiClient = $this->getCdekClientV2Factory()->createDefaultClient();

Для этого нам потребуется создать несколько классов:

Интерфейс фабрики:

Root/libraries/wishboxcdek/src/Factory/CdekClientV2FactoryInterface.php

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

namespace WishboxCdekSDK2\Factory;  use WishboxCdekSDK2\CdekClientV2Interface; use function defined;  defined('_JEXEC') or die;  interface CdekClientV2FactoryInterface { public function createDefaultClient(): CdekClientV2Interface; }

Класс фабрики

Root/libraries/wishboxcdek/src/Factory/CdekClientV2Factory.php

Реализует метод для создания АПИ-клиента.

namespace WishboxCdekSDK2\Factory;  use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Component\ComponentHelper; use WishboxCdekSDK2\CdekClientV2; use WishboxCdekSDK2\CdekClientV2Interface;  class CdekClientV2Factory implements CdekClientV2FactoryInterface { use CacheControllerFactoryAwareTrait;  public function createDefaultClient(): CdekClientV2Interface { $componentParams = ComponentHelper::getParams('com_wishboxcdek');  $client = new CdekClientV2( $componentParams->get('account', ''), $componentParams->get('secure', ''), 60.0 );  $client->setCacheControllerFactory($this->getCacheControllerFactory());  return $client; } }

Класс сервис-провайдера для класса фабрики:

Root/libraries/wishboxcdek/src/Service/Provider/CdekClientV2Factory.php

Регистрирует в контейнере метод для получения экземпляра фабрики.

namespace WishboxCdekSDK2\Service\Provider;  use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use WishboxCdekSDK2\Factory\CdekClientV2FactoryInterface; use function defined;  defined('_JEXEC') or die;  class CdekClientV2Factory implements ServiceProviderInterface { public function register(Container $container): void { $container->set( CdekClientV2FactoryInterface::class, function (Container $container) { return new \WishboxCdekSDK2\Factory\CdekClientV2Factory; } ); } } 

Aware-интерфейс фабрики

Root/libraries/wishboxcdek/src/Factory/CdekClientV2FactoryAwareInterface.php

Описывает геттер и сеттер для установки экземпляра фабрики в экземпляр какого-либо объекта. Этот интерфейс должны будут реализовать классы, которые будут работать с нашей фабрикой.

namespace WishboxCdekSDK2\Factory;  use function defined;  defined('_JEXEC') or die;  interface CdekClientV2FactoryAwareInterface { public function setCdekClientV2Factory(CdekClientV2FactoryInterface $cdekClientV2Factory): CdekClientV2FactoryAwareInterface;  public function getCdekClientV2Factory(): CdekClientV2FactoryInterface; } 

Aware-трейт фабрики

Root/libraries/wishboxcdek/src/Factory/CdekClientV2FactoryAwareInterface.php

Реализует геттер и сеттер для установки экземпляра фабрики в экземпляр какого-либо объекта. Будет использоваться вместе с Aware-интерфейсом.

namespace WishboxCdekSDK2\Factory;  use UnexpectedValueException; use function defined;  defined('_JEXEC') or die;  trait CdekClientV2FactoryAwareTrait { private ?CdekClientV2FactoryInterface $cdekClientV2Factory = null;  public function getCdekClientV2Factory(): CdekClientV2FactoryInterface { if (!$this->cdekClientV2Factory) { throw new UnexpectedValueException('CdekClientV2Factory not set in ' . __CLASS__); }  return $this->cdekClientV2Factory; }  public function setCdekClientV2Factory(CdekClientV2FactoryInterface $cdekClientV2Factory): CdekClientV2FactoryAwareInterface { $this->cdekClientV2Factory = $cdekClientV2Factory;  return $this; } } 

Как подготовить расширения для использования своей фабрики

Плагины:

Подключаем к классу плагина Aware-трейт фабрики.

В сервис-провайдере плагина provider.php регистрируем сервис-провайдер фабрики: строка: 17 и устанавливаем экземпляр фабрики в экземпляр плагина: строка: 28.

use Joomla\CMS\Extension\PluginInterface; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use Joomla\Plugin\RadicalMart\WishboxCdekPackageMultiple\Extension\WishboxCdekPackageMultiple; use WishboxCdekSDK2\Factory\CdekClientV2FactoryInterface; use WishboxCdekSDK2\Service\Provider\CdekClientV2Factory;  defined('_JEXEC') or die;  return new class implements ServiceProviderInterface { public function register(Container $container): void { $container->registerServiceProvider(new CdekClientV2Factory);  $container->set( PluginInterface::class, function (Container $container) { $dispatcher = $container->get(DispatcherInterface::class); $config = (array) PluginHelper::getPlugin('radicalmart', 'wishboxcdekpackagemultiple');  $plugin = new WishboxCdekPackageMultiple($dispatcher, $config); $plugin->setApplication(Factory::getApplication()); $plugin->setCdekClientV2Factory($container->get(CdekClientV2FactoryInterface::class));  return $plugin; } ); } }; 

Теперь в классе плагина доступен экземпляр фабрики $this->getCdekClientV2Factory().

В компоненте

В сервис-провайдере компонента provider.php и классе-расширения компонента надо выполнить тоже самое что и в плагине.

В MVC-фабрике компонента

Экземпляры моделей компонента обычно создаются с помощью MVC-фабрики, нам потребуется её переопределить, о том как это сделать я рассказывал в посте.

Для того что-бы MVC-фабрика могла устанавливать экземпляр нашей фабрики в модели, она сама должна им владеть.

Добавим в сервис-провайдер MVC-фабрики регистрацию сервис-провайдера нашей фабрики в контейнере строка: 35 и установим в MVC-фабрику экземпляр нашей фабрики строка: 58.

namespace Joomla\Component\WishboxCdek\Administrator\Extension\Service\Provider;  use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Mail\MailerFactoryInterface; use Joomla\CMS\MVC\Factory\ApiMVCFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Router\SiteRouter; use Joomla\CMS\User\UserFactoryInterface; use Joomla\Component\WishboxCdek\Administrator\Factory\RequestFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use WishboxCdekSDK2\Factory\CdekClientV2FactoryInterface; use WishboxCdekSDK2\Service\Provider\CdekClientV2Factory; use function defined;  defined('_JEXEC') or die;  class MVCFactory extends \Joomla\CMS\Extension\Service\Provider\MVCFactory implements ServiceProviderInterface { private $namespace;  public function __construct(string $namespace) { parent::__construct($namespace);  $this->namespace = $namespace; }  public function register(Container $container): void { $container->registerServiceProvider(new CdekClientV2Factory); $container->registerServiceProvider(new RequestFactory);  $container->set( MVCFactoryInterface::class, function (Container $container) { if (Factory::getApplication()->isClient('api')) { $factory = new ApiMVCFactory($this->namespace); } else { $factory = new \Joomla\Component\WishboxCdek\Administrator\MVC\Factory\MVCFactory($this->namespace); }  $factory->setFormFactory($container->get(FormFactoryInterface::class)); $factory->setDispatcher($container->get(DispatcherInterface::class)); $factory->setDatabase($container->get(DatabaseInterface::class)); $factory->setSiteRouter($container->get(SiteRouter::class)); $factory->setCacheControllerFactory($container->get(CacheControllerFactoryInterface::class)); $factory->setUserFactory($container->get(UserFactoryInterface::class)); $factory->setMailerFactory($container->get(MailerFactoryInterface::class)); $factory->setCdekClientV2Factory($container->get(CdekClientV2FactoryInterface::class)); $factory->setRequestFactory($container->get(RequestFactoryInterface::class));  return $factory; } ); } } 

Теперь перейдём в методу createModel MVC-фабрики. Перед возвращением экземпляра модели MVC-фабрика выполняет серию проверок и установку зависимостей. Добавим проверку на реализацию моделью aware-интерефейса нашей фабрики CdekClientV2FactoryAwareInterface и установку экземпляра строки:190-200.

namespace Joomla\Component\WishboxCdek\Administrator\MVC\Factory;  use Exception; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormFactoryAwareInterface; use Joomla\CMS\Form\FormFactoryAwareTrait; use Joomla\CMS\Mail\MailerFactoryAwareInterface; use Joomla\CMS\Mail\MailerFactoryAwareTrait; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\ModelInterface; use Joomla\CMS\Router\SiteRouterAwareInterface; use Joomla\CMS\Router\SiteRouterAwareTrait; use Joomla\CMS\User\UserFactoryAwareInterface; use Joomla\CMS\User\UserFactoryAwareTrait; use Joomla\Component\WishboxCdek\Administrator\Factory\RequestFactoryAwareInterface; use Joomla\Component\WishboxCdek\Administrator\Factory\RequestFactoryAwareTrait; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use UnexpectedValueException; use WishboxCdekSDK2\Factory\CdekClientV2FactoryAwareInterface; use WishboxCdekSDK2\Factory\CdekClientV2FactoryAwareTrait; use function defined; use function sprintf;  defined('_JEXEC') or die;  class MVCFactory extends \Joomla\CMS\MVC\Factory\MVCFactory implements MVCFactoryInterface, FormFactoryAwareInterface, SiteRouterAwareInterface, UserFactoryAwareInterface, MailerFactoryAwareInterface, CdekClientV2FactoryAwareInterface { use FormFactoryAwareTrait; use DispatcherAwareTrait; use DatabaseAwareTrait; use SiteRouterAwareTrait; use CacheControllerFactoryAwareTrait; use UserFactoryAwareTrait; use MailerFactoryAwareTrait; use CdekClientV2FactoryAwareTrait; use RequestFactoryAwareTrait;  public function createModel($name, $prefix = '', array $config = []) { // Clean the parameters $name   = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_\\\\]/i', '', $prefix);  if (!$prefix) { @trigger_error( sprintf( 'Calling %s() without a prefix is deprecated.', __METHOD__ ), E_USER_DEPRECATED );  $prefix = Factory::getApplication()->getName(); }  if (!mb_strpos($prefix, "\\")) { $className = $this->getClassName('Model\\' . ucfirst($name) . 'Model', $prefix); } else { $className = $this->getClassName(ucfirst($name) . 'Model', $prefix); }  if (!$className) { return null; }  $model = new $className($config, $this);  if ($model instanceof FormFactoryAwareInterface) { try { $model->setFormFactory($this->getFormFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof DispatcherAwareInterface) { try { $model->setDispatcher($this->getDispatcher()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof DispatcherAwareInterface) { try { $model->setDispatcher($this->getDispatcher()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof SiteRouterAwareInterface) { try { $model->setSiteRouter($this->getSiteRouter()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof CacheControllerFactoryAwareInterface) { try { $model->setCacheControllerFactory($this->getCacheControllerFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof CacheControllerFactoryAwareInterface) { try { $model->setCacheControllerFactory($this->getCacheControllerFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof UserFactoryAwareInterface) { try { $model->setUserFactory($this->getUserFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof MailerFactoryAwareInterface) { try { $model->setMailerFactory($this->getMailerFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof DatabaseAwareInterface) { try { $model->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error('Database must be set, this will not be caught anymore in 5.0.', E_USER_DEPRECATED); $model->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } }  if ($model instanceof CdekClientV2FactoryAwareInterface) { try { $model->setCdekClientV2Factory($this->getCdekClientV2Factory()); } catch (UnexpectedValueException $e) { // Ignore it } }  if ($model instanceof RequestFactoryAwareInterface) { try { $model->setRequestFactory($this->getRequestFactory()); } catch (UnexpectedValueException $e) { // Ignore it } }  return $model; } } 

В моделях компонента

Классам модели теперь надо добавить CdekClientV2FactoryAwareInterface и CdekClientV2FactoryAwareTrait.

Готово! Теперь мы можем получать объект фабрики и создавать экземпляр АПИ-клмента с параметрами по-умолчанию: $apiCient = $this->getCdekClientV2Factory()->createDefaultClient();


ссылка на оригинал статьи https://habr.com/ru/articles/906116/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *