【问题标题】:Model now doing data/form validation. How to return user friendly error messages to the view?模型现在进行数据/表单验证。如何将用户友好的错误消息返回到视图?
【发布时间】:2013-02-23 18:22:03
【问题描述】:

从今天开始,我开始在模型层而不是控制器中验证表单数据。我会尽量缩短代码sn-ps。

这是我的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,但我不能给用户一个正确的消息,比如“提供的姓氏太短”,因为我不知道哪个字段抛出了异常,只知道异常的类型。

我该如何解决这个问题?谢谢。

【问题讨论】:

  • 为什么控制器需要知道模型层状态的变化?这种对异常的痴迷是什么?!
  • 我过去的做法很好,但让控制器有点胖,所以我听说应该在模型层进行表单/日期验证,所以我开始在域的设置器中进行对象。您看到每个人都说以某种方式做到这一点,但是没有示例很难做到。如果消息不首先返回到控制器,你将如何将消息传递给视图?而且我不知道对异常的痴迷是什么,我只是在我读过的很多文章中看到它们,所以养成了我想的习惯。
  • .. 因为互联网上的每个 php 代码示例都只包含好的代码。太棒了!

标签: php oop model-view-controller exception


【解决方案1】:

视图实例应该从模型层请求信息。控制者不负责传递信息。

这也意味着你强迫性地使用异常,这会导致你的抽象层泄漏,这将是完全没有意义的。 “错误”只是模型层的一种状态。这是预期的情况,并非例外。

MVC 中的控制器负责改变模型层的状态和(很少)当前视图实例的状态。他们不应该收到来自服务的任何反馈。

【讨论】:

  • 好的,谢谢。您能否指导我或向我展示一个示例,说明我将如何验证模型层中的表单中的数据,用于名为User 的域对象,属性为$firstName,如果“名字”表单字段太长/short 我如何在视图中向客户端/用户收到某种长度无效的消息?我读过无数文章,看过无数关于 MVC 的视频,它们几乎都在说不同的东西。它开始让我发疯。你应该用 PHP 写一本关于 MVC 的书,在这个阶段我会付给你 1000 欧元。
【解决方案2】:

与其返回一些消息,不如返回一个有用的错误消息,例如“提供的名字太短”。然后可以将其返回给用户。

或者你可以看到extending exceptions你可以指定额外的信息,比如数字代码——你当然可以使用它。

当然,您也可以针对不同的情况创建 Exception 的子类,但最终可能会产生数百个 Exception 子类,这当然会很混乱。

【讨论】:

  • 感谢您的回复。我一直很想这样做,但我在这里读到了phpmaster.com/exceptional-exceptions,“你的调用代码永远不应该读到这条消息。这条消息唯一有用的是开发人员”。我相信那个网站上的作者非常好,所以我想坚持他在那篇文章中所说的话。必须有另一种方法来做到这一点,而不会让事情变得太混乱,但我似乎无法在任何地方找到答案。
  • 我为我的答案添加了更多替代方案
【解决方案3】:

我也有同样的问题。我认为大多数说所有验证都应该在模型中完成的人自己从未开发过完整的 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 的构造函数设为私有,因为如果您直接使用构造函数构建用户,您将跳过所有验证。但这并不意味着它变成了 Singleton。

也许我还会在将表单字段传递给模型之前清理表单字段并使其安全。

诸如 model_error_xyz 之类的键可以在带有适当文本的翻译文件中找到。

更新:

实际上,我认为您可以从构造函数中抛出一个自定义异常,其中包含一组消息。但是我没有提出这个的原因是它导致了半构造的对象,至少在 Java 中是这样,但是,PHP 不是 Java...

您还必须验证每个 setter 函数 :( 在模型实例中进行验证似乎很乏味。

欢迎提出任何想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-25
    • 2020-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多