ZF2:Validator 中的 ServiceLocator 无法获取实例
ZF2: ServiceLocator in Validator unable to fetch instance
我实现了一个自定义验证器 class 如下
class UsernameUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
protected $userManager;
public function getUserManager()
{
if(!$this->userManager)
{
$this->userManager = $this->getServiceLocator()->get('RTUserManager');
}
return $this->userManager;
}
public isValid()
{
$um = $this->getUserManager();
//rest of code
}
}
我已经在表单中使用了这个验证器。
'validators' => array(
array(
'name' => 'RTUser\Form\Validator\EmailUnique'
)
)
一切似乎都很好,但是当进行表单验证时,我收到一个异常提示
Zend\Validator\ValidatorPluginManager::get was unable to fetch or create an instance for RTUserManager
堆栈跟踪指向验证器 class。
'RTUserManager' 可以从控制器创建。所以service_manager的配置是没有问题的。但是验证器无法创建它的实例。
我该如何解决这个问题?
编辑:
这是控制器插件中的代码。
public function register()
{
$newuser = $this->getInputValues();
if($newuser->isValid())
{
$userdata = $newuser->getData(FormInterface::VALUES_AS_ARRAY);
if($this->saveUserToDB($userdata))
{
return $message('Registration has been successful. Verify your email address to login.');
}
}
return $this->registrationForm($newuser);
}
protected function getInputValues()
{
$userdata = $this->getController()->getRequest()->getPost();
$newuser = new RegistrationForm();
$newuser->bind($userdata);
return $newuser;
}
表单代码。
class RegistrationForm extends Form
{
protected $inputfilter;
public function __construct()
{
//code to add form elements
$this->setInputFilter($this->getInputFilter());
}
public function getInputFilter()
{
if(!$this->inputfilter)
{
$inputFilter = new InputFilter();
//code to add other validators and filters
$inputFilter->add(
'name' => 'email',
'validators' => array(
'name' => 'RTUser\Form\Validator\EmailUnique',
)
);
$this->inputfilter = $inputFilter;
}
return $this->inputfilter;
}
}
Zend Framework 有多个管理器,它们都是服务定位器。如果您只知道 ServiceManager
,这可能会造成混淆
在您的验证器中,getServiceLocator
不是 return ServiceManager
,而是 ValidatorPluginManager
,后者当然不知道您的 service_manager
配置。
幸运的是,它扩展了 AbstractPluginManager
- 这意味着它包含实际的服务管理器。
因此,在获得服务之前,您需要先访问 ServiceManager
。相反:
$this->userManager = $this->getServiceLocator()->get('RTUserManager');
尝试:
/** @var ValidatorPluginManager $validatorManager */
$validatorManager = $this->getServiceLocator();
/** @var ServiceManager $serviceManager */
$serviceManager = $validatorManager->getServiceLocator();
$this->userManager = $serviceManager->get('RTUserManager');
这只是一个更清晰的符号:
$this->userManager = $this->getServiceLocator()->getServiceLocator()->get('RTUserManager');
最后,我建议您为您的验证器编写一些工厂来注入您的依赖项 RTUserManager
,以免丢失对依赖项的跟踪。在 类 中使用任何服务定位器都不是好的做法,在 Zend 3 中,服务定位器甚至可能只在工厂中可用。但现在这是可选的。
编辑:
你是对的,直接创建InputFilter
是你的第二个问题。因为你这样做了,它不可能对ServiceManager
有任何了解。要解决此问题,您需要:
- 将
InputFilterPluginManager
注入 Zend\InputFilter\Factory
- 用这个工厂创建
InputFilter
所以你的 RegistrationForm
应该看起来像这样:
class RegistrationForm extends Form
{
protected $inputfilter;
/**
* @var InputFilterPluginManager
*/
protected $inputFilterManager;
/**
* RegistrationForm constructor.
* @param InputFilterPluginManager $inputFilterManager
*/
public function __construct(InputFilterPluginManager $inputFilterManager)
{
$this->setInputFilterManager($inputFilterManager);
parent::__construct("registration");
//code to add form elements
$this->setInputFilter($this->getInputFilter());
}
public function getInputFilter()
{
if(!$this->inputfilter)
{
$factory = new Factory($this->getInputFilterManager());
$inputFilter = $factory->createInputFilter(array(
// other input specifications
"email" => array(
'name' => 'email',
'validators' => array(
array(
'name' => 'Application\Form\Validator\EmailUnique',
),
),
),
));
$this->inputfilter = $inputFilter;
}
return $this->inputfilter;
}
/**
* @return InputFilterPluginManager
*/
public function getInputFilterManager()
{
return $this->inputFilterManager;
}
/**
* @param InputFilterPluginManager $inputFilterManager
*/
public function setInputFilterManager($inputFilterManager)
{
$this->inputFilterManager = $inputFilterManager;
}
}
并且你要么需要在实例化RegistrationForm
时直接传递InputFilterPluginManager
,要么为它建立一个工厂。为简单起见,我将只扩展您当前的插件代码,尽管我更喜欢工厂:
protected function getInputValues()
{
/** @var InputFilterPluginManager $inputManager */
$inputManager = $this->getController()->getServiceLocator()->get("InputFilterManager");
$userdata = $this->getController()->getRequest()->getPost();
$newuser = new RegistrationForm($inputManager);
$newuser->bind($userdata);
return $newuser;
}
就是这样。工厂能够通过 InputFilterPluginManager
访问所有管理器,因此可以创建具有适当依赖注入的验证器。
我实现了一个自定义验证器 class 如下
class UsernameUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
protected $userManager;
public function getUserManager()
{
if(!$this->userManager)
{
$this->userManager = $this->getServiceLocator()->get('RTUserManager');
}
return $this->userManager;
}
public isValid()
{
$um = $this->getUserManager();
//rest of code
}
}
我已经在表单中使用了这个验证器。
'validators' => array(
array(
'name' => 'RTUser\Form\Validator\EmailUnique'
)
)
一切似乎都很好,但是当进行表单验证时,我收到一个异常提示
Zend\Validator\ValidatorPluginManager::get was unable to fetch or create an instance for RTUserManager
堆栈跟踪指向验证器 class。
'RTUserManager' 可以从控制器创建。所以service_manager的配置是没有问题的。但是验证器无法创建它的实例。
我该如何解决这个问题?
编辑: 这是控制器插件中的代码。
public function register()
{
$newuser = $this->getInputValues();
if($newuser->isValid())
{
$userdata = $newuser->getData(FormInterface::VALUES_AS_ARRAY);
if($this->saveUserToDB($userdata))
{
return $message('Registration has been successful. Verify your email address to login.');
}
}
return $this->registrationForm($newuser);
}
protected function getInputValues()
{
$userdata = $this->getController()->getRequest()->getPost();
$newuser = new RegistrationForm();
$newuser->bind($userdata);
return $newuser;
}
表单代码。
class RegistrationForm extends Form
{
protected $inputfilter;
public function __construct()
{
//code to add form elements
$this->setInputFilter($this->getInputFilter());
}
public function getInputFilter()
{
if(!$this->inputfilter)
{
$inputFilter = new InputFilter();
//code to add other validators and filters
$inputFilter->add(
'name' => 'email',
'validators' => array(
'name' => 'RTUser\Form\Validator\EmailUnique',
)
);
$this->inputfilter = $inputFilter;
}
return $this->inputfilter;
}
}
Zend Framework 有多个管理器,它们都是服务定位器。如果您只知道 ServiceManager
在您的验证器中,getServiceLocator
不是 return ServiceManager
,而是 ValidatorPluginManager
,后者当然不知道您的 service_manager
配置。
幸运的是,它扩展了 AbstractPluginManager
- 这意味着它包含实际的服务管理器。
因此,在获得服务之前,您需要先访问 ServiceManager
。相反:
$this->userManager = $this->getServiceLocator()->get('RTUserManager');
尝试:
/** @var ValidatorPluginManager $validatorManager */
$validatorManager = $this->getServiceLocator();
/** @var ServiceManager $serviceManager */
$serviceManager = $validatorManager->getServiceLocator();
$this->userManager = $serviceManager->get('RTUserManager');
这只是一个更清晰的符号:
$this->userManager = $this->getServiceLocator()->getServiceLocator()->get('RTUserManager');
最后,我建议您为您的验证器编写一些工厂来注入您的依赖项 RTUserManager
,以免丢失对依赖项的跟踪。在 类 中使用任何服务定位器都不是好的做法,在 Zend 3 中,服务定位器甚至可能只在工厂中可用。但现在这是可选的。
编辑:
你是对的,直接创建InputFilter
是你的第二个问题。因为你这样做了,它不可能对ServiceManager
有任何了解。要解决此问题,您需要:
- 将
InputFilterPluginManager
注入Zend\InputFilter\Factory
- 用这个工厂创建
InputFilter
所以你的 RegistrationForm
应该看起来像这样:
class RegistrationForm extends Form
{
protected $inputfilter;
/**
* @var InputFilterPluginManager
*/
protected $inputFilterManager;
/**
* RegistrationForm constructor.
* @param InputFilterPluginManager $inputFilterManager
*/
public function __construct(InputFilterPluginManager $inputFilterManager)
{
$this->setInputFilterManager($inputFilterManager);
parent::__construct("registration");
//code to add form elements
$this->setInputFilter($this->getInputFilter());
}
public function getInputFilter()
{
if(!$this->inputfilter)
{
$factory = new Factory($this->getInputFilterManager());
$inputFilter = $factory->createInputFilter(array(
// other input specifications
"email" => array(
'name' => 'email',
'validators' => array(
array(
'name' => 'Application\Form\Validator\EmailUnique',
),
),
),
));
$this->inputfilter = $inputFilter;
}
return $this->inputfilter;
}
/**
* @return InputFilterPluginManager
*/
public function getInputFilterManager()
{
return $this->inputFilterManager;
}
/**
* @param InputFilterPluginManager $inputFilterManager
*/
public function setInputFilterManager($inputFilterManager)
{
$this->inputFilterManager = $inputFilterManager;
}
}
并且你要么需要在实例化RegistrationForm
时直接传递InputFilterPluginManager
,要么为它建立一个工厂。为简单起见,我将只扩展您当前的插件代码,尽管我更喜欢工厂:
protected function getInputValues()
{
/** @var InputFilterPluginManager $inputManager */
$inputManager = $this->getController()->getServiceLocator()->get("InputFilterManager");
$userdata = $this->getController()->getRequest()->getPost();
$newuser = new RegistrationForm($inputManager);
$newuser->bind($userdata);
return $newuser;
}
就是这样。工厂能够通过 InputFilterPluginManager
访问所有管理器,因此可以创建具有适当依赖注入的验证器。