Curry
PHP Framework

バリデーション

リクエストの検証

入力フォームなどによってユーザーの入力をPOSTやGETなどの形式で受け付けるアプリケーションの場合、想定外の入力はエラー扱いとして入力を受け付けないようにする必要があります。
Validatorクラスを利用するとこの仕組が容易に実装可能です。

Validatorクラスの利用

例えばユーザー情報の入力フォームを想定します。

<form method="post">
  名前:<input type="text" name="name" /><br />
  住所:<input type="text" name="address" /><br />
  年齢:<input type="text" name="age" /><br />
  <input type="submit" value="送信">
</form>

名前、住所、年齢を入力するだけの簡単なフォームです。
全ての項目は必須入力とします。
そして名前は30文字以内、年齢は数字のみというルールにしようとします。

Validatorクラスでこれらのルールに従って検証を行います。
まず検証ルールを設定する必要があります。検証ルールの設定はsetRulesメソッドを利用し、配列で指定します。

$rules = array(
    'name' => array(
        array('rule' => 'required'),
        array('rule' => 'length', 'max' => 30)
    ),
    'address' => array(
        array('rule' => 'required')
    ),
    'age' => array(
        array('rule' => 'required'),
        array('rule' => 'number_string')
    )
)
$validator = new Validator();
$validator->setRules($rules);

項目名をキーとする配列とし、各キーの下には複数の検証ルールを配列で指定します。
更に各ルールは検証条件を配列で指定します。
このように全ての検証条件を階層的な配列として設定します。

設定したうえでvalidateメソッドによって検証を実行します。

$result = $validator->validate($this->post);
if ($result == false) {
    $errors = $validator->getError();
}

validateメソッドを実行するとsetRulesで設定したルールにもとづいて検証が行われ、一つでもエラーが存在する場合はvalidateはfalseを返します。
この場合、getErrorによって項目ごとのエラーメッセージが取得できます。このエラーメッセージを入力フォームに表示することにより、ユーザーに訂正を促します。

検証ルールの種類

検証ルールは以下の物が用意されています。

ルールキー検証内容オプション
required必須入力
length文字列長範囲max、min
length_rangelengthのエイリアスです。lengthと同一動作です。
※このルールは削除予定のため、lengthで代用するようにしてください。
max、min
numeric算術数値
number_string数字文字列(0~9)
alphaアルファベット
alphanumアルファベット・数字文字列
singlebyteシングルバイト
regex正規表現pattern

ユーザー定義ルール

Validatorの内部では、ルールキーをキャメルケースに変換したメソッド名が呼び出され、ルールキーごとの検証処理を行なっています。
例えばlength_rangeの場合、ValidatorのlengthRangeメソッドによって実際の検証が行われています。

ユーザーが任意の検証ルールを追加したいと考えた場合、Validatorを継承したクラスを定義し、そこにユーザー定義メソッドを追加すれば、そのメソッド名をスネークケースに変換した名前のルールキーを指定することにより、validateメソッドで利用可能になります。

例えば郵便番号を検証するzipCodeというメソッドをValidatorの継承クラスに定義します。

library/validator_ex.php
class ValidatorEx extends Validator
{
    public function __construct()
    {
        self::$_messages['zip_code'] = 'invalid zip code';
    }

    public function zipCode($value, $options)
    {
        if (!preg_match('/^[0-9]{3}-[0-9]{4}$/', $value)) {
            return false;
        }
        return true;
    }
}

この場合、setRulesメソッドに設定するルールで、ルールキーとして"zip_code"を指定することで、zipCodeメソッドを利用しての検証が行われます。
ただし、ユーザー定義メソッドは必ず引数を2つとり、第1引数に検証対象値、第2引数に検証オプションとする必要があります。第2引数は引数としての定義は必要ですが、不要な場合はメソッド内部では利用する必要はありません。

$validator->setRules(array(
    'zip' => array(
        array('rule' => 'required'),
        array('rule' => 'zip_code')
    )
));

$post = array('zip' => '000-000');
$res = $validator->validate($post);
if ($res == false) {
    // array('zip' => 'invalid zip code')
    $errors = $validator->getError();
}

コントローラーに組み込まれたValidatorを利用する

ControllerはフィールドとしてValidatorのインスタンスを保持しています。
これを利用しての検証が可能です。

setRulesメソッドによるルールの設定は自動的に行われます。
ただしコントローラークラスの$validateRulesフィールドにルールを定義しておく必要があります。
$validateRulesは、setRulesメソッドに指定する配列を、更にアクション名をキーとした配列の下に格納します。

class UserController extends Controller
{
    protected $validateRules = array(
        'regist' => array(
            'name' => array(
                array('rule' => 'required'),
                array('rule' => 'length', 'max' => 30)
            ),
            'address' => array(
                array('rule' => 'required')
            ),
            'age' => array(
                array('rule' => 'required'),
                array('rule' => 'number_string')
            )
        )
    );

    public function index()
    {
    }

    public function regist()
    {
        // $this->validateRules['regist']のルールで検証が行われる
        $res = $this->validator->validate($this->post);
        if ($res == false) {
            $this->session->errors = $this->validator->getError();
            $this->redirect('/user/index');
        }
    }
}

アクションメソッドではvalidateメソッドを呼び出すだけです。

ユーザー定義のValidator継承クラス

Validatorクラスを継承したクラスをコントローラーの$this->validatorとして利用したい場合、index.phpで以下の2行の設定が必要です。
例えばlibraryディレクトリにValidatorExクラスを定義した場合は以下のようにします。

htdocs/index.php
      ・
      ・
      ・
@@Brequire_once SITE_PATH . 'library/validator_ex.php';@

$dispatcher = new Dispatcher();
@@B$dispatcher->setValidatorClass('ValidatorEx');@
      ・
      ・
      ・

オートバリデート

コントローラーにはバリデーションを自動的に行う機能が備わっています。
通常はValidatorクラスのvalidateメソッドにより、アクションメソッドの中で明示的にバリデーションを行いますが、Controllerクラスの$autoValidateをtrueに設定すると、それすらも必要なく、アクションメソッドより前のタイミングで自動的に検証が行われ、エラーがある時にはリクエスト送信元のアクションへリダイレクトし、エラーを表示します。

class UserController extends Controller
{
    protected $autoValidate = true;

    protected $validateRules = array(
        'regist' => array(
            'name' => array(
                array('rule' => 'required'),
                array('rule' => 'length', 'max' => 30)
            ),
            'address' => array(
                array('rule' => 'required')
            ),
            'age' => array(
                array('rule' => 'required'),
                array('rule' => 'number_string')
            )
        )
    );

Controllerを継承したクラスで$autoValidateフィールドをtrueに指定した上で、
$validateRulesを設定すると、設定されたアクションへのリクエスト時、自動的に検証が実行されます。
検証の結果、エラーが存在しない場合はアクションまで処理が到達します。
エラーが存在した場合、リクエスト元のアクションへリダイレクトします。
このリダイレクト時、リダイレクト先(リクエスト元)のアクションでは、ビューへ"errors"というキーでエラー情報が出力されます。
errorsは、エラー対象となったPOSTやGETの項目のキーにエラーメッセージが格納された配列となっており、ビューテンプレートでこれを表示するようにすることで、エラーが表示が可能です。

app/controllers/user_controller.php
class UserController extends Controller
{
    protected $autoValidate = true;

    protected $validateRules = array(
        'regist' => array(
            'name' => array(
                array('rule' => 'required'),
                array('rule' => 'length', 'max' => 30)
            ),
            'address' => array(
                array('rule' => 'required')
            ),
            'age' => array(
                array('rule' => 'required'),
                array('rule' => 'number_string')
            )
        )
    );

    public function index()
    {
    }

    public function regist()
    {
        // 登録処理
    }
}
app/views/templates/user/index.php
<form method="post" action="/user/regist">
  名前:<input type="text" name="name" /><span class="error"><?php echo $errors['name'] ?></span><br />
  住所:<input type="text" name="address" /><span class="error"><?php echo $errors['address'] ?></span><br />
  年齢:<input type="text" name="age" /><span class="error"><?php echo $errors['age'] ?></span><br />
  <input type="submit" value="送信">
</form>

例えば上の例のようなコントローラークラスとビューテンプレートを用意すれば、これだけで検証が自動的に行われ、エラー表示までが行われます。
こうすることでアクションメソッド内にはバリデーションコードを一切記述せずにバリデーション機能を持たせることが可能になり、コードがシンプルになります。