【问题标题】:ZF2: ServiceLocator in Validator unable to fetch instanceZF2:验证器中的 ServiceLocator 无法获取实例
【发布时间】:2015-12-06 15:11:01
【问题描述】:

我已经实现了一个自定义验证器类,如下所示

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

堆栈跟踪指向 Validator 类。

“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-framework2


    【解决方案1】:

    Zend 框架有多个管理器,它们都是服务定位器。如果您只知道ServiceManager

    ,这可能会令人困惑

    在您的验证器中,getServiceLocator 不会返回 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有任何了解。要解决此问题,您需要:

    1. InputFilterPluginManager 注入Zend\InputFilter\Factory
    2. 用这个工厂创建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 访问所有管理器,因此可以创建具有适当依赖注入的验证器。

    【讨论】:

    • 我已经试过了。但是$this->getServiceLocator()->getServiceLocator(); 返回一个空值。这可能是我使用 new 关键字创建表单实例的原因。
    • 创建验证器实例?如果是这样,可能是的,请在您创建验证器的位置添加代码。
    • 没有。验证器由 zend 隐式实例化。仅使用 new 关键字创建表单。我已编辑问题以添加代码。
    • 我也尝试过从服务创建输入过滤器。但它给出了相同的结果。
    • 只需稍作改动即可。在表单中,必须在调用$this->setInputFilter() 之前调用父构造函数。否则,会引发 Call to a member function insert() on null 异常。如果您在答案中更改它可能对其他人有用。非常感谢。
    猜你喜欢
    • 2013-09-12
    • 2013-03-26
    • 2015-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多