2012-01-03 44 views
3

我一直在我的Zend Framework應用程序中創建模塊化可重用組件。在這種情況下,我不是指Zend Framework模塊,而是指能夠擁有可重用的MVC widgety的東西,如果你喜歡的話。我遇到的問題可能對我的實施非常特別,但如果有人能指引我走向正確的方向,我很樂意將其拋出並重新開始。無論如何,具體細節和代碼將有望更好地解釋事情,即使我正在做的不是最好的方式,它應該顯示我想要實現的:如何在Zend框架中創建模塊化MVC組件

一個簡單的例子是郵件列表註冊表單。我想在網站的幾個頁面上使用不同的控制器,這在如何處理數據和返回相關消息方面存在一些問題。我不想請執行下列操作,因爲他們真的聞:

  1. 創建與表單處理的基本控制器和擴展(壞)相關控制器(更糟糕的是
  2. 重複的表單處理代碼! )

乾淨的路要走感覺我創建一個新的控制器來處理郵件列表形式的數據,使用一個視圖助手可以輕鬆地輸出表和相關標記到所需的頁面,然後重定向回在表單處理完畢後發生註冊的頁面。然而,我想使用Zend_Form提供的表單驗證,這意味着如果驗證失敗,我需要將表單對象傳回給視圖幫助器,但在相同的請求中。我目前正在通過將它設置爲視圖上的一個變量,然後轉回到上一頁而不是重定向來做到這一點,這是好的(ish)。如果驗證確定,那麼我寧願使用重定向回到原始頁面。我在做這件事時遇到了麻煩,因爲我想將消息傳遞迴組件以瞭解註冊狀態。通常我會使用FlashMessenger Action Helper,在這種情況下我可以命名空間,所以消息不會與其他頁面數據衝突,但我無法從View Helper中訪問它。所以目前我也在這裏轉發。如果用戶刷新頁面並保持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]); 
    } 
} 

的return_to是包含當前URI(模塊/控制器/動作),這是在視圖助手產生的字符串。我寧願在$ form-> isValid($ _ POST)塊內重定向。

視圖助手:

<?php 
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract { 

    public function mailingList($form, $messages = "") { 
     if(!isset($form)) { 
      $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); 
     } 

     $html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form; 
     $html .= $messages; 
     $html .= '</div>'; 

     return $html; 
    } 

} 

獲取前端控制器的視圖助手的一個實例是不理想,但我寧願封裝儘可能多地。

如果我有一個表單驗證失敗的對象,我可以將它傳遞迴助手以輸出錯誤消息。如果我有一些消息要呈現,我也可以將它們傳遞給幫手。

在我看來,劇本我使用的幫手,像這樣:

<?=$this->mailingList($this->mailingListForm, $this->mailingListMessages);?> 

如果沒有mailingListForm或mailingListMessages對鑑於MailingListController被設置,它會輸出一個新形式的任何消息。

任何幫助,非常感謝!

+0

所以我發現,你可以在一個視圖助手得到一個動作助手的保持。你可以這樣靜態地執行:Zend_Controller_Action_HelperBroker :: getStaticHelper('flashMessenger')。儘管如此,這並不是MVC。我相信肯定有更好的方法? – baseten 2012-01-03 22:33:05

+0

所以我一直在閱讀更多關於動作堆棧和動作助手的信息。兩者都提供瞭解決這個問題的潛在方法,但都會產生大量不必要的開銷,甚至可能在ZF的未來版本中被刪除[link](http://www.rmauger.co.uk/2009/03/why-the -zend-framework-actionstack-is-evil /)我發現這篇文章很有用,它似乎暗示我正在以正確的方式[鏈接](http://robertelwell.info/blog/zend-partial-vs -helper /)我正在考慮編寫一個動作助手來處理我的視圖幫助器中不應該存在的部分。有人有想法嗎? – baseten 2012-01-04 10:25:13

回答

2

使用ajax似乎是一種最佳方式。 View Action Helper僅用於郵件表單的第一次加載。

控制器

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; 
} 

} 

視圖腳本 insert.phtml

<?php echo $this->form; ?> 

Form類

class Form_MailingList extends Zend_Form { 

public function init() { 
    //among other things 
    $this->setAttrib('id', 'mailing-list-form'); 
    $this->setAction('/mailing-list/insert'); 
} 

} 

視圖助手

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>'; 
} 

} 

JS文件郵件,list.js

$(document).ready(function() { 
    $('#mailing-list-form').submit(function() { 
     var formAction = $(this).attr('action'); 
     var formData = $(this).serialize(); 

     $.post(formAction, formData, function(data) { 
      //response going in form's parent container 
      $(this).parent().html(data); 
     }); 

     return false; 
    }); 
}); 
+0

好吧,很酷。是的,這將工作,控制器可能需要調整以允許表單驗證消息。有沒有必要使用動作視圖助手?那可以用Form_MailingList的新實例來替換嗎? jQuery的處理代替標記呢? – baseten 2012-01-04 17:34:58

+0

應該支持表單驗證信息,以及最終的msg,我想它會是「你已經註冊了!」或者其他的東西。 我只是使用相同的變量($ form)爲表單對象(包含表單驗證信息)和最終的味精。 我想有可能跳過行動助手 - 我可能不會。 乾杯 – Weltschmerz 2012-01-04 18:29:22

+0

對不起,是的,我現在看到驗證消息在那裏,重用$ form變量是有道理的。乾杯 – baseten 2012-01-04 21:19:07

0

我認爲你做這件事的方式非常接近我的目標。如果拋開想在頁面中顯示Zend_Form的錯誤消息的要求,那麼你做什麼,而不是爲:

  • 視圖助手只是顯示形式(它並不需要採取的形式對象或消息作爲參數)
  • 的形式提交到其他控制器象現在這樣
  • 郵件列表控制器重定向(而不是轉發)返回成功返回URL
  • 郵件列表控制器重新顯示在表格的自己的,以及失敗時的錯誤

這使得一切都變得更簡單,唯一的問題是,如果有任何驗證錯誤,那麼用戶會丟失它們的上下文,並獲取一個帶有表單的普通舊頁面,而不是它們在哪裏。然後,您可以通過更改要通過提交的表單來解決此問題(現在或稍後)。而不是Ajax,並通過渲染錯誤。 JS。但這將是一個相當數量的工作。

+0

謝謝,很高興知道我在正確的軌道上。雖然驗證錯誤和消息很重要,但我真的很想重定向到原始頁面,而Ajax看起來像是過度殺毒。我想我已經找到了解決方案... – baseten 2012-01-04 16:09:51

0

OK,我想出了一個解決方案,我感到更快樂,解決了一些我的問題面對。希望這可以幫助那些面臨類似問題的人。現在唯一的缺點是我在View Helper中引用了Model。我知道這並不是鬆散的耦合,但我已經看到過這樣做了幾次,甚至建議在ZF docs中避免使用'action'視圖助手(它將創建一個新的MVC分派循環)。總體而言,我認爲DRYness和封裝是值得的,也可能有其他一些適合的術語。

爲了能夠從我的MailingListController使用重定向,但保留來自我的模型的消息和任何表單驗證錯誤,我需要將它們存儲在會話中。對於消息,我通常會使用FlashMessenger動作助手,但是在View Helper中獲取這個消息並不是最佳做法,它不會處理我的表單錯誤,它實際上所做的是將東西保存到會話中,因此無需這麼做。我可以在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(); 

     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); 
    } 
} 

我添加了一個方法,我Model_MailingList類; setFormErrors($ errors),如果驗證失敗,我將從表單傳遞錯誤消息。這將錯誤數組保存到會話中。

我通常使用具有addMessage和getMessages方法的基本模型類。這些只是訪問受保護的消息數組。在我的Model_MailingList中,我重寫了這些方法來將消息存儲在會話中。在addEmailAddress($ emailAddress)方法中,我已經調用addMessage來說明將電子郵件地址插入數據庫是否成功。

型號:

<?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.") 
    } 
} 

我現在不需要任何PARAMS傳遞給視圖助手,因爲它可以查詢它的直接從模型的狀態。 $ this-> view-> messenger只是將數組轉換爲無序列表的另一個視圖助手。

視圖助手:

<?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; 
    } 

} 

然後在Form_MailingList類我只需要添加一個額外的方法來重新填充錯誤消息。儘管getMessages()是Zend_Form的一種方法,但並沒有出現任何相應的setMessages()。您可以在Zend_Form_Element的做到這一點不過,所以我增加了以下功能到Form_MailingList類:

形式:

<?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();?> 

我明白了很多,我所面臨的問題是下降到一個非常具體的情況,但希望這能幫助一些人出來以某種方式!

乾杯,亞歷克斯