PHP MVC Fat控制器VS Fat模型

时间:2013-10-14 21:58:44

标签: php validation model-view-controller error-handling

假设我有一个用于发送电子邮件的基本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.表单字段的描述与输入名称不同,这使得难以自定义错误消息,同时保持模型可移植以便在其他应用程序中使用。似乎我所做的每一次尝试都变得荒谬可笑,但仍然没有达到与初始方法相同的灵活性。

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

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

因此,您的模型保持解耦,您可以在其他应用程序中重复使用它。

答案 1 :(得分:1)

我说不应该胖。处理HTML表单时,我认为最简单的方法是让Form对象(如CommentForm)具有字段对象(例如,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表单一无所知。这是一个简单的模式,一遍又一遍地重复。

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