12

I have two fields on the form ( forgotpassword form ) username and email Id . User should enter one of them . I mean to retrieve the password user can enter user name or the email id . Could some one point me the validation rule for this ?

Is there any inbuilt rule I can use ?

( Sorry if it is already discussed or if I missed)

Thanks for your help

Regards

Kiran

Bujji
  • 1,717
  • 10
  • 41
  • 66

9 Answers9

22

I was trying to solve same problem today. What I've got is the code below.

public function rules()
{
    return array(
        // array('username, email', 'required'), // Remove these fields from required!!
        array('email', 'email'),
        array('username, email', 'my_equired'), // do it below any validation of username and email field
    );
}

public function my_required($attribute_name, $params)
{
    if (empty($this->username)
            && empty($this->email)
    ) {
        $this->addError($attribute_name, Yii::t('user', 'At least 1 of the field must be filled up properly'));

        return false;
    }

    return true;
}

General idea is to move 'required' validation to custom my_required() method which can check if any of field is filled up.

I see this post is from 2011 however I couldn't find any other solution for it. I Hope it will work for you or other in the future.

Enjoy.

Mario Bross
  • 244
  • 2
  • 3
19

Something like this is a bit more generic and can be reused.

public function rules() {
    return array(
        array('username','either','other'=>'email'),
    );
}
public function either($attribute_name, $params)
{
    $field1 = $this->getAttributeLabel($attribute_name);
    $field2 = $this->getAttributeLabel($params['other']);
    if (empty($this->$attribute_name) && empty($this->$params['other'])) {
        $this->addError($attribute_name, Yii::t('user', "either {$field1} or {$field2} is required."));
        return false;
    }
    return true;
}
Damian Dennis
  • 718
  • 7
  • 9
  • 3
    This should be the accepted answer, since it is more detailed and takes advantage of the rule params so you don't have to hardcode the second attributes in the validator function. – Dzhuneyt Feb 26 '14 at 09:13
  • 1
    You should use another syntax for the translation: Yii::t('user', "either {field1} or {field2} is required."), array('{field1}' => $field1, '{field2}' => $field2)) – Fabian Horlacher Sep 18 '14 at 14:04
  • I tested this solution on Yii2, and got the _Setting unknown property: `yii\validators\InlineValidator::other`_ exception. It seems that the inline validators will only recognize parameters decleared in `yii\validators\Validator`, such as `$on`, `$skipOnEmpty` and etc. @hasMobi-AndroidApps – drodata Mar 26 '16 at 14:00
10

Yii2

namespace common\components;

use yii\validators\Validator;

class EitherValidator extends Validator
{
    /**
     * @inheritdoc
     */
    public function validateAttributes($model, $attributes = null)
    {
        $labels = [];
        $values = [];
        $attributes = $this->attributes;
        foreach($attributes as $attribute) {
            $labels[] = $model->getAttributeLabel($attribute);
            if(!empty($model->$attribute)) {
                $values[] = $model->$attribute;
            }
        }

        if (empty($values)) {
            $labels = '«' . implode('» or «', $labels) . '»';
            foreach($attributes as $attribute) {
                $this->addError($model, $attribute, "Fill {$labels}.");
            }
            return false;
        }
        return true;
    }
}

in model:

public function rules()
{
    return [
        [['attribute1', 'attribute2', 'attribute3', ...], EitherValidator::className()],
    ];
}
AndreyTalanin
  • 208
  • 2
  • 5
  • You can also define method "clientValidateAttribute" if you want to set up client side validation. Example loop through model rules, get all "either" validator fields and set up javascript code to check values in HTML form – Aivar Feb 06 '17 at 12:51
  • this should be the best anwser. thank you guy, you saved my time! – riverlet Mar 14 '18 at 03:25
2

I don't think there is a predefined rule that would work in that case, but it would be easy enough to define your own where for username and password fields the rule was "if empty($username . $password) { return error }" - you might want to check for a min length or other field-level requirements as well.

ldg
  • 9,112
  • 2
  • 29
  • 44
  • Thanks for your response . I defined my own , I have similar kind of cases in many places , so thought I could get one defined one already . But any way thanks for clarifying it . – Bujji Aug 17 '11 at 20:42
  • You could also combine this with an Ajax call that verifies that the field they did fill out exists in the DB and checks any status that might be relavant (disabled account, etc.) if that make sense. Then you could use a simple client-side validation (with the same logic above) to at least check one of the fields is populated. – ldg Aug 17 '11 at 20:51
1

This works for me:

            ['clientGroupId', 'required', 'when' => function($model) {
                return empty($model->clientId);
            }, 'message' => 'Client group or client selection is required'],
Rahul Mankar
  • 910
  • 9
  • 17
0

You can use private property inside model class for preventing displays errors two times (do not assign error to model's attribute, but only add to model without specifying it):

class CustomModel extends CFormModel
{
    public $username;
    public $email;

    private $_addOtherOneOfTwoValidationError = true;

    public function rules()
    {
        return array(
            array('username, email', 'requiredOneOfTwo'),
        );
    }

    public function requiredOneOfTwo($attribute, $params)
    {
        if(empty($this->username) && empty($this->email))
        {
            // if error is not already added to model, add it!
            if($this->_addOtherOneOfTwoValidationError)
            {
                $this->addErrors(array('Please enter your username or emailId.'));

                // after first error adding, make error addition impossible
                $this->_addOtherOneOfTwoValidationError = false;
            }

            return false;
        }

        return true;
    }
}
aslawin
  • 1,981
  • 16
  • 22
0

don't forget "skipOnEmpty" attr. It cost me some hours.

 protected function customRules()
{
    return [
              [['name', 'surname', 'phone'], 'compositeRequired', 'skipOnEmpty' => false,],
    ];
}

public function compositeRequired($attribute_name, $params)
{
    if (empty($this->name)
        && empty($this->surname)
        && empty($this->phone)
    ) {
        $this->addError($attribute_name, Yii::t('error', 'At least 1 of the field must be filled up properly'));

        return false;
    }

    return true;
}
0

Yii 1

It can be optimized of course but may help someone

class OneOfThemRequiredValidator extends \CValidator
{
    public function validateAttribute($object, $attribute)
    {
        $all_empty = true;
        foreach($this->attributes as $_attribute) {
            if (!$this->isEmpty($object->{$_attribute})) {
                $all_empty = false;
                break;
            }
        }

        if ($all_empty) {
            $message = "Either of the following attributes are required: ";
            $attributes_labels = array_map(function($a) use ($object) {
                    return $object->getAttributeLabel($a);
                }, $this->attributes);
            $this->addError($object, $_attribute, $message . implode(',', 
            $attributes_labels));
        }
    }
}
pszaba
  • 1,034
  • 9
  • 16
0

yii1

public function rules(): array
{
    return [
        [
            'id',   // attribute for error
            'requiredOneOf', // validator func
            'id',   // to params array
            'name', // to params array
        ],
    ];
}

public function requiredOneOf($attribute, $params): void
{
    $arr = array_filter($params, function ($key) {
        return isset($this->$key);
    });
    
    if (empty($arr)) {
        $this->addError(
            $attribute,
            Yii::t('yii', 'Required one of: [{attributes}]', [
                '{attributes}' => implode(', ', $params),
            ])
        );
    }
}
mat.twg
  • 438
  • 1
  • 5
  • 11