フォーム | Form & Validation

Formオブジェクトを使用すると、フォームやバリデーションを楽に扱えるようになります。
例として、下記の入力項目・制限のあるフォームを作成してみます。

入力キー 表示名 制限
name名前必須/全角12文字以内
emailメールアドレス必須/255バイト以内/メールアドレス形式
age年齢自然数/18~60
sex性別必須/1,2のいずれか

まず、app/forms に Forms_Userクラス を作成します。Form_Object は全てのフォームクラスの基底クラスです。

app/forms/User.php

class Forms_User extends Form_Object
{
  
}
次にすることは、入力名に対応する表示名を設定することです。
class Forms_User extends Form_Object
{
  protected $displayNames = array(
     "name" => "名前",
     "email" => "メールアドレス",
     "age" => "年齢",
     "sex" => "性別",
  );
}
次は、バリデータに標準で実装されているバリデーションメソッドを各入力に充てていきます。バリデーションメソッドなどの詳細はバリデーションの章を参照してください。
class Forms_User extends Form_Object
{
  ...
  
  protected $validators = array(
    "name" => array("required", "strwidth(24)"),
    "email" => array("required", "strlen(255)"),
    "age" => array("nnumber", "min(18)", "max(60)"),
    "sex" => array("required"),
  );
}
とりあえずこの状態で、フォームを動作させてみましょう。フォーム表示アクションでは、先ほど作成したフォームオブジェクトをビューに渡します。
app/index/controllers/Index.php

class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $this->userForm = new Forms_User();
  }
}
ビュー(テンプレート)でフォームを作成します。フォームオブジェクトの n()メソッド は getDisplayName() のエイリアスです。
※INPUT要素などを書き出すtext()メソッドなどの詳細はページ下部で説明しています。
<?php if ($userForm->hasError()) : ?>
  <?php echo $this->partial("error", array("errors" => $userForm->getErrors())) ?>
<?php endif ?>

<form action="<?php echo uri('a: post') ?>" method="post">
  <dl>
    <dt><?php echo $userForm->n("name") ?></dt>
    <dd><?php echo $userForm->text("name") ?></dd>
    <dt><?php echo $userForm->n("email") ?></dt>
    <dd><?php echo $userForm->text("email") ?></dd>
    <dt><?php echo $userForm->n("age") ?></dt>
    <dd><?php echo $userForm->text("age") ?></dd>
    <dt><?php echo $userForm->n("sex") ?></dt>
    <dd><?php echo $userForm->select("sex", array("" => "", 1 => "女性", 2 => "男性")) ?></dd>
    <dd>
      <input type="submit" value="Post" />
    </dd>
  </dl>
</form>
フォームが作成できたので、次はPOST先のアクションを実装します。Formオブジェクト の submit()メソッド の第1引数は入力値の配列なので、リクエストオブジェクトからPOSTされた値を配列で受け取り、それをそのまま渡します。第2引数は入力として有効な入力名を渡しています。次章で扱うModelフォームの場合に特に重要で、この仕組みが無いと攻撃者がHTMLを書き換えた場合などに意図しないデータが入力されてしまうためです。
class Index_Controllers_Index extends Sabel_Controller_Page
{
  ...
  
  public function post()
  {
    $form = new Forms_User();
    $form->submit($this->request->fetchPostValues(), array(
      "name", "email", "age", "sex"
    ));
    
    if ($form->validate()) {
      
    } else {
      $this->userForm = $form;
      $this->view->setName("index");
    }
  }
}
これでとりあえず、フォームが動作するようになりました。
フォームに何も入力しないままPOSTすると、下記のようなエラーメッセージが出力されます。
* 名前は必須です
* メールアドレスは必須です
* 性別は必須です
また、名前に「あいうえおあいうえおあいうえお」を入力すると...
* 名前は全角12文字以内で入力してください
年齢に「test」を入力すると...
* 年齢は整数で入力してください
年齢に「3306」を入力すると...
* 年齢は60以下で入力してください
など、バリデーションが正しく行われていることが分かります。

次に、標準で用意されていないバリデーションの実装方法を説明します。冒頭で、メールアドレスは「メールアドレス形式」であること、また、性別は1,2のいずれかであることが決められていました。これらは、カスタムバリデーションを実装&登録することで実現できます。

まず、Formオブジェクト にチェック用のメソッドを実装します。
class Forms_User extends Form_Object
{
  ...
  
  public function validateEmail($name, $value)
  {
    if (!is_empty($value)) {
      if (形式が不正なら) {
        return $this->getDisplayName($name) . "の形式が不正です";
      }
    }
  }
  
  public function validateSex($name, $value)
  {
    if (!is_empty($value) && !in_array((int)$value, array(1, 2), true)) {
      return $this->getDisplayName($name) . "の値が不正です";
    }
  }
}
※メールアドレス形式のバリデーションは要求や実装者の思想などによって大きく変わるため、実装は省略しています。あなたのいつものチェックコードをコピーしてください。

バリデーションメソッドを実装した後は、それらを登録するだけです。
class Forms_User extends Form_Object
{
  ...
  
  protected $validators = array(
    "name" => ...,
    "email" => array("required", "strlen(255)", "validateEmail"),
    "age" => ...,
    "sex" => array("required", "validateSex"),
  );
}
なお、今回は性別をセレクトボックスで入力させているため、このようなカスタムバリデーションはやりすぎかもしれません。何らかの方法で1, 2以外の値をPOSTしたとしても、不都合なこと(検索にひっかからないなど)がそのユーザ自身に対してのみしか発生しないのならば無視できるためです。

入力部品(HTML)を書き出すメソッド

  • text($name, $attrs = '') - インプット(type="text")
  • password($name, $attrs = '') - インプット(type="password")
  • textarea($name, $attrs = '') - テキストエリア
  • hidden($name, $attrs = '') - インプット(type="hidden")
  • select($name, $values, $attrs = '') - セレクトボックス
  • radio($name, $values, $attrs = '') - ラジオボタン
  • checkbox($name, $values, $attrs = '') - チェックボックス
  • date($name, $yearRange = null, $includeBlank = false) - 年月日のセレクトボックス
  • datetime($name, $yearRange = null, $includeSecond = false, $includeBlank = false) - 年月日時分(秒)のセレクトボックス
  • file($name, $attrs = '') - ファイル参照
$attrs変数 を受け取るメソッドは、書き出すHTMLにそれを追加します。例えば $form->input("test", 'id="myid" class="myclass"') とした場合、出力されるHTMLは下記のようになります。
<input id="myid" class="myclass" type="text" name="test" value="" />
なお、radioとcheckboxに関しては $attrs に "id=" を含めても出力されるHTMLには反映されません。選択要素それぞれにlabelで使用するidを、内部で自動的に付加するためです。

例えば $form->radio("test", array("1" => "one", "2" => "two"), 'id="myid" class="myclass"') としても、出力されるのは下記のようになります。
<input id="radio_1" class="myclass" type="radio" name="test" value="1" /><label for="radio_1">one</label>
<input id="radio_2" class="myclass" type="radio" name="test" value="2" /><label for="radio_2">two</label>
select()メソッド の第2引数 $values は通常配列ですが、下記のように特殊な文字列を渡すことも可能です。
$form->select("test", "20:100")
上記では、20から100の数値を選択するセレクトボックスが作成されます。
このように数値をセレクトボックスにて選択させる場合、その数値の配列をわざわざ作成する必要はありません。
<select name="test">
  <option value="20">20</option>
  <option value="21">21</option>
  <option value="22">22</option>
  ...
  <option value="100">100</option>
</select>
dateやdatetimeの$includeBlankをtrueにすると、各セレクトボックスの先頭に空optionが追加されます。
また、$yearRange(年のセレクトボックスのoption)には下記のような引数を渡すことができます。
  • "-5" - 5年前~当年
  • "+5" - 当年~5年後
  • "10" - 5年前~5年後
  • "2000:2010" - 2000年~2010年
  • array(2000, 2010) - 2000年~2010年