验证域对象设置程序中的表单输入


Validate form input in Domain Objects setters?

自从我开始学习MVC以来,我一直在控制器中验证我的表单数据,这是我在浏览CodeIgniters代码时养成的习惯,但我了解到它执行某些操作的方式并不是最好的,它只是完成了任务。

域对象是否应验证所有表单数据?如果是这样的话,应该在像这样的setter中进行

public function setFirstName($firstName) {
    // Check if the field was required
    if(!$firstName) {
        throw new InvalidArgumentException('The "First name" field is required');
    }
    // Check the length of the data
    // Check the format 
    // Etc etc
}

此外,例如,如果我正在处理基本用户注册,我的User类没有$confirmPassword属性,所以我不会进行

$user->setConfirmPassword($confirmPassword);

检查输入的两个密码是否相等的一种方法是设置$password并执行类似的操作

$user->setPassword($password);
if(!$user->matchPassword($confirmPassword)) {
    throw new PasswordsNotEqualException('Some message');
}

我想这会在服务层完成吗?

任何能帮助我朝着正确方向前进的建议都会很棒。谢谢

域对象是否应验证所有表单数据?如果是的话应该像那样在setter中完成吗

IMO您应该只允许创建有效的对象,存档的最佳方法是在创建对象的方法中进行这些检查。

假设用户的名字不能更改,您将在创建用户时对其进行验证。这样,你就忘记了二传手,因为你不再需要它了。

在某些情况下,您可能希望更改属性,并且也需要对其进行验证(因为如果是这样的话,更改可能会导致从有效对象变成无效对象(。

检查输入的两个密码是否相等的一种方法是设置$密码并执行以下操作。。。

你可以用同样的方式处理这个问题:有一个Password对象,它在创建时检查密码和确认。如果您有一个有效的Password实例,您可以在知道它通过了您指定的所有验证的情况下使用它。

参考

这些设计原则(从一开始就完整有效的对象等(来自Hernan Wilkinson的"Patagonia背后的设计原则"。请务必查看ESUG 2010视频和演示幻灯片。

我最近回答了另一个关于验证属性的问题,我认为您可能会派上用场:https://stackoverflow.com/a/14867390/146124

干杯!

TL;DR

不,setter不应该验证数据。nick2083是完全错误的。

更长的版本

根据Tim Howard提供的定义[source],域对象可以验证它们所包含的域信息的状态。这基本上说明,要想真正拥有一个域对象,所述对象需要能够验证自己。

何时验证

你基本上必须选择:

  • 在每个setter中验证
  • 有一种方法来验证整个对象

如果验证是setter的一部分,那么有一个主要缺点:setter的顺序很重要。

示例:假设您正在申请人寿保险。很有可能,当保单触发时(被保险人死亡(,你会有一个域对象,其中包含被保险人和获得保费的人。你必须确保收件人和被保险人不是同一个人。但是没有规则可以控制你执行setter的顺序

当域对象中有两个或多个参数,它们必须相互验证时,实现会变得有点模糊。最可行的解决方案是检查何时分配了所有参数,但此时您已经失去了在setter中验证的好处:代码的执行已经超过了无效数据的来源。

您将如何处理域对象的有效状态没有设置参数a的情况?如果参数B很大,则21和C已经设置?

结论:当您有非常简单的域对象,没有复杂的验证时,setters中的验证只是可行的解决方案;规则