【问题标题】:Possible to create a factory to instantiate custom Form validators?可以创建一个工厂来实例化自定义表单验证器吗?
【发布时间】:2013-10-28 18:14:36
【问题描述】:

(使用 Zend Framework 2.2.4)

我的验证器工厂在验证时似乎并不“存在”。如果我尝试从表单所在的控制器实例化验证器,它反而可以正常工作:

这行得通...

$mycustomvalidator = $this->getServiceLocator()
    ->get('ValidatorManager')
    ->get('LDP_PinAvailable');

这是代码中的其他设置方式,我似乎找不到问题,并希望避免打开 ZF2 源来理解。从文档的角度来看,这似乎是正确的。

模块配置

public function getValidatorConfig()
{
    return array(
       'abstract_factories' => array(
           '\LDP\Form\Validator\ValidatorAbstractFactory',
       ),
    );
}

工厂类

namespace LDP\Form\Validator;

use Zend\ServiceManager\AbstractFactoryInterface,
    Zend\ServiceManager\ServiceLocatorInterface;

class ValidatorAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
    {
        return stristr($requestedName, 'LDP_PinAvailable') !== false;
    }


    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        // baked in for sake of conversation
        $validator = new \LDP\Form\Validator\PinAvailable();

        if( $validator instanceof DatabaseFormValidatorInterface )
            $validator->setDatabase( $locator->get('mysql_slave') );

        return $validator;
    }
}

自定义验证器

namespace LDP\Form\Validator;

class PinAvailable extends \Zend\Validator\AbstractValidator implements DatabaseFormValidatorInterface
{

    /**
     * @var \Zend\Db\Sql\Sql
     */
    private $database;

    public function setDatabase( \Zend\Db\Sql\Sql $db )
    {
        $this->database = $db;
    }


    public function isValid( $value )
    {
        $DBA = $this->database->getAdapter();
        // do the mixed database stuff here
        return true;
    }
}

最后,数组的表单字段验证器配置部分:

'pin' => array(
    'required' => true,
        'filters'  => array(
            array('name' => 'alnum'),
            array('name' => 'stringtrim'),
        ),
        'validators' => array(
            array( 'name' => 'LDP_PinAvailable' )
        ),
    ),
),

将所有内容拼凑在一起,加载表单,并在提交时使用下面的堆栈跟踪:

2013-10-28T17:09:35-04:00 ERR (3): Exception:
1: Zend\Validator\ValidatorPluginManager::get was unable to fetch or create an instance for LDP_PinAvailable
Trace:
#0 /Users/Saeven/Documents/workspace/Application/vendor/zendframework/zendframework/library/Zend/ServiceManager/AbstractPluginManager.php(103): Zend\ServiceManager\ServiceManager->get('LDP_PinAvailabl...', true)
#1 /Users/Saeven/Documents/workspace/Application/vendor/zendframework/zendframework/library/Zend/Validator/ValidatorChain.php(82): Zend\ServiceManager\AbstractPluginManager->get('LDP_PinAvailabl...', Array)

【问题讨论】:

    标签: php zend-framework2


    【解决方案1】:

    ValidatorPluginManager 扩展了 Zend\ServiceManager\AbstractPluginManager。 AbstractPluginManager 有一个名为“autoAddInvokableClass”的功能,默认启用。

    基本上,这意味着,如果请求的服务名称不能被 ValidatorPluginManager 解析,它将检查该名称是否是有效的类名。如果是这样,它只会按需将其添加为可调用的类,这当然意味着它永远不会退回到您的抽象工厂。

    要避免这种行为,最简单的方法是让您的抽象工厂响应实际上并未解析为实际类名的服务名称。

    见:AbstractPluginManager.php#L98-L100

    【讨论】:

    • 感谢您的回答。这有点像 PITA。从语义上讲,我认为验证器配置中抽象工厂的可用性应该表示开发人员的意图。没有类名解析。
    • 对于 ZF3 来说,这可能是一个值得讨论/提议的问题。随意在 ZF2 存储库上创建一个问题,提出您认为它应该如何表现以及为什么。 :)
    • 完成。现在,我已将验证器的服务名称更改为使用简单的小写字符串。我可以使用控制器中的那个字符串,例如:$this->getServiceLocator()->get('ldp-validator-pinavailable');,它发现它“ok”。但是,当表单尝试验证时,它会失败,Zend\Validator\ValidatorPluginManager::get is unable to fetch or create an instance for ldp-validator-pinavailable
    • 感谢您今天的帮助;我没有标记答案,因为我找到了不包括这里提到的关于名字的内容的解决方案,但留下了良好的业力和谢谢。
    【解决方案2】:

    再挖一些,我发现了问题。它在 Zend\Validator\ValidatorChain 大约第 80 行中提炼为这些行:

    public function plugin($name, array $options = null)
    {
        $plugins = $this->getPluginManager();
        return $plugins->get($name, $options);
    } 
    

    上下文中没有可用的插件管理器。

    当我在控制器中准备表单时,我用谷歌搜索了大约三秒钟才发现我必须这样做:

     $validators = $this->getServiceLocator()->get('ValidatorManager');
     $chain      = new ValidatorChain();
     $chain->setPluginManager( $validators );
     $form->getFormFactory()->getInputFilterFactory()->setDefaultValidatorChain( $chain );
    

    希望这对其他人有所帮助。通过这种方式设置时,您可以使用常规的旧类名,无需扭曲类名。

    【讨论】:

      【解决方案3】:

      在 ZF3/Laminas 中,如果验证器注册为invokable,则可以在表单的getInputFilterSpecification() 中调用验证器,没有问题。如果使用工厂实例化验证器,则会遇到麻烦。如果我理解正确,即使您的表单是这样注册的

      'form_elements' => [
          'factories' => [
              SomeForm::class => SomeFormFactory::class,
          ]
      ]
      

      和你的验证者:

      'validators' => [
          'factories' => [
              SomeValidator::class => SomeValidatorFactory::class,
          ]    
      ]
      

      您不会通过工厂实例化验证器。原因是表单工厂(你得到的那个像$form->getFormFactory())有一个输入过滤器工厂,里面有默认的验证器链。而且这个验证器链没有附加ValidatorManager。如果没有ValidatorManager,默认链无法将验证器名称映射到验证器工厂。

      要解决所有这些令人头疼的问题,请在您的控制器工厂中执行以下操作:

      $form->('FormElementManager')->get(SomeForm::class);
      $form->getFormFactory()->getInputFilterFactory()
              ->getDefaultValidatorChain()->setPluginManager($container->get('ValidatorManager'));
      

      你的麻烦已经过去了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-14
        • 2016-12-02
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多