2010-08-23 81 views
23

我想用Yii構建一個多頁面表單,但對於PHP和Yii來說,這是相當新穎的,我想知道寫多頁表單的最佳做法是什麼。到目前爲止,我打算做的是添加一個名爲「step」的隱藏字段,其中包含用戶在表單中的當前步驟(表單分成3個步驟/頁面)。因此,考慮到這一點,這是我計劃如何處理上一個/下一個按鈕,用戶點擊該控制器:Yii多頁面表單嚮導最佳實踐

public function actionCreate() 
{ 
    $userModel = new User; 

    $data['activityModel'] = $activityModel; 
    $data['userModel'] = $userModel; 

    if (!empty($_POST['step'])) 
    { 
    switch $_POST['step']: 
    case '1': 
    $this->render('create_step1', $data); 
    break; 

    case '2': 
    $this->render('create_step2', $data); 
    break; 

    }else 
    { 
    $this->render('create_step1', $data); 
    } 
} 

請問這種做法是否合理?或者我離開基地,在Yii/PHP中有更好更優化的方法嗎?

謝謝!

+4

看看'了CHtml :: statefulForm'和[線程的Yii論壇](http://www.yiiframework.com/forum/index.php?/topic/ 8413-wizard-forms /) – 2010-08-23 20:31:36

回答

35

有幾種方法可以解決這個問題。我看到你在Yii的論壇貼出所以我想你已經四處搜尋有太多,但如果你沒有:

我所做的是(只對於一個簡單的2步ActiveRecord表單)採取一個單一的行動,並根據按鈕名稱,Yii張貼在表單提交(注:不適用於Ajax提交)的POST名稱分爲條件塊。然後,根據哪個按鈕被擊中,我會渲染正確的表單,並在我的模型上設置正確的方案以進行驗證。

像你一樣隱藏的「step」字段可以起到與檢查submitButton名稱相同的作用。我可能會將「步驟」保存到表單狀態中,而不是添加隱藏字段,但兩者都可以。

有些人使用有狀態的activeForm屬性來保存嚮導中單個步驟的數據,或者您可以使用會話,甚至保存到臨時數據庫表中。在我完全未經測試的例子中,我使用了有狀態表單功能。

這裏是我基本上爲ActiveRecord表單做的一個例子。這正好在 「actionCreate」:

<?php if (isset($_POST['cancel'])) { 
    $this->redirect(array('home')); 
} elseif (isset($_POST['step2'])) { 
    $this->setPageState('step1',$_POST['Model']); // save step1 into form state 
    $model=new Model('step1'); 
    $model->attributes = $_POST['Model']; 
    if($model->validate()) 
    $this->render('form2',array('model'=>$model)); 
    else { 
    $this->render('form1',array('model'=>$model)); 
    } 
} elseif (isset($_POST['finish'])) { 
    $model=new Model('finish'); 
    $model->attributes = $this->getPageState('step1',array()); //get the info from step 1 
    $model->attributes = $_POST['Model']; // then the info from step2 
    if ($model->save()) 
    $this->redirect(array('home')); 
    else { 
    $this->render('form2',array('model'=>$model)); 
} else { // this is the default, first time (step1) 
    $model=new Model('new'); 
    $this->render('form1',array('model'=>$model)); 
} ?> 

的形式會是這個樣子:

Form1中:

<?php $form=$this->beginWidget('CActiveForm', array(
    'enableAjaxValidation'=>false, 
    'id'=>'model-form', 
    'stateful'=>true, 
)); 
<!-- form1 fields go here --> 
echo CHtml::submitButton("Cancel",array('name'=>'cancel'); 
echo CHtml::submitButton("On to Step 2 >",array('name'=>'step2'); 
$this->endWidget(); ?> 

表2:

<?php $form=$this->beginWidget('CActiveForm', array(
    'enableAjaxValidation'=>false, 
    'id'=>'model-form', 
    'stateful'=>true, 
)); 
<!-- form2 fields go here --> 
echo CHtml::submitButton("Back to Step 1",array('name'=>'step1'); 
echo CHtml::submitButton("Finish",array('name'=>'finish'); 
$this->endWidget(); ?> 

我希望這是有幫助!

+1

非常感謝您的幫助!我非常喜歡使用提交按鈕來確定要顯示的步驟。我也將研究ActiveForm方法,這聽起來很有趣。 – 2010-08-25 09:38:25

+0

謝謝分配。我在哪裏可以學到更多類似的東西?像用yii框架開發專業人員一樣。關於 – FDisk 2010-12-20 22:22:47

+1

「注:不使用Ajax的提交工作」,這是事實,尤其是在jQuery的,但你可以按鈕的名稱/值只是追加到form.serialize的結果,將工作 – 2011-03-26 20:16:40

4

Yii提供了一種叫做頁面狀態的功能來實現諸如多步/多頁面表單嚮導之類的功能。

讓我們來看看Yii的文檔第一:

一個頁面狀態是一個變量,它是通過同一頁面的POST請求持久。爲了使用持久頁面狀態,表單必須是有狀態的,這些狀態是使用{@link CHtml :: statefulForm}生成的。

所以每個步驟/頁面的形式都需要是有狀態的表單。要呈現有狀態表單,只需在啓動ActiveForm-Widget時將CActiveForm::stateful屬性設置爲true即可。 在您的控制器中,您可以使用CController::getPageState()CController::setPageState()來設置頁面狀態。

因此,這些都是工作的基礎非常好,如果你的多頁面表單嚮導的實施在傳統的風格,而不AJAX請求作出。

然而,如果你想使用AJAX調用提交的步驟數據並顯示下一步,Yii中的頁面的狀態是不可用的。

爲什麼?所有頁面狀態都通過HTTP-POST在隱藏的輸入字段中傳輸。輸入字段由Yii填充,而所謂的輸出處理。輸出處理在渲染後開始,並將替換部分輸出。所以Yii的頁面狀態功能需要輸出處理。另一方面,AJAX響應可能會被破壞,因爲輸出處理可能會在輸出開始時添加<link><script>標籤以加載所需的JS和CSS文件。

最終我實現了我自己的狀態形式的版本。每次需要時,我都可以使用靜態函數調用ActiveFormWidget::getRequestMultiStepData()來獲取有狀態數據。

注意:有我在執行一個缺點:所有的狀態數據需要收集的表單控件將被初始化之前。但直到現在,我從來沒有遇到任何問題。不過這裏是代碼:

class ActiveFormWidget extends CActiveForm 
{ 
    public static $inputNameMultiStepData = '_multiStepData'; 

    public $multiStep = false; 
    public $multiStepData = array(); 

    public function init() 
    { 
     parent::init(); 

     # Hidden-Fields 
     if ($this->multiStep) { 
      echo Html::hiddenField(static::$inputNameMultiStepData, static::encodeInputData($this->multiStepData)); 
     } 
    } 

    /** 
    * Gets all multi step data sent. 
    * @return array|mixed 
    */ 
    public static function getRequestMultiStepData() 
    { 
     return isset($_REQUEST[static::$inputNameMultiStepData]) ? static::decodeInputData($_REQUEST[static::$inputNameMultiStepData]) : array(); 
    } 


    /** 
    * Encodes form data like Yii does for stateful forms. 
    * @param $data 
    * @return string 
    */ 
    public static function encodeInputData($data) 
    { 
     $data = Yii::app()->getSecurityManager()->hashData(serialize($data)); 

     return base64_encode($data); 
    } 

    /** 
    * Decodes form data like Yii does for stateful forms. 
    * @param $data 
    * @return bool|mixed 
    */ 
    public static function decodeInputData($data) 
    { 
     $data = base64_decode($data); 
     $data = Yii::app()->getSecurityManager()->validateData($data); 
     if ($data !== false) { 
      return unserialize($data); 
     } else { 
      return false; 
     } 
    } 
}