使用表單


使用表單

本章節將介紹如何建立一個從使用者那蒐集資料的表單頁。該頁將顯示一個包含 name 輸入框和 email 輸入框的表單。當蒐集完這兩部分資訊後,頁面將會顯示使用者輸入的資訊。

為了實現這個目標,除了建立一個[操作]和兩個[檢視]外,還需要建立一個[模型]。

貫穿整個小節,你將會學到:

* 建立一個[模型](structure-models.md)代表使用者通過表單輸入的資料
* 宣告規則去驗證輸入的資料
* 在[檢視](structure-views.md)中生成一個 HTML 表單

建立模型 ?

模型類 `EntryForm` 代表從使用者那請求的資料,該類如下所示並儲存在 `models/EntryForm.php` 檔案中。請參考[類自動載入]章節獲取更多關於類命名約定的介紹。
<?php
namespace app\models;
use yii\base\Model;
class EntryForm extends Model
{
    public $name;
    public $email;

    public function rules()
    {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}
該類繼承自 [[yii\base\Model]],Yii 提供的一個基礎類別,通常用來表示資料。

> 補充:[[yii\base\Model]] 被用於普通模型類的父類別並與資料表**無關**。[[yii\db\ActiveRecord]] 通常是普通模型類的父類別但與資料表有關聯(譯者注:[[yii\db\ActiveRecord]] 類其實也是繼承自 [[yii\base\Model]],增加了資料庫處理)。

`EntryForm` 類包含 `name` 和 `email` 兩個公共成員,用來儲存使用者輸入的資料。它還包含一個名為 `rules()` 的方法,用來返回資料驗證規則的集合。上面宣告的驗證規則表示:

* `name` 和 `email` 值都是必須的
* `mail` 的值必須滿足 email 地址驗證

如果你有一個從使用者那蒐集資料的 `EntryForm` 物件,你可以呼叫它的 [[yii\base\Model::validate()|validate()]] 方法觸發資料驗證。如果有資料驗證失敗,將把 [[yii\base\Model::hasErrors|hasErrors]] 屬性設為 ture,想要知道具體發生什麼錯誤就呼叫 [[yii\base\Model::getErrors|getErrors]]。
<?php
$model = new EntryForm();
$model->name = 'Qiang';
$model->email = 'bad';
if ($model->validate()) {
    // 驗證成功!
} else {
    // 失敗!
    // 使用 $model->getErrors() 獲取錯誤詳情
}

建立操作 ?

下面你得在 `site` 控制器中建立一個 `entry` 操作用於新建的模型。操作的建立和使用已經在[說一聲你好](start-hello.md)小節中解釋了。
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\EntryForm;
class SiteController extends Controller
{
    // ...其它程式碼...

    public function actionEntry()
    {
        $model = new EntryForm;

        if ($model->load(Yii::$app->request->post()) && $model->validate()) {
            // 驗證 $model 收到的資料

            // 做些有意義的事 ...

            return $this->render('entry-confirm', ['model' => $model]);
        } else {
            // 無論是初始化顯示還是資料驗證錯誤
            return $this->render('entry', ['model' => $model]);
        }
    }
}
該操作首先建立了一個 `EntryForm` 物件。然後嘗試從 `$_POST` 蒐集使用者提交的資料,由 Yii 的 [[yii\web\Request::post()]] 方法負責蒐集。如果模型被成功填充資料(也就是說使用者已經提交了 HTML 表單),操作將呼叫 [[yii\base\Model::validate()|validate()]] 去確保使用者提交的是有效資料。

> 補充:表示式 `Yii::$app` 代表[應用]範例,它是一個全域性可存取的單例。同時它也是一個[服務定位器],能提供 `request`,`response`,`db` 等等特定功能的元件。在上面的程式碼裡就是使用 `request` 元件來存取應用範例收到的 `$_POST` 資料。

使用者提交表單後,操作將會渲染一個名為 `entry-confirm` 的檢視去確認使用者輸入的資料。如果沒填表單就提交,或資料包含錯誤(譯者:如 email 格式不對),`entry` 檢視將會渲染輸出,連同表單一起輸出的還有驗證錯誤的詳細資訊。

> 注意:在這個簡單例子裡我們只是呈現了有效資料的確認頁面。實踐中你應該考慮使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] 去避免[表單重複提交問題](http://en.wikipedia.org/wiki/Post/Redirect/Get)。

建立檢視 ?

最後建立兩個檢視檔案 `entry-confirm` 和 `entry`。他們會被剛才建立的 `entry` 操作渲染。

`entry-confirm` 檢視簡單地顯示提交的 name 和 email 資料。檢視檔案儲存在 `views/site/entry-confirm.php`。
<?php
use yii\helpers\Html;
?>
<p>You have entered the following information:</p>

<ul>
    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>
    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>
</ul>
`entry` 檢視顯示一個 HTML 表單。檢視檔案儲存在 `views/site/entry.php`。
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'name') ?>
    <?= $form->field($model, 'email') ?>
    <div class="form-group">
        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
    </div>
<?php ActiveForm::end(); ?>
檢視使用了一個功能強大的[小部件](structure-widgets.md) [[yii\widgets\ActiveForm|ActiveForm]] 去生成 HTML 表單。其中的 `begin()` 和 `end()` 分別用來渲染表單的開始和關閉標籤。在這兩個方法之間使用了 [[yii\widgets\ActiveForm::field()|field()]] 方法去建立輸入框。第一個輸入框用於 “name”,第二個輸入框用於 “email”。之後使用 [[yii\helpers\Html::submitButton()]] 方法生成提交按鈕。

嘗試下 ?

用瀏覽器存取下面的 URL 看它能否工作:
http://hostname/index.php?r=site/entry
你會看到一個包含兩個輸入框的表單的頁面。每個輸入框的前面都有一個標籤指明應該輸入的資料型別。如果什麼都不填就點選提交按鈕,或填入格式不正確的 email 地址,將會看到在對應的輸入框下顯示錯誤資訊。
[驗證錯誤的表單]
輸入有效的 name 和 email 資訊並提交後,將會看到一個顯示你所提交資料的確認頁面。
[輸入資料的確認頁]

### 效果說明 ?

你可能會好奇 HTML 表單暗地裡是如何工作的呢,看起來它可以為每個輸入框顯示文字標籤,而當你沒輸入正確的資訊時又不需要重新整理頁面就能給出錯誤提示,似乎有些神奇。

是的,其實資料首先由用戶端 JavaScript 指令碼驗證,然後才會提交給伺服器通過 PHP 驗證。[[yii\widgets\ActiveForm]] 足夠智慧到把你在 `EntryForm` 模型中宣告的驗證規則轉化成用戶端 JavaScript 指令碼去執行驗證。如果使用者瀏覽器禁用了 JavaScript, 伺服器端仍然會像 `actionEntry()` 方法裡這樣驗證一遍資料。這保證了任何情況下使用者提交的資料都是有效的。

> 警告:用戶端驗證是提高使用者體驗的手段。無論它是否正常啟用,伺服器端驗證則都是必須的,請不要忽略它。

輸入框的文字標籤是 `field()` 方法生成的,內容就是模型中該資料的屬性名。例如模型中的 `name` 屬性生成的標籤就是 `Name`。

你可以在檢視中自定義標籤:
<?= $form->field($model, 'name')->label('自定義 Name') ?>
<?= $form->field($model, 'email')->label('自定義 Email') ?>
> 補充:Yii 提供了相當多類似的小部件去幫你生成複雜且動態的檢視。在後面你還會了解到自己寫小部件是多麼簡單。你可能會把自己的很多檢視程式碼轉化成小部件以提高重用,加快開發效率。

總結 

本章節指南中你接觸了 MVC 設計模式的每個部分。學到了如何建立一個模型代表使用者資料並驗證它的有效性。

你還學到了如何從使用者那獲取資料並在瀏覽器上回顯給使用者。這本來是開發應用的過程中比較耗時的任務,好在 Yii 提供了強大的小部件讓它變得如此簡單。

下一章你將學習如何使用資料庫,幾乎每個應用都需要資料庫。