0

I have following validation rule:

['dagen_ids', 'required', 'message' => 'Selecteer.', 'when' => function($model) {
    return $model->frequentie == 2;
}]

The attribute dagen_ids is an array thats being populated by a Select2 widget (multi-select).

What I to achieve is to have a validation rule that only is required when $model->frequentie == 2, and when it is, the user needs to select at least 2 options.

For the second part I tried the following inline validation code, but it doesn't seem to have any effect unfortunately..

['dagen_ids', function ($attribute, $params) {
    if (count($this->$attribute) < 2) {
        $this->addError($attribute, 'Selecteer minimaal 2 dagen.');
    }
}]

Could someone help me a little further and explain to me how to combine these validation rules?

EDIT

Controller:

public function actionView($id) 
{
    $planning = $this->findModel($id);
    $post = Yii::$app->request->post();

    if (Yii::$app->request->isAjax && $planning->load($post) && $planning->validate() && $planning->save()) {
            Yii::$app->session->setFlash('success', [
                'type' => 'success',
                'duration' => 10000,
                'icon' => 'fa fa-check',
                'message' => 'Opdracht is succesvol bijgewerkt.'.Html::button(Icon::show('hand-o-right', ['class' => ''], Icon::FA). Yii::t('app', 'View'), ['class' => 'btn btn-sm btn-success modalButton pull-right', 'data-notify' => 'dismiss', 'data-content' => Url::to(['planning/view', 'id' => $id])]),
                'title' =>  Html::tag('span', 'Opdracht bijgewerkt', ['style' => 'font-weight: bold;']),
                'positonY' => 'top',
                'positonX' => 'right'
            ]);
    }
    else {
        return $this->renderAjax('view', ['planning' => $planning]); 
    }
}

JQuery:

$('body').on('beforeSubmit', 'form#view_form', function () {

    var form = $(this);
    // return false if form still have some validation errors
    if (form.find('.has-error').length) {
        return false;
    }

    // submit form
    $.ajax({
        url: form.attr('action'),
        data: form.serialize(),
        type: 'post'
    }).done(function(){
        $('.modal').modal('hide');
        console.log('Form send!');
        $.pjax.reload({
            container: '#planner_grid', 
            timeout: 10000, 
            replace: false
        });
    }).fail(function(){
        console.log('Server error...');
    }); 

    return false;
});
JK87
  • 379
  • 2
  • 12
  • 26

2 Answers2

1

Quote from The Definitive Guide to Yii 2.0: Validating Input - Inline Validators:

Note: By default, inline validators will not be applied if their associated attributes receive empty inputs or if they have already failed some validation rules. If you want to make sure a rule is always applied, you may configure the skipOnEmpty and/or skipOnError properties to be false in the rule declarations.

So you might need to add those 2 properties to false, and you can also change your condition so it checks the value on frequentie:

['dagen_ids', function ($attribute, $params) {
    if ($this->frequentie == 2 && count($this->$attribute) < 2) {
        $this->addError($attribute, 'Selecteer minimaal 2 dagen.');
    }
}, 'skipOnEmpty' => false, 'skipOnError' => false]

This way you don't really need to have 2 different validation rules.

marche
  • 1,756
  • 1
  • 14
  • 24
  • I tried your code, but now I'm able to submit the form somehow.. When I have a record with 5 `dagen_ids` selected by example and I edit the row and select 0 `dagen_ids` instead, the form will be submitted, but the `dagen_ids` attribute seems to be unaffected and still has 5 values. I will edit my question and paste my controller and JQuery. I also tried to write a standalone validator with your code using the Yii2 docs (http://www.yiiframework.com/doc-2.0/guide-input-validation.html#standalone-validators) but there isn't any difference.. – JK87 Aug 30 '16 at 10:16
  • It would help to know how you use these dagen_ids, because the validation is not the same thing as the code that handles many_to_many relationships which i guess this model of yours uses. – marche Aug 30 '16 at 20:03
0

Finally figured it out..

In this case with an inline/standalone validator I needed Ajax validation.

I'm using a DetailView plugin and it looks like this:

echo DetailView::widget([
    'model' => $planning,
    'attributes' => $attributes,
    'mode' => 'view',
    'panel' => [
        'heading' => $planning->stopTitel, 
        'type' => DetailView::TYPE_PRIMARY,
    ],
    'bordered' => false,
    'responsive' => true,
    'hover' => true,
    'fadeDelay'=> 800,
    'deleteOptions' => [
        'params' => ['id' => $planning->planning_id, 'delete' => true],
        'url' => ['delete']
    ],
    'container' => ['id' => 'opdracht-bewerken'],
    'formOptions' => ['id' => 'view_form', 'action' => Url::current(['#' => 'opdracht-bewerken']), 'validationUrl' => Url::toRoute(['validate-form']), 'enableClientValidation' => true, /*'enableAjaxValidation' => true*/], // If you want Ajax validation on the whole form, uncomment this.
]);

But I only want one field to be Ajax validated so in my case I needed to add the following to my dagen_ids field:

'fieldConfig' => ['enableAjaxValidation' => true] (Documentation)

My Controllers looks like:

use yii\web\Response;
use yii\helpers\Json;
use kartik\widgets\ActiveForm;

    public function actionView($id) 
    {
        $planning = $this->findModel($id);
        $post = Yii::$app->request->post();

        if ($planning->load($post) && $planning->save()) {
            Yii::$app->session->setFlash('success', [
                'type' => 'success',
                'duration' => 10000,
                'icon' => 'fa fa-check',
                'message' => 'Opdracht is succesvol bijgewerkt.'.Html::button(Icon::show('hand-o-right', ['class' => ''], Icon::FA). Yii::t('app', 'View'), ['class' => 'btn btn-sm btn-success modalButton pull-right', 'data-notify' => 'dismiss', 'data-content' => Url::to(['planning/view', 'id' => $id])]),
                'title' =>  Html::tag('span', 'Opdracht bijgewerkt', ['style' => 'font-weight: bold;']),
                'positonY' => 'top',
                'positonX' => 'right'
            ]);
        }
        else {
            return $this->renderAjax('view', ['planning' => $planning]); 
        }
    }

    public function actionValidateForm() {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $planning = new Planning();
        $post = Yii::$app->request->post();

        $planning->load($post);
        return ActiveForm::validate($planning);
    }

And my standalone validator:

namespace backend\components\validators;

use yii\validators\Validator;

class DagenValidator extends Validator
{
    public function validateAttribute($model, $attribute)
    {
        if ($model->frequentie == 2 && count($model->$attribute) < 2) {
            $this->addError($model, $attribute, 'Selecteer minimaal 2 dagen.');
        }
    }
}

Now in my Planning model I can add to following rule:

['dagen_ids', DagenValidator::className(), 'skipOnEmpty' => false, 'skipOnError' => false]
JK87
  • 379
  • 2
  • 12
  • 26