Zend 2: Получить сервисный локатор в плагине

Я хочу получить сервисный локатор в моем собственном плагине. Я создаю его через фабрику в Module.php:

public function getControllerPluginConfig() {
return [
'factories' => [
'Application\Plugin\AppPlugin' => function($serviceManager) {

$serviceLocator = $serviceManager->getServiceLocator();
$appPlugin = new \Application\Plugin\AppPlugin();
$appPlugin->setLocator($serviceLocator);
return $appPlugin;
}
]
];
}

…и мой плагин:

<?php

namespace Application\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

class AppPlugin extends AbstractPlugin {

protected $locator;

public function getAppInfo() {

$config = $this->locator->get('Config');
}

/**
* Set Service Locator
* @param \Zend\ServiceManager\ServiceManager $locator
*/
public function setLocator($locator) {
$this->locator = $locator;
}
}

Затем я вызываю getAppInfo () в моем контроллере:

$appPlugin = new AppPlugin();
$appPlugin->getAppInfo();

…и я получаю ошибку:

Fatal error: Call to a member function get() on a non-object in /vagrant/app/module/Application/src/Application/Plugin/AppPlugin.php on line 13
Call Stack
#   Time    Memory  Function    Location
1   0.0027  232104  {main}( )   ../index.php:0
2   0.6238  3166904 Zend\Mvc\Application->run( )    ../index.php:21
3   1.4410  5659112 Zend\EventManager\EventManager->trigger( )  ../Application.php:314
4   1.4410  5659112 Zend\EventManager\EventManager->triggerListeners( ) ../EventManager.php:205
5   1.4411  5660872 call_user_func ( )  ../EventManager.php:444
6   1.4411  5661440 Zend\Mvc\DispatchListener->onDispatch( )    ../EventManager.php:444
7   1.4505  5704600 Zend\Mvc\Controller\AbstractController->dispatch( ) ../DispatchListener.php:93
8   1.4505  5705080 Zend\EventManager\EventManager->trigger( )  ../AbstractController.php:118
9   1.4505  5705080 Zend\EventManager\EventManager->triggerListeners( ) ../EventManager.php:205
10  1.4507  5716656 call_user_func ( )  ../EventManager.php:444
11  1.4507  5716784 Zend\Mvc\Controller\AbstractActionController->onDispatch( ) ../EventManager.php:444
12  1.4508  5717504 Application\Controller\IndexController->indexAction( )  ../AbstractActionController.php:82
13  1.4641  5727768 Application\Plugin\AppPlugin->getAppInfo( ) ../IndexController.php:21

Но если я передаю сервисный локатор с моего контроллера, он работает нормально:

$appPlugin = new AppPlugin();
$appPlugin->setLocator($this->getServiceLocator());
$appPlugin->getAppInfo();

Я буду рад, если кто-нибудь объяснит мне, что я делаю не так. Благодарю.

0

Решение

Когда это вообще возможно, вы не должны пытаться получить доступ к ServiceLocator внутри какого-либо класса, кроме фабрики. Основная причина этого заключается в том, что если ServiceLocator внедряется в ваш класс, вы теперь не представляете, каковы зависимости этого класса, потому что теперь он может потенциально содержать что-либо.

Что касается внедрения зависимостей, у вас есть два основных варианта: внедрение конструктора или установки. Как правило, всегда предпочитайте конструктор инъекций. Внедрение сеттера должно использоваться только для необязательных зависимостей, и это также делает ваш код более двусмысленным, потому что класс теперь изменчив. Если вы используете чисто конструкторское внедрение, ваши зависимости неизменны, и вы всегда можете быть уверены, что они будут там.

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

Увидеть https://stackoverflow.com/a/18866169/1312094 для хорошего описания того, как настроить класс для реализации FactoryInterface

При этом давайте придерживаться вашего примера фабрики замыкания для целей этого обсуждения. Вместо внедрения ServiceManager мы внедряем массив конфигурации (который все еще слишком зависим от бога; лучше ввести конкретный ключ конфигурации, который вам нужен, или еще лучше — экземпляр класса).

public function getControllerPluginConfig() {
return [
'factories' => [
\Application\Plugin\AppPlugin::class => function($serviceManager) {
$serviceLocator = $serviceManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$appPlugin = new \Application\Plugin\AppPlugin($config);
return $appPlugin;
}
]
];
}

И здесь плагин модифицируется, чтобы принять конфигурацию в конструкторе. Я предполагаю, что этот код плагина является просто доказательством концепции, и что вы действительно сделаете что-то полезное с плагином, кроме возврата конфигурации, но, надеюсь, это действительно показывает шаблон для внедрения ваших зависимостей плагина.

<?php

namespace Application\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

class AppPlugin extends AbstractPlugin {

protected $config;

public function __construct($config){
$this->config = $config;
}

public function getAppInfo() {
return $this->config;
}

}

Кроме того, небольшая вещь, но при условии PHP 5.5+, рассмотрите возможность использования \Application\Plugin\AppPlugin::class как я сделал в примере выше, вместо строкового литерала для ключа конфигурации.

2

Другие решения

Вы не можете создать экземпляр класса непосредственно в контроллере, который вам нужен, чтобы получить его из диспетчера плагинов контроллера с помощью ключа ‘Application \ Plugin \ AppPlugin’

$pluginManager = $this->getPluginManager();
$appPlugin = $pluginManager->get('Application\Plugin\AppPlugin');
$appPlugin->getAppInfo();

В качестве альтернативы вы можете изменить метод на:

public function getAppInfo()
{
$serviceManager = $this->controller->getServiceLocator();
$config = $serviceManager->get('Config');
}
0

Я создал его через ServiceLocatorAwareInterface и сделал его активируемым:

namespace Application\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class AppPlugin extends AbstractPlugin implements ServiceLocatorAwareInterface {

/**
* @var ServiceLocatorInterface
*/
protected $serviceLocator;

/**
* Get system information
* @return type
*/
public function getAppInfo() {

$config = $this->getServiceLocator()->get('Config');
// to do something...
}

/**
* Retrieve service manager instance
*
* @return ServiceLocator
*/
public function getServiceLocator() {
return $this->serviceLocator->getServiceLocator();
}

/**
* Set service locator
*
* @param ServiceLocatorInterface $serviceLocator
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
}

Затем в module.appplication.config:

'controller_plugins' => [
'invokables' => [
'AppPlugin'     => \Application\Controller\Plugin\AppPlugin::class
]
]

…и теперь я могу использовать свой плагин в контроллере следующим образом:

$app = $this->AppPlugin()->getAppInfo();
0
По вопросам рекламы [email protected]