Yii2 Forms & Validation

Introduction

Yii2 provides a powerful and flexible way to handle forms and validation. In this guide, we will cover everything you need to know about forms, validation rules, ActiveForm widgets, and custom validators.


1. Creating a Form Model

A form model extends yii\base\Model and is used for validation purposes.


namespace app\models;

use Yii;
use yii\base\Model;

class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $message;
    public $agree;

    public function rules()
    {
        return [
            [['name', 'email', 'subject', 'message'], 'required'],
            ['email', 'email'],
            ['subject', 'string', 'max' => 255],
            ['message', 'string', 'min' => 10],
            ['agree', 'required', 'requiredValue' => 1, 'message' => 'You must agree to the terms.'],
        ];
    }
}

2. Using ActiveForm

ActiveForm is used to generate forms in Yii2. Example:

use yii\widgets\ActiveForm;
use yii\helpers\Html;

$form = ActiveForm::begin();

echo $form->field($model, 'name')->textInput(['maxlength' => true]);
echo $form->field($model, 'email')->input('email');
echo $form->field($model, 'subject')->textInput();
echo $form->field($model, 'message')->textarea(['rows' => 6]);
echo $form->field($model, 'agree')->checkbox();

echo Html::submitButton('Submit', ['class' => 'btn btn-primary']);

ActiveForm::end();

2.1 Available Input Widgets in ActiveForm

  • Text Input: $form->field($model, 'name')->textInput();
  • Password Input: $form->field($model, 'password')->passwordInput();
  • Text Area: $form->field($model, 'message')->textarea(['rows' => 6]);
  • Checkbox: $form->field($model, 'agree')->checkbox();
  • Dropdown List: $form->field($model, 'country')->dropDownList(['US' => 'USA', 'UK' => 'United Kingdom']);
  • Radio Button: $form->field($model, 'gender')->radioList(['M' => 'Male', 'F' => 'Female']);
  • File Upload: $form->field($model, 'file')->fileInput();
  • Date Picker (using jQuery UI):
echo $form->field($model, 'dob')->widget(\yii\jui\DatePicker::class, ['dateFormat' => 'yyyy-MM-dd']);

3. Validation Rules in Yii2

3.1 Required Rule with Conditional Validation

[['q2'], 'required', 'when' => function ($model) {
    return $model->q1 == '1';
}]

3.2 Unique Validation

['state_code', 'unique', 'when' => function ($model, $attribute) {
    return $this->state_model->$attribute != $model->$attribute;
}, 'targetClass' => MasterState::className(),
    'message' => 'This District Code has already been added']

3.3 Agree Checkbox with RequiredValue

['agree', 'required', 'requiredValue' => 1, 'message' => 'You must agree to the terms.']

3.4 Predefined Validators

  1. Required: [['name'], 'required']
  2. String Length: [['subject'], 'string', 'min' => 3, 'max' => 255]
  3. Email: [['email'], 'email']
  4. Number: [['age'], 'number']
  5. Boolean: [['is_active'], 'boolean']
  6. Compare: [['password_repeat'], 'compare', 'compareAttribute' => 'password']
  7. Date: [['dob'], 'date', 'format' => 'yyyy-MM-dd']
  8. In Range: [['status'], 'in', 'range' => [0, 1]]
  9. Trim: [['name'], 'trim']

4. Adding Errors Dynamically

You can add errors dynamically using addError():

if ($this->age < 18) {
    $this->addError('age', 'You must be at least 18 years old.');
}

5. Custom Validation Rule

5.1 Creating a Custom Validator

namespace app\models;

use yii\validators\Validator;

class MyCustomValidator extends Validator
{
    public function validateAttribute($model, $attribute)
    {
        if ($model->$attribute !== 'expected_value') {
            $this->addError($model, $attribute, 'Invalid value for {attribute}.');
        }
    }
}

5.2 Using the Custom Validator

['custom_field', MyCustomValidator::class]

6. AJAX Validation in Yii2

Enable AJAX validation by setting 'enableAjaxValidation' => true:

$form = ActiveForm::begin([
    'enableAjaxValidation' => true,
]);

Using Regular Expressions in Yii2 Validation Rules

Yii2 provides the match validator, which allows you to validate an attribute using a regular expression. This is useful for enforcing specific formats, such as email-like usernames, strong passwords, or specific text patterns.

Basic Usage of match

The match rule requires a pattern attribute where you define your regular expression.

public function rules()
{
    return [
        [['username'], 'match', 'pattern' => '/^[a-zA-Z0-9_-]{3,16}$/', 'message' => 'Username can only contain letters, numbers, underscores, and dashes (3-16 characters).'],
    ];
}

This rule ensures that the username only contains letters, numbers, underscores, or dashes and is between 3 to 16 characters long.

Validating a Strong Password

You can enforce strong password rules using regex.

public function rules()
{
    return [
        [['password'], 'match', 'pattern' => '/^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/', 
         'message' => 'Password must be at least 8 characters long, contain one uppercase letter, one number, and one special character.'],
    ];
}

Ensuring a Field Contains Only Numbers

If a field should only contain digits, you can use:

public function rules()
{
    return [
        [['mobile_number'], 'match', 'pattern' => '/^\d{10}$/', 'message' => 'Mobile number must be exactly 10 digits.'],
    ];
}

Validating Email-like Usernames

Sometimes, you may allow email-like usernames but don’t want to use the built-in email validator:

public function rules()
{
    return [
        [['email_username'], 'match', 'pattern' => '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', 'message' => 'Invalid email format.'],
    ];
}

Custom Error Messages for Regex Validation

If the built-in error message is not enough, you can customize it further:

public function rules()
{
    return [
        [['postal_code'], 'match', 'pattern' => '/^\d{5}(-\d{4})?$/', 'message' => 'Postal code must be in the format 12345 or 12345-6789.'],
    ];
}

Regular Expression Summary

  • Use match validator to enforce specific formats.
  • Define a pattern using a regular expression.
  • Provide a meaningful message for user-friendly error feedback.
  • Use regex for enforcing strong passwords, numeric-only fields, email-like structures, and custom formats.

Creating a Custom Word Limit Validator

Let's modify the Word500Validator to be dynamic, allowing us to set a custom word limit.

Step 1: Create the Custom Validator Class

Create a new file in common/validators/WordLimitValidator.php:

<?php

namespace common\validators;

use yii\validators\Validator;

/**
 * Class WordLimitValidator
 * A dynamic validator to limit the number of words in a text attribute.
 */
class WordLimitValidator extends Validator
{
    public $maxWords = 500; // Default limit

    public function validateAttribute($model, $attribute)
    {
        if (!empty($model->$attribute)) {
            $wordCount = str_word_count($model->$attribute);
            if ($wordCount > $this->maxWords) {
                $this->addError($model, $attribute, 
                    "{$model->getAttributeLabel($attribute)} cannot exceed {$this->maxWords} words. Currently: {$wordCount} words.");
            }
        }
    }
}

Step 2: Use the Custom Validator in a Model

Now, apply this validator in your model’s rules() method:

public function rules()
{
    return [
        [['description'], 'required'],
        [['description'], \common\validators\WordLimitValidator::className(), 'maxWords' => 300], // Set custom limit
    ];
}

Step 3: Using it with ActiveForm

<?= $form->field($model, 'description')->textarea(['rows' => 6]) ?>

Benefits of a Dynamic Custom Validator

  • Reusability: Can be used across multiple models.
  • Flexibility: Set different word limits for different attributes.
  • Custom Error Messages: Provides precise feedback to users.

Regular Expression-Based Validators in Yii2

Yii2 allows you to create validation rules using regular expressions (match rule) or by implementing custom validators. Below are some commonly used validation rules for various Indian identifiers using regex patterns.

1. Aadhar Number Validator

A valid Aadhar number must be a 12-digit numeric value starting with a digit from 2-9.

[['aadhar_number'], 'match', 'pattern' => '/^[2-9]{1}[0-9]{3}[0-9]{4}[0-9]{4}$/', 'message' => 'Invalid Aadhar Number.']

2. Bank Account Number Validator

A bank account number should be between 9 and 18 digits.

[['bank_account_number'], 'match', 'pattern' => '/^[0-9]{9,18}+$/', 'message' => 'Invalid Bank Account Number.']

3. Bank IFSC Code Validator

An IFSC code consists of 4 uppercase letters, followed by 0, and then 6 alphanumeric characters.

[['ifsc_code'], 'match', 'pattern' => '/^[A-Z]{4}0[A-Z0-9]{6}$/', 'message' => 'Invalid IFSC Code.']

4. PAN Card Number Validator

A valid PAN card number follows the pattern: 5 letters, 4 digits, 1 letter.

[['pan_number'], 'match', 'pattern' => '/^([a-zA-Z]){5}([0-9]){4}([a-zA-Z]){1}?$/', 'message' => 'Invalid PAN Number.']

5. Pin Code Validator

A PIN code in India consists of exactly 6 digits.

[['pin_code'], 'match', 'pattern' => '/^[0-9]{6}+$/', 'message' => 'Invalid PIN Code.']

6. GSTIN Validator

A GSTIN (Goods and Services Tax Identification Number) in India follows a structured 15-character pattern.

[['gstin'], 'match', 'pattern' => '/^([0][1-9]|[1-2][0-9]|[3][0-5])([a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9a-zA-Z]{1}[zZ]{1}[0-9a-zA-Z]{1})+$/', 'message' => 'Invalid GSTIN.']

7. Mobile Number Validator

A valid mobile number should be 10 digits and should not start with 0.

[['mobile_number'], 'match', 'pattern' => '/^[123456789]\d{9}$/', 'message' => 'Invalid Mobile / Phone Number.']

Alternatively, using a custom validator:

public function validateMobile($attribute, $params)
{
    if (!preg_match('/^[123456789]\d{9}$/', $this->$attribute)) {
        $this->addError($attribute, 'Invalid Mobile / Phone Number.');
    }
}

Add this rule in the model:

[['mobile_number'], 'validateMobile']

How to Use These Rules in a Yii2 Model

To apply these validators in your model:

public function rules()
{
    return [
        [['aadhar_number'], 'match', 'pattern' => '/^[2-9]{1}[0-9]{3}[0-9]{4}[0-9]{4}$/', 'message' => 'Invalid Aadhar Number.'],
        [['bank_account_number'], 'match', 'pattern' => '/^[0-9]{9,18}+$/', 'message' => 'Invalid Bank Account Number.'],
        [['ifsc_code'], 'match', 'pattern' => '/^[A-Z]{4}0[A-Z0-9]{6}$/', 'message' => 'Invalid IFSC Code.'],
        [['pan_number'], 'match', 'pattern' => '/^([a-zA-Z]){5}([0-9]){4}([a-zA-Z]){1}?$/', 'message' => 'Invalid PAN Number.'],
        [['pin_code'], 'match', 'pattern' => '/^[0-9]{6}+$/', 'message' => 'Invalid PIN Code.'],
        [['gstin'], 'match', 'pattern' => '/^([0][1-9]|[1-2][0-9]|[3][0-5])([a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9a-zA-Z]{1}[zZ]{1}[0-9a-zA-Z]{1})+$/', 'message' => 'Invalid GSTIN.'],
        [['mobile_number'], 'match', 'pattern' => '/^[123456789]\d{9}$/', 'message' => 'Invalid Mobile / Phone Number.'],
    ];
}

Conclusion

This guide covers everything related to Yii2 forms and validation, including input widgets, rules, and custom validators. Now, you can build robust forms with Yii2!