selectボックスの選択した値に応じて、別の入力欄に入力がされているかをチェックするようなバリデータを実装したい。
Yiiのエクステンションがあるか探してみたんだけど、phpのバリデータのみで、クライアントサイドのバリデートもするものは見つからなかったので、ちょっと作ってみた。
要件としては
となります。
まず、以下のようなselectボックスとしてuse_ej、入力欄としてexdataがあるフォームを用意します。
<?php
/* @var $model Event *//* @var $form CActiveForm */?>
<div class="form">
<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'id'=>'event-form',
'clientOptions'=>array('validateOnSubmit'=>true),
)); ?>
<p class="help-block">「 <span class="required">*</span> 」のある欄は入力必須です。</p>
<?php echo $form->errorSummary($model); ?>
<?php echo $form->dropDownListRow($model,'use_ej', $model->getUseEjArray(),array('class'=>'span5')); ?>
<?php echo $form->textAreaRow($model,'exdata',array('class'=>'span5')); ?>
<div class="form-actions">
<?php $this->widget('bootstrap.widgets.TbButton', array(
'buttonType'=>'submit',
'type'=>'primary',
'label'=>'登録',
)); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form --> 次にモデルを用意します。
バリデートのルールとして、use_ejは入力必須、exdataはuse_ejの値がself::EVENT_USE_EJ_NOの場合のみ必須となるようにします。
<?php
class Event extends CActiveRecord
{
const EVENT_USE_EJ_YES=1;
const EVENT_USE_EJ_NO=2;
public $use_ej_array=array(
self::EVENT_USE_EJ_YES => '利用する',
self::EVENT_USE_EJ_NO => '利用しない',
);
public rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('use_ej', 'required'),
array('exdata', 'ext.MyValidators.exData', 'field'=>'use_ej', 'value'=>self::EVENT_USE_EJ_NO, 'message'=>'「利用しない」を選んだ場合は入力してください。'),
);
}
public getUseEjArray()
{
return $this->use_ej_array;
}
} protected/extensions/MyValidators/exDataの内容は以下のような感じです。
バリデータ作成については、「Yiiでvalidatorを作成する。」を見てください。
<?php
class exData extends CValidator
{
public $field;
public $value;
private $_message='{attribute} を入力してください。';
/**
* Validates the attribute of the object.
* If there is any error, the error message is added to the object.
* @param CModel $object the object being validated
* @param string $attribute the attribute being validated
*/ protected validateAttribute($object,$attribute)
{
$f=null;
// check the strength parameter used in the validation rule of our model
if (!empty($this->field) && isset($object->{$this->field}))
$f=$object->{$this->field};
if(!empty($f) && $this->value==$f)
{
// extract the attribute value from it's model object
$value=$object->$attribute;
if(empty($value))
{
$message=$this->message!==null?$this->message:Yii::t('yii',$this->_message);
$this->addError($object,$attribute,$message);
}
}
}
/**
* Returns the JavaScript needed for performing client-side validation.
* @param CModel $object the data object being validated
* @param string $attribute the name of the attribute to be validated.
* @return string the client-side validation script.
* @see CActiveForm::enableClientValidation
*/ public clientValidateAttribute($object,$attribute)
{
$message=$this->message;
$condition="jQuery.trim(value)=='' && jQuery.trim(jQuery('#".get_class($object).'_'.$this->field."').val())=='".$this->value."'";
if($message===null)
$message=Yii::t('yii',$this->_message);
return "
if(".$condition.") {
messages.push(".CJSON::encode($message).");
}
";
}
} ただ、この場合、
ということをしてもexdataのエラーは消えないということが起こります。
モデルのrules関数にuse_ejに新しいルールを用意しようと思ったんですが、そうするとuse_ejのほうに期待しないエラーが表示されることになります。
で、結局、うまい方法がわからなかったので、ビューに、「use_ejの値が変わったら、exdataフィールドのみバリデートする」というJavascriptを追加することにしました。
Javascriptを追加したビューは以下のような感じです。
<?php
/* @var $model Event *//* @var $form CActiveForm */
Yii::app()->clientScript->registerScript('useej', "
var getAFValue = (o) {
var type,
c = [];
if (!o.length) {
return undefined;
}
if (o[0].tagName.toLowerCase() === 'span') {
o.find(':checked').each( () {
c.push(this.value);
});
return c.join(',');
}
type = o.attr('type');
if (type === 'checkbox' || type === 'radio') {
return o.filter(':checked').val();
} else {
return o.val();
}
};
$('#Event_use_ej').change((){
var form=$('#event-form');
var settings=form.data('settings');
var messages = {};
$.each(settings.attributes, (i) {
if(this['id']=='Event_exdata'){
var value, msg = [];
value = getAFValue(form.find('#' + this['inputID']));
this.clientValidation(value, msg, this);
if (msg.length) {
messages[this['id']] = msg;
}
if (this.status) {
$.fn.yiiactiveform.updateInput(this, messages, form);
}
}
});
});
");
?>
<div class="form">
<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'id'=>'event-form',
'clientOptions'=>array('validateOnSubmit'=>true),
)); ?>
<p class="help-block">「 <span class="required">*</span> 」のある欄は入力必須です。</p>
<?php echo $form->errorSummary($model); ?>
<?php echo $form->dropDownListRow($model,'use_ej', $model->getUseEjArray(),array('class'=>'span5')); ?>
<?php echo $form->textAreaRow($model,'exdata',array('class'=>'span5')); ?>
<div class="form-actions">
<?php $this->widget('bootstrap.widgets.TbButton', array(
'buttonType'=>'submit',
'type'=>'primary',
'label'=>'登録',
)); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
なにか、スッキリする方法があれば教えて下さい。