Php 如何在Zend框架中创建模块化MVC组件

Php 如何在Zend框架中创建模块化MVC组件,php,model-view-controller,zend-framework,Php,Model View Controller,Zend Framework,我在Zend Framework应用程序中创建模块化可重用组件时遇到问题。在本例中,我不是指Zend框架模块,而是指如果您愿意的话,具有可重用MVC widgety功能的能力。我遇到的问题对我的实现来说可能非常特殊,但如果有人能给我指出正确的方向,我完全乐意将它扔掉,重新开始。无论如何,细节和代码将有望更好地解释事情,即使我所做的不是最好的方式,它也应该显示我试图实现的目标: 一个简单的例子是邮件列表注册表单。我想在使用不同控制器的站点的几个页面上包括这一点,这在如何处理数据和返回相关消息方面提

我在Zend Framework应用程序中创建模块化可重用组件时遇到问题。在本例中,我不是指Zend框架模块,而是指如果您愿意的话,具有可重用MVC widgety功能的能力。我遇到的问题对我的实现来说可能非常特殊,但如果有人能给我指出正确的方向,我完全乐意将它扔掉,重新开始。无论如何,细节和代码将有望更好地解释事情,即使我所做的不是最好的方式,它也应该显示我试图实现的目标:

一个简单的例子是邮件列表注册表单。我想在使用不同控制器的站点的几个页面上包括这一点,这在如何处理数据和返回相关消息方面提出了一些问题。我不想做以下任何一项,因为它们真的很臭:

  • 创建一个基本控制器,其中表单处理位于和扩展(错误)
  • 相关控制器中的表单处理代码重复(甚至更糟!)
  • 对我来说,干净的方法是创建一个新的控制器来处理邮件列表表单数据,使用视图助手轻松地将表单和相关标记输出到所需的页面,然后重定向回表单处理后发生注册的页面。但是,我希望使用Zend_表单提供的表单验证,这意味着如果验证失败,但在同一请求中,我需要以某种方式将表单对象传递回视图帮助器。目前,我将其设置为视图中的一个变量,然后转发回上一页,而不是重定向,这是正常的(ish)。如果验证没有问题,那么我更愿意使用重定向返回到原始页面。但我在执行此操作时遇到了问题,因为我想将有关注册状态的消息传递回组件。通常我会使用FlashMessenger操作助手,在这种情况下,我可以为它命名名称,这样消息就不会与其他页面数据冲突,但我无法从视图助手中访问它。所以现在我也在转发这个案例。我更喜欢重定向,以防止用户刷新页面时表单重新提交,并保持URL干净。我意识到我本质上希望在一个页面中有一个微型MVC调度过程,我认为这就是操作堆栈的用途?我真的不知道很多关于这虽然和任何指针将不胜感激。以下是我当前的代码:

    控制器:

    <?php
    class MailingListController extends Zend_Controller_Action {
    
        public function insertAction() {
            $request = $this->getRequest();
            $returnTo = $request->getParam('return_to');
    
            if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
                $this->_redirect('/');
            }
    
            $mailingList = new Model_MailingList();
            $form = new Form_MailingList();
    
            $returnTo = explode('/', $returnTo);
    
            if($form->isValid($_POST)) {
                $emailAddress = $form->getValue('email_address');
    
                $mailingList->addEmailAddress($emailAddress);
    
                $this->view->mailingListMessages = $mailingList->getMessages();
                $this->view->mailingListForm = "";
            }
            else {
                $this->view->mailingListForm = $form;
            }
    
            $this->_forward($returnTo[2], $returnTo[1], $returnTo[0]);
        }
    }
    
    <?php
    
    class MailingListController extends Zend_Controller_Action {
    
        public function insertAction() {
            $request = $this->getRequest();
            $returnTo = $request->getParam('return_to');
    
            if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
                $this->_redirect('/');
            }
    
            $mailingList = new Model_MailingList();
            $form = new Form_MailingList();
    
            if($form->isValid($_POST)) {
                $emailAddress = $form->getValue('email_address');
                $mailingList->addEmailAddress($emailAddress);
            }
            else {
                $mailingList->setFormErrors($form->getMessages());
            }
    
            $redirect = rtrim($request->getBaseUrl(), '/') . $returnTo;
            $this->_redirect($redirect);
        }
    }
    

    我认为你的做法与我的做法非常接近。如果您将希望在页面中显示Zend_表单错误消息的要求放在一边,那么您要做的是:

    • 视图帮助器只显示表单(不需要将表单对象或消息作为参数)
    • 表单会像现在一样提交给其他控制器
    • 成功后,邮件列表控制器将重定向(而不是转发)回返回URL
    • 邮件列表控制器自行重新显示表单,并在失败时显示错误

    这使得一切变得更简单,唯一的问题是,如果存在任何验证错误,那么用户将丢失其上下文,并获得一个带有表单的普通旧页面,而不是它们所在的位置。然后,您可以通过将表单更改为通过提交来解决此问题(现在或以后)。而使用Ajax,并通过渲染错误。JS。但是这将是一个相当大的工作量。

    使用ajax似乎是一个最佳的方法。查看操作助手仅用于邮件表单的第一次加载

    控制器

    class MailingListController extends Zend_Controller_Action {
    
    public function insertAction() {
        $request = $this->getRequest();
    
        $form = new Form_MailingList();
    
        if ($request->isPost()) {
            if ($form->isValid($request->getPost())) {
                $mailingList = new Model_MailingList();
                $emailAddress = $form->getValue('email_address');
                $mailingList->addEmailAddress($emailAddress);
                $form = $mailingList->getMessages();
            }
        }
    
        $this->view->form = $form;
    }
    
    }
    
    class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
    
    public function mailingList() {
        $this->view->headScript()->appendFile('/js/mailing-list.js');
        return '<div id="mailing-list-wrap">' . $this->view->action('insert', 'mailing-list') . '</div>';
    }
    
    }
    
    查看脚本insert.phtml

    <?php echo $this->form; ?>
    
    查看帮助程序

    class MailingListController extends Zend_Controller_Action {
    
    public function insertAction() {
        $request = $this->getRequest();
    
        $form = new Form_MailingList();
    
        if ($request->isPost()) {
            if ($form->isValid($request->getPost())) {
                $mailingList = new Model_MailingList();
                $emailAddress = $form->getValue('email_address');
                $mailingList->addEmailAddress($emailAddress);
                $form = $mailingList->getMessages();
            }
        }
    
        $this->view->form = $form;
    }
    
    }
    
    class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
    
    public function mailingList() {
        $this->view->headScript()->appendFile('/js/mailing-list.js');
        return '<div id="mailing-list-wrap">' . $this->view->action('insert', 'mailing-list') . '</div>';
    }
    
    }
    

    好的,我想出了一个让我感到高兴的解决方案,解决了我所面临的一些问题。希望这能帮助那些面临类似问题的人。现在唯一的缺点是我在视图辅助对象中引用模型。我知道这不是松散耦合,但我以前见过好几次这样做,在中甚至建议使用它,以避免使用“action”视图帮助器(这将创建一个新的MVC调度循环)。总的来说,我认为干燥和封装是值得的,可能还有其他合适的行话

    为了能够使用从MailingListController返回的重定向,但维护来自模型的消息和任何表单验证错误,我需要将它们存储在会话中。对于消息,我通常会使用FlashMessenger操作助手,但由于在视图助手中获取此信息不是最佳做法,因此它不会处理我的表单错误,而且它真正做的只是将内容保存到会话中,这是不必要的。我可以在Model_MailingList中实现我自己的会话存储,我也可以将其用于表单错误。然后,我可以在重定向后用错误重新填充表单,并打印出任何相关消息。不管怎样,代码如下:

    控制器:

    <?php
    class MailingListController extends Zend_Controller_Action {
    
        public function insertAction() {
            $request = $this->getRequest();
            $returnTo = $request->getParam('return_to');
    
            if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
                $this->_redirect('/');
            }
    
            $mailingList = new Model_MailingList();
            $form = new Form_MailingList();
    
            $returnTo = explode('/', $returnTo);
    
            if($form->isValid($_POST)) {
                $emailAddress = $form->getValue('email_address');
    
                $mailingList->addEmailAddress($emailAddress);
    
                $this->view->mailingListMessages = $mailingList->getMessages();
                $this->view->mailingListForm = "";
            }
            else {
                $this->view->mailingListForm = $form;
            }
    
            $this->_forward($returnTo[2], $returnTo[1], $returnTo[0]);
        }
    }
    
    <?php
    
    class MailingListController extends Zend_Controller_Action {
    
        public function insertAction() {
            $request = $this->getRequest();
            $returnTo = $request->getParam('return_to');
    
            if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
                $this->_redirect('/');
            }
    
            $mailingList = new Model_MailingList();
            $form = new Form_MailingList();
    
            if($form->isValid($_POST)) {
                $emailAddress = $form->getValue('email_address');
                $mailingList->addEmailAddress($emailAddress);
            }
            else {
                $mailingList->setFormErrors($form->getMessages());
            }
    
            $redirect = rtrim($request->getBaseUrl(), '/') . $returnTo;
            $this->_redirect($redirect);
        }
    }
    
    
    
    我意识到我所面临的许多问题都是由一系列非常特殊的情况造成的,但希望这能在某种程度上帮助其他人

    干杯,
    Alex

    因此我发现您可以在视图助手中找到一个动作助手。您可以这样静态地执行:Zend\u Controller\u Action\u HelperBroker::getStaticHelper('flashMessenger')。但这感觉不是很MVC。我确信一定有更好的方法?所以我已经阅读了更多关于动作堆栈和动作助手的内容。两者都提供了解决此问题的潜在方法,但都会产生大量不必要的开销,甚至可能在ZF的未来版本中被删除。我发现本文很有用,并且似乎表明我正在以正确的方式编写一个操作帮助器,以处理视图帮助器中不应该存在的部分。有人有什么想法吗?谢谢,很高兴知道我在正确的轨道上。不过,确认错误和消息很重要,我非常喜欢
    <?php
    
    class Model_MailingList extends Thinkjam_Model_DbAbstract {
    
        private $_session;
    
        public function __construct() {
            $this->_session = new Zend_Session_Namespace(__CLASS__);
        }
    
        public function setFormErrors($errors) {
            $this->_session->formErrors = $errors;
        }
    
        public function getFormErrors() {
            $errors = array();
    
            if(isset($this->_session->formErrors)) {
                $errors = $this->_session->formErrors;
                unset($this->_session->formErrors);
            }
    
            return $errors;
        }
    
        // override addMessage and getMessages
    
        protected function addMessage($message) {
            if(!isset($this->_session->messages)) {
                $this->_session->messages = array();
            }
            $this->_session->messages[] = $message;
        }
    
        public function getMessages() {
            if(isset($this->_session->messages)) {
                $this->_messages = $this->_session->messages;
                unset($this->_session->messages);
            }
    
            return $this->_messages;
        }
    
        …
    
        public function addEmailAddress($emailAddress) {
            ...
    
            // I call this if db insert was successful: 
            $this->addMessage("Thank you. You have been successfully added to the mailing list.")
        }
    }
    
    <?php
    
    class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
    
        private $_mailingList;
    
        public function MailingList() {
            $this->_mailingList = new Model_MailingList();
            return $this;
        }
    
        public function getForm() {
            $request = Zend_Controller_Front::getInstance()->getRequest();
            $currentPage = '/' . $request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
    
            $form = new Form_MailingList();
            $form->setAction('/mailing-list/insert');
            $form->setCurrentPage($currentPage);
            $form->setErrors($this->_mailingList->getFormErrors());
    
            $html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form;
            $html .= $this->view->messenger($this->_mailingList->getMessages());
            $html .= '</div>';
    
            return $html;
        }
    
    }
    
    <?php
    
    class Form_MailingList extends Thinkjam_Form_Abstract {
    
        ...
    
        public function setErrors(array $errors) {
            foreach($errors as $key => $value) {
                $this->getElement($key)->setErrors($value);
            }
        }
    
    }
    
    <?=$this->MailingList()->getForm();?>