2

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

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

4

2 回答 2

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 表单一无所知。这是一个反复重复的简单模式。

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

于 2013-10-15T03:09:53.373 回答
1

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

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

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

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

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

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

于 2013-10-14T23:44:00.333 回答