У меня есть библиотека для работы с АПИ Сдэка. Для работы с ней надо создать экземпляр класса WishboxCdekSDK2\CdekClientV2 и вызвать нужный метод.
$apiClient = new CdekClientV2($account, $secure, $timeout); $apiClient->getCities();
Цель данной статьи получать АПИ-клиент в основном компоненте и плагинах следующим образом:
$apiClient = $this->getCdekClientV2();
или в любом другом расширении:
$apiClient = Joomla\CMS\Factory::getApplication() ->bootComponent('com_wishboxcdek')->getCdekClientV2(); $apiClient = Joomla\CMS\Factory::getApplication() ->bootPlugin('wishboxcdek', 'console')->getCdekClientV2();
И в этом нам помогут DI контейнеры.
Давайте пойдём от обратного.
Шаг 1: трейт WishboxCdekSDK2\Trait\CdekClientV2ServiceTrait
Трейт — реализует методы
getCdekClientV2иsetCdekClientV2.
Root/libraries/wishboxcdek/src/Trait/CdekClientV2ServiceTrait.php:
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later; */ namespace WishboxCdekSDK2\Trait; use UnexpectedValueException; use WishboxCdekSDK2\CdekClientV2; use WishboxCdekSDK2\CdekClientV2Interface; use function defined; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for the CdekClientV2 service class. * * @since 1.0.0 */ trait CdekClientV2ServiceTrait { /** * The API client. * * @var CdekClientV2|null * * @since 1.0.0 */ private ?CdekClientV2Interface $cdekClientV2 = null; /** * Get the API client. * * @return CdekClientV2Interface * * @since 1.0.0 */ public function getCdekClientV2(): CdekClientV2Interface { if (!$this->cdekClientV2) { throw new UnexpectedValueException('CdekClientV2 not set in ' . __CLASS__); } return $this->cdekClientV2; } /** * The API client. * * @param CdekClientV2Interface $cdekClientV2 CdekClientV2 * * @return void * * @since 1.0.0 */ public function setCdekClientV2(CdekClientV2Interface $cdekClientV2): void { $this->cdekClientV2 = $cdekClientV2; } }
Подключим его к классам компонента Root/administrator/components/com_wishboxcdek/src/Extension/WishboxcdekComponent.php и плагина Root/plugins/console/wishboxcdek/src/Extension/Wishboxcdek.php .
Шаг 2: класс сервис-провайдера WishboxCdekSDK2\Service\ProviderCdekClientV2
Сервис-провайдер — реализует интерфейс
Joomla\DI\ServiceProviderInterface, в методе register которого в контейнере регистрируется функция, которая создаёт экземпляр АПИ-клиента.
В каждом компоненте или плагине есть файл provider.php с анонимным классом, реализующим интерфейс Joomla\DI\ServiceProviderInterface, используемый для регистрации сервис-провайдеров.
Значит нам нужен сервис-провайдер для нашего класса WishboxCdekSDK2\CdekClientV2.
По аналогии с сервис-провайдерами классов ядра Joomla назовём класс точно так же как называется класс который он будет регистрировать — WishboxCdekSDK2\Service\ProviderCdekClientV2.
Root/libraries/wishboxcdek/src/Service/Provider/CdekClientV2.php
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later */ namespace WishboxCdekSDK2\Service\Provider; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use function defined; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service MVC factory. * * @since 1.0.0 */ class CdekClientV2 implements ServiceProviderInterface { /** * Аккаунт сервиса интеграции. * * @var string * * @since 1.0.0 */ private string $account; /** * Секретный пароль сервиса интеграции. * * @var string * * @since 1.0.0 */ private string $secure; /** * Timeout * * @var float|null * * @since 1.0.0 */ private ?float $timeout; /** * @param string $account Account * @param string|null $secure Secure * @param float|null $timeout Timeout * * @since 1.0.0 */ public function __construct(string $account, ?string $secure = null, ?float $timeout = 10.0) { $this->account = $account; $this->secure = $secure; $this->timeout = $timeout; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 1.0.0 * * @noinspection PhpUnusedParameterInspection */ public function register(Container $container): void { $container->set( \WishboxCdekSDK2\CdekClientV2Interface::class, function (Container $container) { return new \WishboxCdekSDK2\CdekClientV2( $this->account, $this->secure, $this->timeout ); } ); } }
Теперь перейдём в сервис-провайдер компонента строки 49-59 и 71.
Root/administrator/components/com_wishboxcdek/services/provider.php
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later; */ use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Component\Router\RouterFactoryInterface; use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface; use Joomla\CMS\Extension\ComponentInterface; use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory; use Joomla\CMS\Extension\Service\Provider\RouterFactory; use Joomla\CMS\HTML\Registry; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\Component\Wishboxcdek\Administrator\Extension\WishboxcdekComponent; use Joomla\Component\Wishboxcdek\Site\CMS\Extension\Service\Provider\MVCFactory; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use WishboxCdekSDK2\Service\Provider\CdekClientV2; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The Wishboxcdek service provider. * * @since 1.0.0 */ return new class implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 1.0.0 * * @noinspection PhpMissingReturnTypeInspection */ public function register(Container $container) { $container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\Wishboxcdek')); $container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Wishboxcdek')); $container->registerServiceProvider(new RouterFactory('\\Joomla\\Component\\Wishboxcdek')); // Получаем параметры компонента $componentParams = ComponentHelper::getParams('com_wishboxcdek'); // Регистрируем класс сервис-провайдера АПИ клиента в контейнере $container->registerServiceProvider( new CdekClientV2( $componentParams->get('account', ''), $componentParams->get('secure', ''), 60.0 ) ); $container->set( ComponentInterface::class, function (Container $container) { $component = new WishboxcdekComponent($container->get(ComponentDispatcherFactoryInterface::class)); $component->setRegistry($container->get(Registry::class)); $component->setMVCFactory($container->get(MVCFactoryInterface::class)); $component->setRouterFactory($container->get(RouterFactoryInterface::class)); // Устанавливаем АПИ-клиент в класс компонента $component->setCdekClientV2($container->get(\WishboxCdekSDK2\CdekClientV2Interface::class)); return $component; } ); } };
И аналогично зарегистрируем сервис-провайдер в плагине Root/plugins/console/wishboxcdek/services/provider.php.
Следующие способы получения АПИ-клиента уже должны работать.
$apiClient = Joomla\CMS\Factory::getApplication() ->bootComponent('com_wishboxcdek')->getCdekClientV2(); $apiClient = Joomla\CMS\Factory::getApplication() ->bootPlugin('wishboxcdek', 'console')->getCdekClientV2();
Но наша цель $apiClient = $this->getCdekClientV2(); в моделях компонента.
Шаг 3: Интерфейс WishboxCdekSDK2\Interface\CdekClientV2AwareInterface и трейт WishboxCdekSDK2\TraitCdekClientV2AwareTrait
Так же пойдём от обратного, напишем для модели интерфейс, который описывает метод для установки АПИ-клиента setCdekClientV2.
Root/libraries/wishboxcdek/src/Interface/CdekClientV2AwareInterface.php
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later */ namespace WishboxCdekSDK2\Interface; // phpcs:disable PSR1.Files.SideEffects use WishboxCdekSDK2\CdekClientV2Interface; use function defined; defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a form factory. * * @since 1.0.0 */ interface CdekClientV2AwareInterface { /** * Set the form factory to use. * * @param CdekClientV2 $cdekClientV2 The API client to use. * * @return CdekClientV2AwareInterface This method is chainable. * * @since 1.0.0 */ public function setCdekClientV2(CdekClientV2Interface $cdekClientV2): CdekClientV2AwareInterface; }
И трейт, который реализует методы setCdekClientV2 и getCdekClientV2.
Root/libraries/wishboxcdek/src/Trait/CdekClientV2AwareTrait.php
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later; */ namespace WishboxCdekSDK2\Trait; use UnexpectedValueException; use WishboxCdekSDK2\CdekClientV2; use WishboxCdekSDK2\Interface\CdekClientV2AwareInterface; use function defined; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for the CdekClientV2 service class. * * @since 1.0.0 */ trait CdekClientV2AwareTrait { /** * The API client. * * @var CdekClientV2|null * * @since 1.0.0 */ private ?CdekClientV2 $cdekClientV2 = null; /** * Get the API client. * * @return CdekClientV2 * * @throws UnexpectedValueException May be thrown if the factory has not been set. * * @since 1.0.0 */ public function getCdekClientV2(): CdekClientV2Interface { if (!$this->cdekClientV2) { throw new UnexpectedValueException('CdekClientV2 not set in ' . __CLASS__); } return $this->cdekClientV2; } /** * The API client. * * @param CdekClientV2 $cdekClientV2 CdekClientV2 * * @return CdekClientV2AwareInterface * * @since 1.0.0 */ public function setCdekClientV2(CdekClientV2Interface $cdekClientV2): CdekClientV2AwareInterface { $this->cdekClientV2 = $cdekClientV2; return $this; } }
Подключаем интерфейс и трейт к классу модели, теперь она поддерживает методы setCdekClientV2 и getCdekClientV2.
Но кто устанавливает АПИ-клиент в модель?. - Правильно, MVC-фабрика. Давайте вспомним пост Использование своего класса MVC фабрики в компоненте Joomla 5.
Открываем переопределённый класс MVC-фабрики, находим метод createModel и в конце добвляем следующие строки:
if ($model instanceof CdekClientV2AwareInterface) { try { $model->setCdekClientV2($this->getCdekClientV2()); } catch (UnexpectedValueException $e) { // Ignore it } }
Мы проверяем наличие у модели интерфейса и если есть, устанавляваем свой АПИ-клиент.
Значит и сам класс MVC-фабрики должен использовать эти интерфейс и трейт.
А в фабрику экземпляр АПИ-клиента при создании устанавливает её сервис-провайдер.
Root/administrator/components/com_wishboxcdek/src/Extension/Service/Provider/MVCFactory.php
<?php /** * @copyright (c) 2013-2025 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru> * @license GNU General Public License version 2 or later; see LICENSE.txt */ 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\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use WishboxCdekSDK2\CdekClientV2Interface; use function defined; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service MVC factory. * * @since 1.0.0 */ class MVCFactory extends \Joomla\CMS\Extension\Service\Provider\MVCFactory implements ServiceProviderInterface { /** * The extension namespace * * @var string * * @since 1.0.0 */ private $namespace; /** * MVCFactory constructor. * * @param string $namespace The namespace * * @since 1.0.0 */ public function __construct(string $namespace) { parent::__construct($namespace); $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 1.0.0 */ public function register(Container $container): void { $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->setCdekClientV2($container->get(CdekClientV2Interface::class)); return $factory; } ); } }
Готово! Теперь в модели работает код $apiClient = $this->getCdekClientV2();.
