从今天起,我开始在模型层而不是控制器中验证表单数据。我将尽可能缩短代码片段。
这是来自我的User
域对象的方法(setLastName()
方法基本相同)
public function setFirstName($firstName) {
if(!$firstName) throw new 'InvalidArgumentException('Some message');
if( strlen($firstName) < 2 || strlen($firstName) > 20 ) throw new 'LengthException('Some message');
if(preg_match('/[^a-zA-Z''.-'s]/', $firstName)) throw new FormatException('Some message');
$this->firstName = $firstName;
}
在我的控制器中,我有类似于的东西
$userService = $this->serviceFactory->build('User');
try {
$userService->register('John', 'M');
}
catch('InvalidArgumentException $ex) {
}
catch('LengthException $ex) {
}
catch(etc etc)
在我的UserService
方法register()
中,我有一些类似的东西
$user->setFirstName($firstName);
$user->setLastName($lastName);
当运行setFirstName()
方法时,它将成功设置提供的名字。setLastName()
方法将抛出一个LengthException
,因为它太短。
这就是我想要的,但当它返回到服务层,然后返回到控制器,我捕捉到它时,我知道抛出了LengthException
,但我不能给用户一个合适的消息,比如"提供的姓氏太短",因为我不知道异常是为哪个字段抛出的,只知道异常的类型。
我该如何解决这个问题?谢谢
View实例应该从模型层请求信息。控制器不负责传递信息。
这也意味着你强迫性地使用异常,导致抽象层泄漏,这将是完全没有意义的。"错误"只是模型层的一种状态。这是预期的情况,并非例外。
MVC中的控制器负责更改模型层的状态和(很少)当前视图实例的状态。他们不应该从服务部门收到任何反馈。
与其返回一些消息,不如返回一条有用的错误消息,如"提供的名字太短"。然后可以将其返回给用户。
或者,您可以看到,在扩展异常时,您可以指定其他信息,如数字代码,当然也可以使用此信息。
当然,您可以为不同的情况创建Exception的子类,但最终可能会有数百个Exception
子类,这当然会很混乱。
我有同样的问题。我认为,大多数认为所有验证都应该在模型中完成的人自己从来没有开发过完整的PHP MVC应用程序,他们只知道书籍和理论。从来没有看到过关于该主题的任何代码。
不管怎样,我已经想到了一个可能的解决方案。你对下面的代码有什么看法:
// Controller
$user = User::make(
$_POST['lastname'], $_POST['firstname'],
$_POST['gender'], [...]
);
if(is_array($user)) {
// store the errors in a view variable and forward, or store in session and redirect
$_SESSION['errors'] = $user;
$this->_redirect('add');
exit;
}
// Model
public static make($lastname, $firstname, $gender, [...]) {
$errors = array();
if(/* test firstname */) $errors[] = 'model_error_firstname';
if(/* test lastname */) $errors[] = 'model_error_lastname';
if(!empty($errors)) return $errors;
return new User($lastname, $firstname, $gender, [...]);
}
该模型将有一个静态函数,如果出现问题,它将返回一个有错误的数组,或者如果验证正常,则返回一个新的模型对象
在控制器中,您可以测试是否返回了数组。
也许我会把User的构造函数设置为私有的,因为如果你直接用构造函数构建用户,你会跳过所有的验证。但这并不意味着它变成了一个单身汉。
也许我也会在将表单字段传递给模型之前对其进行消毒并确保其安全。
类似model_error_xyz这样的键可以在带有适当文本的翻译文件中找到。
更新:
实际上,我认为您可以从构造函数中抛出一个自定义异常,该异常包含一组消息。但我没有提出这个建议的原因是,它导致了半构建的对象,至少在Java中是这样,但嘿,PHP不是Java。。。
您还必须验证每个setter函数:(在模型实例中进行验证似乎很乏味
欢迎有任何想法。