入境时或保存前验证?

时间:2011-03-31 20:39:26

标签: php oop validation

我的大脑开始受伤所以我决定在这里问。

我有一个数据对象Employee。 Getters,setter,formatters等我有一个管理器EmployeeManager,它处理数据库访问和其他事情。现在我在EmployeeManager中有一个大的验证块,但我一直想知道我是否可以将其中一些移动到setter。

例如,我现在有;

public function getSSN($bFormatted = true) { 
    return ($bFormatted) ? $this->format_ssn($this->SSN) : $this->SSN; 
}
public function setSSN($s, $bValidate = false)
{
    // If we're validating user entry, save a copy.
    // Either way, store a trimmed version.
    if ($bValidate): $this->SSNToValidate = $s; endif;
    $this->SSN = str_replace('-', '', $s);
}
public function getSSNToValidate() { return $this->SSNToValidate; }

这是做什么的:
*当你设置一个SSN时,如果它是由系统完成的(比如从数据库中),那么它就是setSSN('123456789', false),因为SSN存储在数据库中没有破折号。
*当您从用户输入设置SSN时,它只是setSSN('123-45-6789'),然后不仅修剪破折号,而且还存储要验证的原始版本(因为我想根据格式进行验证)
*当你得到一个SSN时,如果要求格式化(除了你写入数据库之外总是这样),它会根据Employee类中的另一个函数对其进行格式化。

所以我的问题是:我可以在这里将验证添加到setter,而不是依赖于Manager类中的单片验证函数吗?因为我开始不得不处理来自整个应用程序的错误,所以我决定暂时转移到一个中央的错误处理程序静态类,而不是每个管理器都维护自己的错误列表。

因此,我可以轻松地为此添加错误处理:

public function setSSN($s, $bFromUser = false)
{
    if ($bFromUser && !$this->validateSSN($s))
    {
        ErrorHandler::add(array('ssn' => 'Invalid SSN entered')); 
    }
    else
    {
        $this->SSN = str_replace('-', '', $s);
    }
}

所以我想我的问题是:这是否有意义,或者我是通过将管理器中的验证(按需执行或者在写入数据库之前)移动到对象(将在执行时执行)来自我调整)?

这是一般的通用,我只是使用SSN作为一个很好的例子。

2 个答案:

答案 0 :(得分:3)

您应该始终在创建对象或在对象中设置值时进行验证。这样,您始终可以保证有一个处于有效状态的对象。如果验证不在对象本身中,则无法保证对象将被验证。

对象外的示例验证:

在这种情况下,有一个错误,我们忘记验证SSN。

class EmployeeManager
{
    function saveEmployee($employee)
    {
        //Oops, we forgot to validate SSN
        $db->save($employee);  //This is just SQL to persist the employee.
    }
}

//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone");  //No validation is done here.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee);  //This call will persist FredFlintstone because validation was missed.

对象内部的示例验证:

在这种情况下,employee对象验证其所有输入。这样我们就知道如果我们有员工的实例,那么其中的所有数据都是有效的。

class Employee
{
    function setSSN($input)
    {
        if(strlen($input) != 9)
        {
            throw new Exception('Invalid SSN.');
        }
        //Other validations...
        $this->ssn = $input;
    }
}

//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone");  //This call will now throw an exception and prevent us from having a bad object.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee);

此外,您永远不应该允许创建未完全初始化的对象。如果说员工需要SSN,则不提供空构造函数。您的构造函数应该包含所有必填字段的参数(与我为清晰起见省略它们的示例不同)。

答案 1 :(得分:2)

基于服务的应用程序的一般经验法则是,在持久性之前不久,验证应始终始终发生在服务器端。您可以选择在客户端上执行验证,以获得更好的用户体验。但是,仅在客户端(即在您的setter中)执行验证是不安全的。永远不要依赖客户数据。

在进行基于客户端的验证时,每个类(无论是模型,视图模型,演示者等,取决于您的架构模式)最好自我验证,而不是依赖于某些外部验证器。