MVC问题:我应该在控制器或模型中放置表单验证规则吗?

时间:2011-04-13 14:49:43

标签: php codeigniter

一方面,表单验证可以被视为应用程序逻辑的一部分,因此属于模型。

另一方面,它直接处理来自视图的输入并处理显示错误等。从这个角度来看,将它放入控制器更有意义。

从MVC的角度来看哪一个是正确的方法?

P.S我的表单验证实际上只包括编写字段列表,它们的规则,并将其传递给表单验证库,它返回true / false是否通过验证。

示例:

$this->load->library('form_validation');
$this->form_validation->set_rules('name', 'Name', 'required');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email');
//........
if ($this->form_validation->validate())
    // Process data
else
    $this->register_form(); //A controller action that will show a view with errors

是否应该将其放入控制器或模型中?

9 个答案:

答案 0 :(得分:72)

理想情况下,您需要3层验证:

  1. 查看:客户端(javascript,html5验证等)。这会在数据到达控制器之前捕获明显的错误和遗漏,浪费用户的时间并在出现错误时调用不必要的页面加载。
  2. 控制器:这是您的表单验证图层。控制器通常用于直接处理输入,并将其发送到模型。表单中的每个字段都非常罕见,您的数据库中有一个直接相关的列,您通常需要在将数据传递给模型之前以某种方式更改数据。仅仅因为您需要验证一个名为“确认电子邮件”的字段,并不意味着您的模型将处理“确认电子邮件”值。有时,这将是最后的验证步骤。
  3. 模型:这是验证的最后一道防线,也可能是您将数据发送到模型而不是直接来自表单的唯一验证帖子。有很多时候需要从控制器调用向数据库发送数据,或者使用非用户输入的数据。我们不希望看到数据库错误,我们希望看到应用程序本身抛出的错误。模型通常不应直接处理$ _POST数据或用户输入,它们应该从控制器接收数据。您不希望像电子邮件确认一样处理无用的数据。

答案 1 :(得分:19)

验证是Model的问题。只有模型知道您的数据应该是什么样子。您在模型中描述了数据字段,因此您应该在同一位置描述此字段的验证规则。

这对我来说似乎很明显,但我很乐意听取对手的意见。

答案 2 :(得分:12)

我想说在大多数情况下,表单验证代码应该在控制器(而不是模型)中。

Madmartigan在上面的评论中说得最好“表单验证!==数据验证。并非所有表单都与模型互动”

Web表单在逻辑上是MVC的View / Controller部分的一部分,因为用户在视图中与它们交互。

答案 3 :(得分:5)

似乎每个人总是说模型提到这个问题,这有其优点(与相反的情况相比),但我认为这个问题的答案更加微妙。 应在模型上执行数据本身的验证。

但还有其他类型的验证,例如表单是否已提交了意外字段(显然是出于安全目的),或者用户是否有权请求操作。通过在模型中放置这些类型的验证,它可以粘合模型(数据的抽象)以完全分离事物,例如用户系统如何工作或如何为安全目的评估表单提交。

您可以想象更改其中一个类或类系统,然后弄乱,因为您还必须更改所有模型。而控制器是客户端输入和数据之间的中介:在该角色中,它们是上述示例的适当验证器,可能还有许多其他验证器。

答案 4 :(得分:2)

考虑到其他答案(以及一些研究),如果您必须使用非空字段,电子邮件验证和内容等规则验证数据,则Controller不应让这些数据通过自身,但如果您有规则例如“只有信誉大于150的用户可以拒绝回答”,你应该在模型层中这样做。

如果您想要进行业务规则验证,我建议您在软件的任何部分使用Business Object Pattern这样的对象,当您想要“拒绝回答”时,您就拥有了自己的业务逻辑保留和集中。

答案 5 :(得分:2)

这是一个有趣的理论讨论,但如果我们关注的问题是在Codeigniter(CI)的背景下做出的:

在CI中,您可以指定自定义验证规则,如下所示:

$this->form_validation->set_rules('email', 'Email', 'required|callback_my_validation');

在这种情况下,您必须定义一个名为“my_validation”的公共函数,该函数必须返回true或false,并且框架会将错误(如果返回false)添加到一堆错误。

所以...如果你把这个代码放在控制器中,你就是无意中暴露公共网址,这意味着它可能会调用类似“http://yoursite.com/my_validation”的东西(我不喜欢不认为你打算这样做。 保护此URL的唯一方法是进入“routes.php”文件并阻止访问此URL。这似乎并不实用,似乎指出CI开发人员希望我们在模型中处理验证的方向。

答案 6 :(得分:1)

该模型应验证自己的数据。

假设您有联系模式,只需要名字和电话号码。它应该验证填写的名字和电话号码。

但是,如果此联系人型号是报价的一部分,您可能还需要一个全名和电子邮件地址。

在这种情况下,您可以扩展Contact模型(成为QuoteContact模型)并添加更多验证,或者您可以在Quote模型上进行额外验证。

您应该编写模型以便在其他应用程序中重复使用(即使它们永远不会),因此它们应独立于控制器。如果验证在控制器中,那么如果切换到说命令行版本,则会丢失这些验证。

答案 7 :(得分:0)

如果使用codeigniter在服务器端验证表单,则在控制器中验证

您需要使用像此一样的自动加载包含form_validation库

$autoload['libraries'] = array("form_validation") 

或者直接在Controller中加载

$this->load->library('form_validation');

然后将验证规则设置为每个表单字段

$this->form_validation->set_rules('username', 'User Name', 'required');
$this->form_validation->set_rules('useremail', 'User Email', 'required|valid_email');

如果在验证表单字段后发现任何错误,则会捕获验证函数

if ($this->form_validation->validate()) {
    //return back to form
} else {
    //successful validate all field 
}

答案 8 :(得分:0)

其他答案中没有涉及到另一个角度。这取决于你在说什么控制器/视图!如果Javascript在用户输入时检查验证,则出于安全原因,您的后端也应该进行验证(这可能会再次出现在后端的控制器中)或模型,因为任何人都可以在没有浏览器的情况下通过Ajax推送数据。

对于性能原因,您应该在前端控制器/视图中进行验证,因为每次用户选择无效的出生日期或某事时您都不想访问数据库

除了M,V,和/或 C中验证的理论基础之外,您还必须考虑前端与后端的实用性,而不考虑MVC。

我个人的建议是不要仅限于一级验证。错误的验证(如其他答案中提到的确认密码示例)会对架构产生严重影响。