PHP MVC Fat controller VS Fat model



假设我有一个用于发送电子邮件的基本HTML表单:

<form action="contactSubmit" method="POST">
    <label for="name" class="italic">Name:</label>
    <input type="text" name="name" value="" maxlength="20" required="required" autofocus="autofocus" />
    <label for="email" class="italic">E-mail:</label>
    <input type="email" name="reply_to" value="" maxlength="255" required="required" />
    <label for="comments" class="italic">Comments:</label>
    <textarea name="message" rows="10" cols="50" required="required"></textarea>
    <br />
    <input type="submit" class="submit" value="Send" />
</form>

目前,所有验证都在控制器中完成:

// submit contact request
public function contactSubmit() {
    // process form if submitted
    if ( $this->formSubmit() ) {
        // validate input
        $name = isset($_POST['name']) && $this->validate($_POST['name'], null, 20) ? $_POST['name'] : null;
        $reply_to = isset($_POST['reply_to']) && $this->validate($_POST['reply_to'], 'email', 255) ? $_POST['reply_to'] : null;
        $message  = isset($_POST['message']) && $this->validate($_POST['message']) ? $_POST['message'] : null;
        // proceed if required fields were validated
        if ( isset( $name, $reply_to, $message ) ) {
            $to = WEBMASTER;
            $from = 'nobody@' . $_SERVER['SERVER_NAME'];
            $reply_to = $name . ' <' . $reply_to . '>';
            $subject = $_SERVER['SERVER_NAME'] . ' - Contact Form';
            // send message
            $mail = $this->model->build('mail');
            if ( $mail->send($to, $from, $reply_to, $subject, $message ) ) {
                $_SESSION['success'] = 'Your message was sent successfully.';
            } else {
                // preserve input
                $_SESSION['preserve'] = $_POST;
                // highlight errors
                $_SESSION['failed'] = 'The mail() function failed.';
            }
        } else {
            // preserve input
            $_SESSION['preserve'] = $_POST;
            // highlight errors
            if ( !isset( $name ) ) {
                $_SESSION['failed']['name'] = 'Please enter your name.';
            }
            if ( !isset( $reply_to ) ) {
                $_SESSION['failed']['reply_to'] = 'Please enter a valid e-mail.';
            }
            if ( !isset( $message ) ) {
                $_SESSION['failed']['message'] = 'Please enter your comments.';
            }
        }
    }
    $this->view->redirect('contact');
}
我想远离"胖控制器"

,更多地转向"胖模型",但我一生都无法弄清楚如何将验证从前面的控制器干净地移植到处理模型:

public function send( $to, $from, $reply_to, $subject, $message ) {
    // generic headers
    $headers = 'MIME-Version: 1.0' . PHP_EOL;
    $headers .= 'From: ' . $from . PHP_EOL; // should belong to a domain on the server
    $headers .= 'Reply-to: ' . $reply_to . PHP_EOL;
    // send message
    return mail( $to, $subject, $message, $headers );
}

表单只有 3 个必填字段,而模型的方法接受 5 个。表单字段的描述与输入名称不同,这使得很难自定义错误消息,同时保持模型可移植性以用于其他应用程序。似乎我所做的每一次尝试最终都变得非常胖,并且仍然没有达到与最初方法相同的灵活性。

有人可以向我展示一种将验证从控制器移动到模型的干净方法,同时仍然保持自定义错误消息的灵活性,并保持模型的可移植性以用于其他应用程序?

首先,你真的在使用纯OOP吗?为什么要对外部数据使用模型方法?模型应该使用它自己的属性。如果创建对象所需的数据无效,则不应实例化模型对象。

您需要创建更多与模型通信的层。例如,您可以有一个可以处理此验证的模型工厂层。假设你有一个用户模型,在模型工厂层你有一个用户工厂。你可以把验证逻辑放在这里。如果创建用户模型所需的数据无效,则甚至不要创建模型。

一种更抽象的方法是添加更多层,例如 dataTransferObjects 层 。在这里,您可以拥有将数据传输到模型或模型工厂的对象。同样,您可以在此处放置验证逻辑,然后可以使用 modelFactories 从 UserDTO 对象(用户数据传输对象)创建用户模型。

问题是,您永远不应该在没有事先准备数据的情况下创建模型,例如验证它。这就是为什么模型应该使用它自己的属性,并且不直接传入外部数据(只传递数据来初始化对象)。

您应该阅读有关领域驱动设计和设计模式的更多信息。

因此,通过这种方式,您的模型保持解耦,您可以在其他应用程序中重用它。

我想说两者都不应该胖。在处理HTML表单时,我认为拥有像CommentForm这样的Form对象是最干净的,该对象具有执行自己的字段级验证的字段对象(例如,EmailField extends Field)。

如果表单通过验证,则可以保证您拥有干净的数据。窗体还可以创建和返回各种模型,这些模型本身也会在成员级别进行验证。但没有什么是胖的...每个位只需要知道与他们直接相关的东西。

控制器如下所示:

if ($form->validate($request->post())) {
  // grab the email model from the form
  $email = $form->getEmail();
  // assume $mailTransport implements some mailer interface
  if ($mailTransport->send($email) == true) {
    // sent email
    return $response->redirect('success');
  }
  else {
    // something unexpected happened
    $view->flashError('Unable to send email');
  }
}
$view->form = $form;

每个处理的控制器最终看起来都非常相似且非常干净。这些模型对HTML表单一无所知。这是一个很容易一遍又一遍重复的模式。

有很多方法可以做到这一点;以上恰好是我最喜欢的通用解决方案。

最新更新