0 follower

モデルの作成

フォームに必要なHTMLコードを書く前に、エンドユーザから、どの様なタイプの データが送られてくる事を期待するか、どの様なルールが適用されるべきかを 決定します。モデルクラスはこれらの情報を記録するために 使用することができます。モデルはサブセクション Model で定義されているように、 ユーザーの入力を保持し、入力チェックを行う中心的な場所です。

ユーザーの入力をどのように扱うかによって、2つのタイプのモデルを 作成することができます。もしユーザーの入力が収集され、 使用された後に破棄される場合は、 form model を作成しましょう;もしユーザーの入力が収集され、データベースへ保存される場合 は代わりに active record を使いましょう。 両方のモデルは共通のインターフェースとして定義された CModel という基底クラスを必要とします。

注意: このセクションでは例として form model を主に扱いますが、 active record においても同様なことが可能です。

1. モデルクラスの定義

以下では、ログインページでユーザーの入力を収集する為に使用される LoginForm モデルクラスを作成します。ログインで扱う情報は 認証時にのみ必要で、保存される必要はありませんので、LoginForm のモデルは form model として作成します。

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}

3つの属性が LoginForm の中で宣言されています: $username$password、 そして $rememberMeです。これらはユーザーの入力したユーザー名とパスワード、 そして、ユーザーがログイン情報を保存するかどうかのオプションを保持する為に 用いられます。$rememberMe は、デフォルトで false になっているので、 ログインフォームに表示される結びついたオプションは、初期状態では チェックされてない状態となります。

情報: ここでは、これらのメンバ変数を普通のプロパティと区別する為に、 プロパティと呼ばずに 属性 (attribute) と呼びます。属性は主に、 ユーザの入力やデータベースのデータを保持する為に用いられるプロパティです。

2. バリデーションルールの宣言

ユーザがインプットを送信し、モデルが値を受け取った際、私たちは その値を使用する前に正しい値であるか検証する必要があります。 これはインプットに対して、セットになったルールのバリデーション(検証) を実行する事によって成し遂げられます。バリデーションルールは rules() メソッドの中で定義し、このメソッドはルールの構成を配列として 返さなければいけません。

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('password', 'authenticate'),
        );
    }
 
    public function authenticate($attribute,$params)
    {
        if(!$this->hasErrors())  // インプットエラーが無い場合にのみ認証を行います
        {
            $identity=new UserIdentity($this->username,$this->password);
            if($identity->authenticate())
            {
                $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days
                Yii::app()->user->login($identity,$duration);
            }
            else
                $this->addError('password','Incorrect password.');
        }
    }
}

上記のコードは usernamepassword の両方が必須であり、password は認証に掛けられなければならない事を示しています。

rules() によって返される、それぞれのルールは下記の様なフォーマット でなければなりません:

array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...additional options)

AttributeList の箇所は、ルールによって検証されなければならない属性 (attribute)の名前がカンマ区切りの文字列として入ります; Validator はどのようなバリデーションが適用されなければならないかを示しています; on パラメータはオプションで、ルールが適用されるシナリオのリスト を示しています; そして追加のオプション(additional option)の箇所は 名前-値 のペアで、結びついたバリデータのプロパティの値を初期化する為に 使用されます。

バリデーションルールの中で Validator を指定するには3つの方法があります。 ひとつ目に、上記の authenticate の例の様に、Validator はモデルクラス の中のメソッド名を取る事ができます。バリデータメソッドは下記の様な書式 でなければなりません:

/**
 * @param string the name of the attribute to be validated
 * @param array options specified in the validation rule
 */
public function ValidatorName($attribute,$params) { ... }

二つ目に、Validator はバリデータクラスの名前にする事ができます。 ルールが適用されたとき、バリデータクラスのインスタンスが作成され、 実際の検証が行われます。追加のオプションの値は、インスタンスの属性の値を 初期化する為に用いられます。バリデータクラスは CValidator を継承しなければいけません。

注意: アクティブレコードモデルでルールを定義する場合、on という名前の特別なオプションを使用する事ができます。このオプションは 'insert''update' のどちらかにする事が可能で、レコードのインサート の時のみにルールが適用されるか、アップデートの時のみにルールが 適用されるかをそれぞれ指定することが出来ます。もしセットされていない場合、 ルールは save() がコールされた時に両方のケースで適用されます。

三つ目に、Validator は予め定義されたバリデータクラスの エイリアスを用いる事が出来ます。上記の例にあるように、requiredCRequiredValidator のエイリアスで、これは属性の値が、空の値では無い と検証された事を保証します。下記は予め定義されたバリデータの エイリアスの完全なリストです:

  • captcha: CCaptchaValidator のエイリアスで、属性の値が  CAPTCHAで表示された ベリフィケーションコードと等しい事を確認します。

  • compare: CCompareValidator のエイリアスで、 属性の値が、 もう一つの属性、または定数と等しい事を確認します。

  • email: CEmailValidator のエイリアスで、属性の値が正式な Eメールアドレスである事を確認します。

  • default: CDefaultValueValidatorのエイリアスで、指定した属性にデフォルト地を代入します。

  • exist: CExistValidatorのエイリアスで、属性値が指定されたテーブルの列に存在することを保証します。

  • file: CFileValidator のエイリアスで、属性の値にアップロード されたファイルの名前が入っている事を確認します。

  • filter: CFilterValidator のエイリアスで、 属性の値を フィルタによって変形します。

  • in: CRangeValidator のエイリアスで、属性の値が、 予め定めた値のリストの中にある事を確認します。

  • length: CStringValidator のエイリアスで、 データの長さが特定の範囲内にある事を確認します。

  • match: CRegularExpressionValidator のエイリアスで、 データが正規表現にマッチする事を確認します。

  • numerical: CNumberValidator のエイリアスで、 データが数値として妥当であるかを確認します。

  • required: CRequiredValidator のエイリアスで、 属性が空でない事を確認します。

  • type: CTypeValidator のエイリアスでデータが 特定の型である事を確認します。

  • unique: CUniqueValidator のエイリアスで、データが データベースのテーブルのカラムで一意な値である事を確認します。

  • url: CUrlValidator のエイリアスで、データが妥当なURLである事 を確認します。

下記は予め定義されたバリデータを使用した例のリストです:

// username は必須です。
array('username', 'required'),
// username の長さが3から12の間である事を確認します。
array('username', 'length', 'min'=>3, 'max'=>12),
// 登録シナリオでは、パスワードとパスワード2は一致する必要があります。
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
// ログインシナリオでは、パスワードは認証されなければなりません。
array('password', 'authenticate', 'on'=>'login'),

3. 属性への代入をセキュアにする

注意:シナリオベースでの属性への代入は1.0.2から利用可能です。

モデルインスタンスが作成した後、多くの場合は属性値にユーザからの入力値を代入する必要があります。 これは以下のように一括代入を使うと容易に行えます。

$model=new LoginForm;
$model->scenario='login';
if(isset($_POST['LoginForm']))
    $model->attributes=$_POST['LoginForm'];

注意: scenario プロパティは、バージョン 1.0.4 以降で利用可能です。一括代入では、どの属性を一括代入できるかを決定するために、 このプロパティ値が利用されます。 バージョン 1.0.2 と 1.0.3 では、特定のシナリオに一括代入を行うために、 下記の方法を使用する必要があります。

$model->setAttributes($_POST['LoginForm'], 'login');

最後の文は、login シナリオの対応モデル属性に $_POST['LoginForm'] の すべてのエントリを一括代入します。 これは、下記の代入と同等です。

foreach($_POST['LoginForm'] as $name=>$value)
{
    if($name is a safe attribute)
        $model->$name=$value;
}

各属性が一括代入しても安全かどうかの判断は、 safeAttributesメソッドの戻り値と、指定されたシナリオによって決定されます。 デフォルトでは、CFormModelのすべてのパブリックメンバプロパティが安全と返されます。 一方で CActiveRecordでは、プライマリキーを除いたテーブルのカラムすべてが安全と返されます。 このメソッドをオーバーライドして、安全な属性をシナリオによって制限することができます。 例えば、ユーザモデルはたくさんの属性を持ちますが、loginシナリオではusernamepassword しか使わないので、以下のようにして制限を加えます。

public function safeAttributes()
{
    return array(
        parent::safeAttributes(),
        'login' => 'username, password',
    );
}

より正確には、safeAttributesメソッドの戻り値は以下のような構造です。

array(
   // これらの属性は下で明示的に指定されたしなりの以外のすべてのケースで一括代入の対象になる
   'attr1, attr2, ...',
     *
   // これらの属性はシナリオ1でのみ一括代入される
   'scenario1' => 'attr2, attr3, ...',
     *
   // これらの属性はシナリオ2でのみ一括代入される
   'scenario2' => 'attr1, attr3, ...',
)

モデルでシナリオを必要としない場合(例:常にひとつのシナリオで使われる、 すべてのシナリオで同じ安全な属性のセットを使う)、戻り値は単純な文字列でもかまいません。

'attr1, attr2, ...'

安全でない属性にデータを代入するには、以下のように個々に代入式を書く必要があります。

$model->permission='admin';
$model->id=1;

4. バリデーションの始動

モデルにユーザからの入力値をセットしたら、 バリデーションを始動する為に、CModel::validate() をコールする事 が出来ます。このメソッドは、一つのエラーも起こらずに、バリデーションが 成功したかどうかを示す値を返します。アクティブレコードモデルでは、 モデルをデータベースに保存する為に、 save() をコールした時点で、バリデーションは自動的に動作します。

CModel::validate()を呼ぶ際に、シナリオパラメータを指定することができます。 指定されたシナリオに当てはまるバリデーションルールのみが実行されます。 シナリオに当てはまるバリデーションデータは、ルールの on オプションが設定されていないか、 指定されたシナリオ名を含むものです。 CModel::validate()を呼ぶ際にシナリオを指定しないと、on オプションが設定されていないルールのみが実行されます。

例えば、以下の評価式を実行して、ユーザ登録時のバリデーションをおこうなうことができます。

$model->scenario='register';
$model->validate();

Note: scenarioプロパティはバージョン1.0.4から利用可能です。 validationメソッドはどのルールがチェックされるかどうかをこのプロパティによって決定します。 バージョン1.0.2と1.0.3では、シナリオベースのバリデーションを実行する際に以下のようにする必要があります。

$model->validate('register');

バリデーションルールはフォームモデルクラスで以下のように宣言できます。

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=>'register'),
        array('password', 'compare', 'on'=>'register'),
    );
}

結果として、最初のルールはすべてのシナリオに適用され、 そのほかの二つのシナリオはregisterシナリオの場合のみ適用されます。

注意: シナリオベースのバリデーションはバージョン1.0.1から利用可能です。

5. バリデーションエラーの回収

バリデーションエラーがあるかどうかをチェックする為に CModel::hasErrors() を使用する事ができ、もしエラーがある場合、エラーメッセージ獲得の為に CModel::getErrors() を使用する事ができます。メソッドは両方とも、 個別の属性か全ての属性に対して使用する事が出来ます。

6. 属性のラベル

フォームをデザインする時、それぞれのインプットフィールドにラベルの表示 が必要となる事が度々あります。ラベルはユーザに、どの様な種類の情報を フィールドに入力するかを伝えます。ビューの中にラベルを直接書き込む事も可能 ですが、結びついたモデルの中で定めた方がより柔軟で便利なことが多いでしょう。

デフォルトでは、CModel は単純に属性の名前をラベルとして返します。 これは attributeLabels() メソッドを オーバーライドする事でカスタマイズ可能です。次のサブセクション に見るように、モデルの中でのラベルの定義は、より早く、 力強いフォームの作成を私達にもたらします。