У меня есть библиотека для работы с АПИ Сдэка. Для работы с ней надо создать экземпляр класса 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();
.