Мы будем использовать фабрику для создания объектов и установки в них зависимостей.
Какие интерфейсы фабрик есть в 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/
Добавить комментарий