在哪里验证用户输入?

时间:2016-03-25 11:31:28

标签: php validation oop

我正在开发一个使用面向对象方法的新应用程序,其中涉及一些REST,我没有使用任何框架。

我遇到的问题是在下面的setter中验证用户输入的最佳位置:

public function setSalary($salary)
{
    if (Validator::money($salary))
        $this->salary = $salary;
    else
        return 'Error that is an invalid number';
}

还是在控制器中?

public function updateSalary()
{
    $errors = array();

    if (Validator::money($_POST['salary']))
        $salary = $_POST['salary'];
        else
            $errors ['salary']  = 'Error that is an invalid number';

    if(count($errors))
        return $errors;

    $employee = new Employee($_POST['e_Id']);
    $employee->setSalary($salary);

    $employee->save();
}

如果我要插入setter,我的控制器应如何显示,并返回验证错误?

我已经看到大多数人在控制器中进行验证,但我认为应该是验证的模型,因为它将使用数据,我们可以重用该模型而不重复自己。 但是,有时候验证规则可能需要在某些特殊情况下有所不同,例如对不同视图的不同验证或对晚餐管理员的不同验证。

您认为哪一个符合最佳做法?

5 个答案:

答案 0 :(得分:7)

首先,由于您似乎希望实现类似MVC的结构,让我们从一些常见错误开始,这些错误与验证无直接关系。

  • 只有部分代码(包含PHP超级全局)应该是引导阶段。在你的代码中撒满了超级全局,这使得测试变得非常困难。您的代码也会通过<input>名称与您的HTML紧密结合。

  • 即使您的forif语句包含一行,也应始终使用大括号。好吧,一般来说,您的代码应遵循PSR-1PSR-2指南。

  • 控制器不应该有任何逻辑,也不应该处理数据保存。阅读this post,也许它会清除一些内容。

好的..现在回到原来的主题。

一般来说,有两种思想流派:

  1. 您在域实体中进行验证

    您的域实体(在您的情况下为Employee)包含与其相关的所有业务角色。如果它处于有效状态,它可以使用这些规则进行评估。

    代码会是这样的:

    $employee = new Entity\Employee;
    $employee->setID($id);
    $employee->setSalary($money);    
    if ($employee->isValid()) {
        $mapper = new Mapper\Employee($dbConn);
        $mapper->store($emplyee);
    }
    
  2. 您永远不会创建无效的域名实体

    此方法来自DDD,其中您的域实体由其他类创建,并且它只能从一个有效状态更改为另一个有效状态。基本上,如果你想探索这种方法,你必须阅读this book(可能好几次)。

  3. 此外,还有另一个验证表单,前两个说明涵盖了注释:数据完整性检查。这是验证的类型,实际上是我的RDBMS。例如,UNIQUE约束。

    当您遇到ans完整性违规时,它通常会抛出您在服务层中处理的异常。

答案 1 :(得分:1)

每次向数据库写入数据时都必须调用验证。所以在这种情况下来自控制器。实际验证发生在模型中。模型是对象,它知道它的字段遵循哪些规则,并且可以检查数据是否有效。此外,该模型是世界其他地方和数据库之间的边界。所以,我会做这样的事情:

public function updateSalary()
{
    $employee = new Employee($_POST['e_Id']);
    $employee->setSalary($_POST['salary']));
    if ($employee->validate()) {
        $employee->save();             
    } else {
        return $employee->getErrors();
    }
}

为什么我这样给你:

  • 因为您将验证保留在一个地方。稍后,如果要验证另一个字段,则将再次调用validate()方法。您不会为每个字段或类写另一个验证;
  • 您可以创建一个基类并将validate()方法放在那里 - 所有客户端都将调用validate()方法,而不关心字段的细节。 validate方法仅关注要验证的内容 - 哪些字段和规则是什么。此信息将在特定(子)类中设置,如Employee类。
  • 如果您只想验证一个字段(如您的情况),则在validate()方法中,您可以简单地检查哪些字段已更改,并仅对这些字段进行验证。

答案 2 :(得分:0)

取决于您,如果验证规则是“全局”的,换句话说,如果每次更新数据库表/对象适当时它们是相同的,则将它们放在模型中,否则验证控制器中的用户输入在不同的情况下,您需要为同一个实体提供不同的验证规则。

答案 3 :(得分:0)

首先,我不是下面的怪人就是我的想法。

它应该在控制器中完成,只是因为现在你只是验证号码,这只是简单的检查,我认为你只需要为此应用正则表达式。

我实际上理解的是,模型是您保持业务逻辑的地方,但如果您的字段值总是错误的,那么您永远不会处理业务逻辑,并且您不希望您的模型发挥作用。

答案 4 :(得分:0)

我建议尽可能在模型中应用验证。它的优点是可以以更完整的方式直接测试模型,并保证模型只保留有效数据。

当然,Controller需要处理验证,并且可能是第一个在涉及分布式项目的复杂验证时调用验证的层。但是在你给出的例子中没有这样的复杂性。

请注意,无论如何,某些验证甚至会由数据库引擎执行(例如require('models/user')和主键要求)。

我还建议在模型中使用异常,因为这可以保证运行函数的中断,并允许您在Controller中以类似的方式处理所有(验证)错误。我建议配置数据库访问层以触发异常。如果是PDO,您可以按如下方式进行:

node_modules

在模型中,您将在验证失败时抛出异常:

NOT NULL

在Controller中你会发现错误并记录它们:正如你在 $ errors 中所做的那样,但是我会将它们保留在模型中,以便以后访问View。这说明了模型如何检测验证错误,但Controller处理它。

我还建议不要直接创建一个Employee实例,而是让Model为你做这个:

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

使用发布的参数调用后一个函数,因为这可以更好地指示该方法用作输入的内容。顶级PHP代码如下所示:

public function setSalary($salary) {
    if (!Validator::money($salary)) {
        throw new Exception('Invalid value provided as salary.');
    }
    $this->salary = $salary;
}

View将访问记录的错误以将其报告给客户端。

我意识到关于在哪里检测验证错误,在哪里处理它们,何时触发异常(以及什么时候没有)等等的争论将永远不会结束。但这对我有用。