2010-08-23 4 views
23

Ich versuche, ein mehrseitiges Formular mit Yii zu erstellen, bin aber ziemlich neu in PHP und Yii und frage mich, was die beste Praxis für das Schreiben eines mehrseitigen Formulars ist. Bisher plane ich, ein verstecktes Feld namens "Schritt" hinzuzufügen, das den aktuellen Schritt enthält, auf dem sich der Benutzer im Formular befindet (das Formular ist in 3 Schritte/Seiten unterteilt). Also in diesem Sinne, das ist, wie ich plane, auf vorherige/nächsten Tasten in den Controller den Benutzer durch Klick zu handhaben:Yii Multi-Seiten-Formular-Assistent Best Practice

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

Ist dieser Ansatz sinnvoll? Oder bin ich weit weg von der Basis und es gibt einen viel besseren und optimierten Weg, dies in Yii/PHP zu tun?

Danke!

+4

Werfen Sie einen Blick auf 'CHtml :: statefulForm' und ein [Thema bei Yü Forum] (http://www.yiiframework.com/forum/index.php?/topic/ 8413-wizard-forms /) –

Antwort

35

Es gibt ein paar Möglichkeiten, dies zu erreichen. Ich sehe dich im Yü Forum gepostet, damit ich nehme an, Sie um auch dort gesucht haben, aber falls Sie nicht:

Was ich getan habe, ist (nur für ein einfaches 2-stufiges ActiveRecord-Formular) eine einzelne Aktion ausgeführt und diese in Bedingungsblöcke aufgeteilt, basierend auf dem Button-Namen, den Yii POSTs auf einem Formular abschicken (Anmerkung: funktioniert nicht mit Ajax-Submits). Dann, je nachdem, welche Taste gedrückt wurde, rendere ich das korrekte Formular und setze das richtige Szenario für Validierungszwecke auf mein Modell.

Ein verstecktes "Schritt" -Feld, wie Sie es haben könnten, könnte dem gleichen Zweck dienen wie der submitButton-Name. Ich würde vielleicht den "Schritt" in den Formularzustand speichern, anstatt ein verstecktes Feld hinzuzufügen, aber beides wäre in Ordnung.

Einige Personen verwenden das stateful activeForm-Attribut, um die Daten eines einzelnen Schritts im Assistenten zu speichern, oder Sie können die Sitzung verwenden oder sogar in einer temporären Datenbanktabelle speichern. In meinem unten völlig ungeprüften Beispiel verwende ich eine Stateful-Form-Funktionalität.

Hier ist ein Beispiel, was ich im Grunde für ein ActiveRecord-Formular getan habe. Dies geht in der "actionCreate":

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

Form 2:

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

Die Formen wie folgt aussehen würde

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

hoffe ich, dass ist hilfreich!

+1

Danke das war sehr hilfreich! Ich mag Ihren Ansatz, die Sendeschaltflächen zu verwenden, um zu bestimmen, welcher Schritt angezeigt werden soll. Ich werde auch den ActiveForm-Ansatz untersuchen, der interessant klingt. –

+0

Vielen Dank allot. Wo kann ich mehr solche Sachen lernen? Wie professionelle Entwicklung mit YII-Framework. – FDisk

+1

in Bezug auf "Hinweis: funktioniert nicht mit AJAX submits", das ist wahr, vor allem in jQuery, aber Sie können nur die Schaltfläche Name/Wert an das Ergebnis von form.serialize anhängen und funktioniert –

4

Yii bietet eine Funktion namens page states, um Dinge wie einen Multi Step/Multi Page Form Wizard zu implementieren.

Lässt sich die Yü docs einen Blick zuerst:

Ein Seitenzustand ist eine Variable, die über POST-Anfragen von der gleichen Seite persistent ist. Um persistente Seitenzustände zu verwenden, müssen die Formulare statusbehaftet sein, die mit {@link CHtml :: statusfulForm} generiert werden.

Also die Formen jedes Schrittes/jeder Seite müssen statusbehaftete Formen sein.Um ein statusbehaftetes Formular zu rendern, müssen Sie nur die CActiveForm::stateful-Eigenschaft auf true festlegen, wenn Sie das ActiveForm-Widget starten. Innerhalb Ihres Controllers können Sie Ihre Seitenzustände mit CController::getPageState() oder CController::setPageState() abrufen und einstellen.

Das sind die Grundlagen, die ziemlich gut funktionieren, wenn die Implementierung Ihres mehrseitigen Formularassistenten im traditionellen Stil ohne AJAX-Anforderungen erfolgt.

Wenn Sie jedoch AJAX-Aufrufe verwenden möchten, um Schrittdaten zu übermitteln und den nächsten Schritt anzuzeigen, sind die Seitenzustände von Yii nicht verwendbar.

Warum? Alle Seitenzustände werden innerhalb eines versteckten Eingabefeldes durch HTTP-POST transportiert. Das Eingabefeld wird von Yii während der so genannten Ausgabeverarbeitung gefüllt. Die Ausgabeverarbeitung startet nach das Rendern und wird Teile der Ausgabe ersetzen. So Yi's Seitenstatus-Funktion erfordert die Ausgabeverarbeitung. AJAX-Antworten auf der anderen Seite können dadurch beschädigt werden, weil die Ausgabeverarbeitung auch <link> oder <script> Tags am Anfang der Ausgabe hinzufügen kann, um erforderliche JS- und CSS-Dateien zu laden.

Am Ende habe ich meine eigene Version von Stateful Forms implementiert. Ich bin in der Lage, meine statusbehafteten Daten mit dem statischen Funktionsaufruf ActiveFormWidget::getRequestMultiStepData() jedes Mal zu bekommen, wenn ich es brauche.

Hinweis: Es gibt einen Nachteil in meiner Implementierung: Alle statusbehafteten Daten müssen gesammelt werden, bevor das Formular-Widget initialisiert wird. Aber ich hatte bis jetzt nie ein Problem damit. Aber hier ist der Code:

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