当我阅读您的帖子时,我想到了一个关于您所写内容的问题:
为什么不验证表单,而不验证模型的对象?
我的意思是,以 OOP 的方式看待事物,您的模型对象(或域对象)是那些知道哪些数据对其每个属性有效或无效的对象。
不要那样做,将这种逻辑推入 UI 会使您的设计变得脆弱、依赖于 UI 并且更难维护。如果您向模型的一个对象添加新属性,您还必须修改表单验证器。
如果您使用对象验证,其想法是不能在无效状态下创建对象。如果您尝试使用无效数据对其进行修改,则会引发异常。
这使得使用表单变得容易。您唯一需要做的就是填充您的对象并注意该过程中引发的异常。
这只是让您开始并了解解决此问题的另一种方法的想法。
关于您关于表单验证的问题,正如其他人所说,最好不要重新发明轮子并使用现有的、经过验证的验证框架。
但是,如果您对此感到好奇,这里是众多方法之一您可以做到这一点:
让我们来看看你需要的东西:你正在谈论一个需要验证的表单具有一个或多个验证函数。然后你谈论一个函数,它告诉你表单是否通过了验证,结果你得到了在过程中发现的错误列表验证阶段。
当您谈论 OOP 时,要走的路是通过一个代表问题域(表单验证域)实体的每个概念或想法,通过一个类来模拟它们的行为。
因此,很自然地想到一个带有ValidationRule 实例列表的FormValidator 类,每个实例都在验证过程中协作。这个验证过程是通过调用FormValidator 的validate 函数来完成的。此外,每个ValidationRule 都会在调用它自己的validate 方法时给出ValidationRuleResult 类的实例,该实例告诉验证是否成功,以及错误消息和附加数据(如果需要)关于验证。评估完所有验证规则后,FormValidator 类的 validate 方法将返回 ValidationResult 类的实例,该实例汇总了评估规则的所有验证结果,并提供了找到的错误列表。
为了切实做到这一点,以下是我们正在讨论的示例模型:
示例实现
免责声明:请记住,与任何设计一样,它可能包含缺陷。以下内容旨在帮助您解决问题,而不是完整的解决方案。
class FormValidator {
private $_validationRules;
function __construct() {
$this->_validationRules = array();
}
// Registers a new validation rule
function addRule($aValidationRule) { $this->validationRules[] = $aValidationRule; }
// Validates $aForm, evaluating each of the $_validationRules defined
function validate($aForm) {
$aValidationResult = new ValidationResult();
foreach($this->_validationRules as $aValidationRule) {
$aValidationRuleResult = $aValidationRule->validate($aForm);
$aValidationResult->addResult($aValidationRuleResult);
}
return $aValidationResult;
}
}
abstract class ValidationRule {
private $_fieldName;
// The form's field name to be validated
function __construct($aFieldName) {
$this->_fieldName = $aFieldName;
}
function fieldName() { return $this->_fieldName; }
// Returns an instance of ValidationResult describing the result of evaluating the ValidationRule in $aForm.
abstract public function validate($aForm);
}
class ValidationResult {
private $_validationRuleResults;
function __construct() {
$this->_validationRuleResults = array();
}
// Registers a validation rule result
function addResult($aValidationRuleResult) {
$this->_validationRuleResults[] = $aValidationRuleResult;
}
// Returns the list of the error messages of the validation rule results that did't passed
function errorsFound() {
$errors = array();
foreach($this->validationRuleResults as $aValidationResult) {
if ($aValidationResult->passed()) continue;
$errors[] = $aValidationResult->errorMessage();
}
return $errors;
}
// Tells whether all the validation rule results passed or not
function validationPassed() {
foreach($this->validationRuleResults as $validationResult) {
if ($validationResult->passed() == false) return false;
}
return true;
}
}
class ValidationRuleResult {
private $_passed, $_error_message;
function __construct($passed) {
$this->_passed = $passed;
$this->_error_message = '';
}
// Tells whether the form passed this validation rule or not
public function passed() { return $this->_passed; }
public function
// The error message should be empty if passed to avoid confusion
public function errorMessage { return $this->passed() ? '' : $this->_error_message; }
public function setErrorMessage($anErrorMessage) { $this->_error_message = $anErrorMessage; }
}
您可以通过这种方式创建验证规则:
class NotEmptyValidationRule extends ValidationRule {
public function validate($aForm) {
$fieldName = $this->fieldName();
$fieldValue = $aForm[$fieldName];
$passed = !empty($fieldValue);
$result = new ValidationRuleResult($passed);
if (!$passed) {
$result->setErrorMessage("$fieldName cannot be empty");
}
return $result;
}
}
注意事项:
- 我假设 $aForm 是字段名称/值的关联数组
- 您可以注意到,如果验证规则通过,则不会使用结果(因为
ValidationResult 类仅适用于未通过的结果)。请记住,这只是一个示例,仅用于帮助您,并非完整的解决方案。
用法
$rule = new NotEmptyValidationRule('name');
$validator = new FormValidator();
$validator->addRule($rule);
$aForm = $__POST['myForm'];
$validationResult = $validator->validate($aForm);
if ($validationResult->validationPassed()) {
$errorsFound = $validationResult->errorsFound();
// do something with the $errorMessage
$errorMessage = array_join('<br/>', $errorsFound);
}