Piece Framework Listバリデータの拡張
こんばんは、前回のブログが本家のニュースで紹介され、ただただ恐縮している高橋です。
それにしても、一緒に紹介されているPieceCodeGeneratorの出来は素晴らしいですね。私の稚拙なスクリプトも、もっと使いやすくなるよう改良していきたいと思います。
さて、今回はPiece Frameworkでリスト項目のバリデート処理を行うときのことについて書いてみます。
登録フォームなどでリスト項目がある場合、アクションのphpで項目をテンプレートに割り付け、バリデーションのyamlにその検証リストを設定しますが、そのまま記述すると次のような感じになるかと思います。
アクションのphp
<?php
・・・
$elements = $this->_getFormElements();
$elements['fruit']['_options'] = array('apple' => 'りんご',
'banana' => 'バナナ',
'cherry' => 'さくらんぼ'
);
$viewElement = &$this->_payload->getViewElement();
$viewElement->setElement('_elements', $elements);
・・・
?>
バリデーションのyaml
・・・
- name: fruit
description: 果物
validator:
- name: List
rule:
elements: [apple, banana, cherry]
message: リストにある%_description%を選択してください
・・・
このように記述した場合、リストの項目に変更があるとphpとyamlの両方を修正する必要があるため、修正ミスが起こりやすいですね。
そこで、WithMethodを使用して、リスト項目を一元管理できるように変更してみます。
リスト項目のクラス
<?php
class MyFruit
{
static $fruit = array('apple' => 'りんご',
'banana' => 'バナナ',
'cherry' => 'さくらんぼ'
);
static function checkFruit($value, &$payload, &$results) {
if (isset(self::$fruit[$value])) {
return true;
} else {
return false;
}
}
}
?>
アクションのphp
<?php
・・・
$elements = $this->_getFormElements();
$elements['fruit']['_options'] = MyFruit::$fruit;
$viewElement = &$this->_payload->getViewElement();
$viewElement->setElement('_elements', $elements);
・・・
?>
バリデーションのyaml
・・・
- name: fruit
description: 果物
validator:
- name: WithMethod
rule:
class: MyFruit
method: checkFruit
message: リストにある%_description%を選択してください
・・・
これでリスト項目に変更があっても、MyFruitだけ書き換えればいいので、ちょっと楽になりました。
ただ、新たに別のリスト項目が増えた場合、項目の配列だけでなくcheckFruit()と同様のメソッドも追加しないといけないので、そこがまだ面倒ですね。
そこで、ListとWithMethodを足して2で割ったようなList2.phpという独自バリデータを作ってみます。yamlの指定はelementsの代わりに、classとmemberでも配列の指定ができるものとします。
バリデーションのyaml
・・・
- name: fruit
description: 果物
validator:
- name: List2
rule:
class: MyFruit
member: fruit
message: リストにある%_description%を選択してください
・・・
List2.php
<?php
・・・
function validate($value)
{
if (!is_array($value)) {
$value = (array)$value;
}
$class = $this->_getRule('class');
$member = $this->_getRule('member');
$isStatic = $this->_getRule('isStatic');
if (is_null($class) || is_null($member)) {
$elements = $this->getRule('elements');
} else {
if (!Piece_Right_ClassLoader::loaded($class)) {
Piece_Right_ClassLoader::load($class, $this->_getRule('directory'));
if (Piece_Right_Error::hasErrors('exception')) {
return;
}
if (!Piece_Right_ClassLoader::loaded($class)) {
Piece_Right_Error::push(PIECE_RIGHT_ERROR_NOT_FOUND,
"The class [ $class ] not found in the loaded file.",
'exception',
array('validator' => __CLASS__)
);
return;
}
}
if ($isStatic) {
$elements = $class::$$member;
} else {
$instance = &new $class();
if (isset($instance->$member)) {
return false;
}
$elements = $instance->$member;
}
}
if (!is_array($elements)) {
return false;
}
foreach ($value as $element) {
if (!in_array($element, $elements)) {
return false;
}
}
return true;
}
・・・
function _initialize()
{
$this->_addRule('elements', array());
$this->_addRule('class');
$this->_addRule('member');
$this->_addRule('isStatic', true);
$this->_addRule('directory');
}
・・・
?>
これで完成かなと思ったところ、「$elements = $class::$$member;」でシンタックスエラーが発生!
どうやらこうは書けない模様です。とはいえ、eval()ではreturn以外で値が取得できませんし、call_user_func()ではメソッドを書かずに済ますという当初の目的が果たせません。
あともう少しのところで、今回は時間切れ……。ううーん、何かいい方法はないものでしょうか?