CodeIgniterを始めて一番最初に躓いたのがフォームバリデーションについてです。
バリデーションの種類として標準で定義されているものに加えて、コールバック(callback)関数を用いてオリジナルなバリデーションを定義することも出来ます。
私は、とある入力欄が入力されているか、かつデータベースに存在するか(validate_credentialsという自分で定義した関数でチェック)という検証をしたかったので、
$this->form_validation->set_rules("hoge", "HOGE", "required|trim|callback_validate_credentials");
と書きました。
入力チェックはrequiredで検証します。とあるCodeigniter入門サイトのコードを真似て書いたものですが、未入力の時は必須エラーとなり、データベースに存在しない場合は独自のエラーメッセージを表示することを想定しています。しかしながら、未入力で送信して検証すると先にvalidate_credentials関数でのチェックが行われてしまいます。
この原因と対策について調べると、
- callbackが優先されるのがは通常の仕様である
- ルールを分けてrequired→callbackの順に検証するような処理にする
- コアの_prepare_rulesをcallbackよりもrequiredが優先する処理でオーバライド
という事が分かりました。
2の場合はコードが長くなってしまうので3の方法を採用します。
下記コードは、公式フォーラムのスレッドにかつて掲載されていたものです。
<?php
class MY_Form_validation extends CI_Form_validation {
public function __construct()
{
parent::__construct();
}
protected function _prepare_rules($rules)
{
// Required rules always go first
$required_rules = array();
$new_rules = array();
$callbacks = array();
foreach ($rules as &$rule)
{
// Let 'required' always be the first rule
// No use in running any callbacks if required has not been checked
if ($rule === 'required')
{
array_unshift($required_rules, 'required');
}
// 'isset' is a kind of a weird alias for 'required' ...
elseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required'))
{
array_unshift($required_rules, 'isset');
}
// The old/classic 'callback_'-prefixed rules
elseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0)
{
$callbacks[] = $rule;
}
// Proper callables
elseif (is_callable($rule))
{
$callbacks[] = $rule;
}
// "Named" callables; i.e. array('name' => $callable)
elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
{
$callbacks[] = $rule;
}
// Everything else goes at the end of the queue
else
{
$new_rules[] = $rule;
}
}
// Required rules go first, then any callbacks, then the rest
$callbacks = array_merge($required_rules, $callbacks);
return array_merge($callbacks, $new_rules);
}
}
上記コードを、プレフィックスのMY_を付けた”MY_Form_validation.php”として”application/libraries”に保存します。
これで期待する動作になりました。