Olá a todos!

Bom, como todos sabem, existe a validação pelo lado do cliente, que é uma validação em javascript, sem a necessidade de usar validações em ajax, que dependendo do servidor haverá um lag na respostas.

Por esse motivo que a versão 2 do Yii Framework, vem por padrão a validação por "Client-Side" sem precisa setar nada no ActiveForm.

Mas vamos ao o que interessa, tive uma situação real, que o cliente queria que o formulário de busca contem-se 2 campos que houvesse validação, mais que seria obrigatório um ou outro, se caso nenhum dos dois forem selecionado ou preenchido, a validação indica que os campos é de seleção/preenchimento obrigatório, mas caso 1 dos campos seja selecionado, o outro campo deixa de ser obrigatório.

Na versão 2 do Yii Framework, existe a possibilidade de acrescentar sua própria validação, complementando ao padrão do formulário, para isso vamos usar o "Deffered Validation", que é parecido com o Transaction do MYSQL, é uma execução assíncrona em javascript, assim você consegue validar todos os campos e depois mostrar a mensagem.

Abaixo segue a estrutura do formulário:

<?php $this->registerJs('var stopValidation = false;');?>
<?php $model = FormSearch; ?>
<?php $form = ActiveForm::begin(['id' => 'search-form']); ?>
<?php $list = ArrayHelper::map(Type::find()->all(), 'id', 'title'); ?>
<?= $form->field($model, 'type')->dropDownList($list, ['prompt' => 'Select']); ?>
<?= $form->field($model, 'query'); ?>
<?php ActiveForm::end(); ?>

A class FormSearch:

<?php
namespace app\models\form;
use yii\base\Model;
class FormSearch extends Model
{

    public $type;
    public $query;

    public function rules()
    {
        return [
            [['type', 'query'], 'safe'],
            [['type', 'query'], \app\components\FormSearchValidator::className()],
            ['query', 'filter', 'filter' => 'strip_tags'],
            ['query', 'filter', 'filter' => function($value) {
                    return \yii\helpers\Html::encode($value);
                }
            ],
        ];
    }

    public function attributeLabels()
    {
        return [
            'type' => 'Tipo',
            'query' => 'Título',
        ];
    }

}

Agora vou postar uma class que se extende a class yii\validators\Validator.

namespace app\components;

use yii\validators\Validator;

class FormSearchValidator extends Validator
{

    public function init()
    {
        parent::init();
        $this->message = 'On of the fields is required.';
    }

    public function validateAttribute($model, $attribute)
    {
        /*https://github.com/yiisoft/yii2/blob/master/docs/guide/input-validation.md#using-client-side-validation-*/
    }

    public function clientValidateAttribute($model, $attribute, $view)
    {
        if (empty($model->type) && empty($model->query)) {
            $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
            return <<<JS
var def = $.Deferred(); 

if (attribute.name=='type' && value != '') { 
    stopValidation = true;
    def.reject(); 
}
if (attribute.name=='query' && value != '') { 
    stopValidation = true;
    def.reject(); 
} 

if (attribute.name=='type' && value == '') { 
    if(stopValidation == true){
        def.reject();
    } else { 
        messages.push($message); def.resolve();
    }
}
if (attribute.name=='query' && value == '') { 
    if(stopValidation == true){
        def.reject();
    } else { 
        messages.push($message); 
        def.resolve(); 
    }
}
deferred.push(def);
JS;
        }
    }

}

Caso alguém tenha uma solução melhor, estamos aberto a troca de idéias.

Caso tenha alguma dúvida, não exite em postar.


Discussão