簡體   English   中英

如何在Zend Framework 3中的驗證器中注入依賴項?

[英]How to inject a dependency into a Validator in Zend Framework 3?

我創建了一個自定義的驗證器,稱為CredentialsValidator,如果傳遞的憑證有效,則返回true。 憑據的實際驗證是依賴項AccountService的責任,可以通過getAccountService()方法在Validator中使用該依賴項。 CredentialsValidator :: isValid($ value,$ context = null)的有趣部分是:

$accountService = $this->getAccountService();

$accountService->setEmail($this->getEmail());
$accountService->setPassword($this->getPassword());

try {
    $accountService->auth();
} catch (RuntimeException $exception) {
    $this->setMessage($exception->getMessage());
    $this->error(self::INVALID_CREDENTIALS);

    return false;
}

屬性$ email和$ password的填充如下:

if (isset($context['email'])) {
    $this->setEmail($context['email']);
}

if (isset($context['password'])) {
    $this->setPassword($context['password']);
}

當我在單元測試中實例化CredentialsValidator並手動分配依賴項AccountService時,它可以100%正確地工作。

在實際的Web應用程序中,使用module.config.php中的標准配置通過ServiceManager實例化AccountService:

return [
    'service_manager' => [
        'factories' => [
            AccountServiceFactory::class   => AccountServiceFactory::class,
        ],
    ]
];

但是,我的目標是創建一個典型的“登錄”表單,該表單使用CredentialsValidator來驗證用戶憑據。

為此,我創建了一個擴展Zend \\ Form \\ Form的表單:

$this->add([
    'type'  => 'text',
    'name' => 'email',
    'attributes' => [
        'id' => 'email'
    ],
    'options' => [
        'label' => 'Email',
    ],
]);

$this->add([
    'type'  => 'password',
    'name' => 'password',
    'attributes' => [
        'id' => 'password'
    ],
    'options' => [
        'label' => 'Password',
    ],
]);

以及相關的模型,定義了getInputFilter()方法:

public function getInputFilter()
{
    if ($this->inputFilter) {
        return $this->inputFilter;
    }

    $this->inputFilter = new InputFilter();

    $this->inputFilter->add([
        'name'     => 'email',
        'required' => true,
        'filters'  => [
            ['name' => StringTrimFilter::class],
            ['name' => StripTagsFilter::class],
            ['name' => StripNewlinesFilter::class],
        ],
        'validators' => [
            [
                'name' => EmailAddressValidator::class,
                'break_chain_on_failure' => true,
                'options' => [
                    'allow' => HostnameValidator::ALLOW_DNS,
                    'useMxCheck' => false,
                ],
            ],

        ],
    ]);

    $this->inputFilter->add([
        'name'     => 'password',
        'required' => true,
        'filters'  => [
            ['name' => StringTrimFilter::class],
            ['name' => StripTagsFilter::class],
            ['name' => StripNewlinesFilter::class],
        ],
        'validators' => [
            [
                'name'    => StringLengthValidator::class,
                'break_chain_on_failure' => true,
                'options' => [
                    'min' => 1,
                    'max' => 128
                ],
            ]
        ],

    ]);

這是問題開始的地方。 當我添加:

[
    'name' => CredentialsValidator::class,
    'break_chain_on_failure' => true,
],

在“密碼”字段的“驗證器”鍵上,我無法注入所需的依賴關系,該依賴關系存儲在ServiceManager中,因此CredentialsValidator無法工作,因為它無法訪問AccountService實例。

我為這個問題提出了兩種解決方案,其中一種我立即丟棄,因為它使用單例,而另一種在工作時需要手動傳遞依賴關系。

解決方案1:使用在Module.php中創建的單例

在onBootstrap(MvcEvent $ event)方法中,可以創建一個單例:

AccountService::getInstance()

然后可以在CredentialsValidator中訪問它,並調用Controller。

我放棄了此解決方案,因為它使用了現在不建議使用的Singleton模式。

解決方案2:手動傳遞AccountService實例

在Controller中,可以將AccountService實例傳遞到Model的構造函數中:

$model = new Model([AccountService::class => $accountService]);

然后在Model :: getInputFilter()中,將該實例傳遞到'password'字段的'validators'鍵,如下所示:

$this->inputFilter->add([
    'name'     => 'password,
    'required' => true,
    'filters'  => [
        ['name' => StringTrimFilter::class],
        ['name' => StripTagsFilter::class],
        ['name' => StripNewlinesFilter::class],
    ],
    'validators' => [
        [
            'name'    => StringLengthValidator::class,
            'break_chain_on_failure' => true,
            'options' => [
                'min' => 1,
                'max' => 128
            ],
        ],
        [
            'name' => CredentialsValidator::class,
            'break_chain_on_failure' => true,
            'options' => [
                AccountService::class => $this->getAccountService(),
            ],
        ],
    ],

]);

然后,CredentialsValidator只需通過其構造函數接受依賴關系:

if (array_key_exists(AccountService::class, $options)) {
    $this->setAccountService($options[AccountService::class]);
}

該解決方案確實有效,並且確實尊重類之間的接口,但是,手動傳遞AccountService實例是一項相當大的額外工作,實際上,ServiceManager和注入的全部目的就是避免這種情況。 解決方案2在Zend Framework 3應用程序中感覺像是異物。

我的問題:如何在不從Controller手動傳遞的情況下訪問CredentialsValidator中的AccountService實例?

預先感謝您。

我認為您可以為CredentialsValidator創建一個Factory。 然后寄存器內的工廠validators配置在module.config.php或內部getValidatorConfig()Module.php

示例: module.config.php

'service_manager' => [
    'factories' => [
    ]
],
'validators' => [
    'factories' => [
          CredentialsValidator::class => CredentialsValidatorFactory::class
     ]
]

Module.php

public function getValidatorConfig()
{
    return [
        'factories' => [
             CredentialsValidator::class => new                                         CredentialsValidatorFactory::class($param, $param2)
        ]
    ]
}

由於驗證器已經注冊,因此您只需在InputFilter配置中注冊名稱

$this->inputFilter->add([
     'name'     => 'Credential',
    'required' => true,        
    'validators' => [
         [
            'name' => CredentialsValidator::class,
         ]
    ],
]);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM