6

I'm trying to make yii2 to validate my ActiveForm field that should be numeric and of 8 characters long.

Following is what I tried in the default LoginForm model of yii2/advanced/backend, but unfortunately the isNumeric validator simply doesn't kick in:

public function rules()
{
    return [
        // username and password are both required
        [['username', 'password'], 'required'],
        // username should be numeric
        ['username', 'isNumeric'],
        // username should be numeric
        ['username', 'string', 'length'=>8],
        // password is validated by validatePassword()
        ['password', 'validatePassword'],
    ];
}

/**
 * Validates if the username is numeric.
 * This method serves as the inline validation for username.
 *
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
public function isNumeric($attribute, $params)
{
    if (!is_numeric($this->username))
        $this->addError($attribute, Yii::t('app', '{attribute} must be numeric', ['{attribute}'=>$attribute]));
}

/**
 * Validates the password.
 * This method serves as the inline validation for password.
 *
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
public function validatePassword($attribute, $params)
{
    if (!$this->hasErrors()) {
        $user = $this->getUser();
        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError($attribute, 'Incorrect username or password.');
        }
    }
}

I also tried adding a scenario as suggested in a related post (https://stackoverflow.com/a/27817221/2037924) but that only worked (as in displayed the error) if I did not include the password field in the scenario.

Is this a good way to achieve this at all, or can you think of a better way of doing it?

Note: the reason I define username as string is because the numbers may contain leading 0's.

Community
  • 1
  • 1
benomatis
  • 5,536
  • 7
  • 36
  • 59

4 Answers4

11

Try with integer data type:

[['username'], 'integer'],
[['username'], 'string', 'min' => 8],

It will validate both numeric and length. This will do the trick.

Insane Skull
  • 9,220
  • 9
  • 44
  • 63
  • `min` in this case will check for a number that has a value of 8 minimum, not a length of characters being 8, ie. `01` will pass, and it should not... – benomatis Oct 12 '15 at 06:19
  • I could have sworn I tried this... anyways, this is working, so thank you! – benomatis Oct 12 '15 at 19:19
  • 1
    -1231231 and +1231231 will pass your check, because it is an integer AND it is 8 letters long. I guess that is ok. – Mihai P. Oct 13 '15 at 00:24
  • @MihaiP. I haven't thought of minus and plus signs, to be honest, good point... I think this whole issue comes down to the fact that custom validators do not automatically work client side, [js validation has to be turned on](http://stackoverflow.com/questions/33088935/yii2-activeform-combine-rules-multiple-validation-on-one-field#comment54016068_33088935) according to a commenter on my other question – benomatis Oct 14 '15 at 18:56
  • sorry, I had to accept @MihaiP.'s answer as the correct one. Purely from the perspective of this question he is right. The reason his answer didn't work before was because I expected my customValidator function within the form class to work on client-side. As it comes out, that will only work if you set up a custom validator class separately, see more on this [here](http://www.yiiframework.com/forum/index.php/topic/68095-advancedbackend-activeform-custom-validator-ignored) – benomatis Oct 14 '15 at 20:23
9

Read more about the validations here: http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html#number

This works fine for me using the contact form from yii2-basic

/**
 * @return array the validation rules.
 */
public function rules()
{
    return [
        // name, email, subject and body are required
        [['name', 'email', 'subject', 'body'], 'required'],
        // email has to be a valid email address
        ['email', 'email'],
        ['subject', 'is8NumbersOnly'],
        // verifyCode needs to be entered correctly
        ['verifyCode', 'captcha'],
    ];
}

public function is8NumbersOnly($attribute)
{
    if (!preg_match('/^[0-9]{8}$/', $this->$attribute)) {
        $this->addError($attribute, 'must contain exactly 8 digits.');
    }
}
Mihai P.
  • 9,307
  • 3
  • 38
  • 49
  • sorry, but how will this make it 8 digits, and also consider leading 0's? – benomatis Oct 11 '15 at 23:59
  • nope, not working (without the scenario), I put a simple string with letters in the username field and it validates fine. Again, it works with the scenario, but only if I leave the password field out of it. I believe the issue is not with the validator function, but with the way the scenario is handled, or somehow the 2 validators don't "work together"(?)... seriously, no idea, hence my question... – benomatis Oct 12 '15 at 06:16
  • works perfectly. No idea what scenario you are talking about. I tested my code and it just works for me. – Mihai P. Oct 13 '15 at 00:08
  • See my comment on the accepted answer, make sure you want to accept + and - in the fields. – Mihai P. Oct 14 '15 at 00:18
  • You were right, the way my question was asked, this is the correct answer. Thank you. I explained the actual issue I had in my comment on the other answer, but to be sure history is kept (for the case that one is deleted), here it is again: I expected my customValidator function within the form class to work on client-side. As it comes out, that will only work if you set up a custom validator class separately, see more on this [here](http://www.yiiframework.com/forum/index.php/topic/68095-advancedbackend-activeform-custom-validator-ignored) – benomatis Oct 14 '15 at 20:24
  • wait, if you want it to work client side you have to write custom js code for it. You do not have to be in another class just you have to write special JS code for it to work. see the example here: http://www.yiiframework.com/doc-2.0/guide-input-validation.html#conditional-validation – Mihai P. Oct 15 '15 at 06:33
  • Next time mention that you want client side validation, it will be much easier to figure out that is happening. What I gave you will not work if you want client side validation. I always disable that and use ajax validation instead, much easier to set up. – Mihai P. Oct 15 '15 at 06:34
  • the doc wasn't very clear to me, to be honest, I didn't quite get how I could implement that directly in my LoginForm class... – benomatis Oct 15 '15 at 13:33
  • to your point on the documentation, they actually create a separate validator in order to make the custom validator function work client-side, so not sure how I'd do it directly in the LoginForm, to be honest... – benomatis Oct 16 '15 at 20:15
  • Not sure why you do not use ajax validation. Unless you have a very high traffic website it should work perfect. I do not even bother with client side validation, just ajax one and you are ready to go. – Mihai P. Oct 17 '15 at 00:43
  • I like to think of my web app as a potentially high traffic one, and build it that way for easy scalability... I don't see the difficulty in client-side validation for now, though that may change ;) – benomatis Oct 17 '15 at 04:44
  • Everybody thinks their website will change the world. Look up MVP (minimum viable product), I would just build it the fastest way possible just to get it going, when you start having issues with speed you look into improving it. Too many project just get bogged down in insignificant details and making "expensive" tweaks. – Mihai P. Oct 19 '15 at 01:40
  • Thanks for the advice and all your help!! – benomatis Oct 19 '15 at 04:22
1
public function rules()
{
    return [
        // username should be numeric
        ['username', 'match', 'pattern' => '/^\d{8}$/', 'message' => 'Field must contain exactly 8 digits.],

        // ...
    ];
}
  • 2
    This is pretty much the same as [Mihai P.'s already accepted answer](http://stackoverflow.com/a/33071398/2037924) it's just less complete... – benomatis Aug 29 '16 at 18:38
  • I don't understand why do you think, that the rule ['username', 'match', 'pattern' => '/^\d{8}$/', 'message' => 'Field must contain exactly 8 digits.'] is incomplete. The method isNumeric() don't do more than my recomendation but it has more rows of code and that is not optimal solution. IMHO – Anatolii Dev Sep 02 '16 at 05:48
  • for one, you didn't even include an error message, and second, you solution, again, is almost exactly the same as a previous answer, so it's not really useful... but see how the community decides, ie if you'll receive upvotes... – benomatis Sep 02 '16 at 05:51
  • @Dave2e ditto for this answer – miken32 Sep 03 '16 at 23:47
0

try this:

public function rules()
{
return [
    // name, email, subject and body are required
    [['name', 'email', 'subject', 'body'], 'required'],
    // email has to be a valid email address
    ['email', 'email'],
    [['subject'], 'number'],
    [['subject'], 'string', 'max' => 8, 'min' => 8],
    // verifyCode needs to be entered correctly
    ['verifyCode', 'captcha'],
];
}