How to Refresh Yii2 Captcha: Button & Auto Reload Implementation

Advertisement

Refreshing Yii2 captcha solves the limitations of the default captcha widget, which lacks a refresh button and shows the same image on page reload. This technical guide explains how to implement a refreshable captcha by creating a custom CaptchaRefreshable class and adding auto-refresh logic, enhancing both user experience (UX) and form security.

Advertisement

Why Add a Refresh Feature to Yii2 Captcha?

The default Yii2 captcha widget has two main limitations. First, it does not provide a button or link to refresh the captcha image. Second, the same image persists even when the page is reloaded. This can be frustrating when the image is difficult to read. Therefore, implementing a refresh Yii2 captcha feature is crucial for a smoother verification process.

Step 1: Create the CaptchaRefreshable Class

Create a new class file named CaptchaRefreshable.php. Place it in the common/models/ directory for the Advanced template, or models/ for the Basic template. This class will extend the functionality of yii\captcha\Captcha.

Advertisement
<?php
namespace common\models;

class CaptchaRefreshable extends \yii\captcha\Captcha
{
    /**
     * Overrides the default HTML template to add a refresh link.
     */
    public function init()
    {
        $refreshLink = \yii\helpers\Html::a('New Image', '#', [
            'id' => 'refresh-captcha',
            'class' => 'text-small'
        ]);

        $this->template = '
            <div id="verify-code" class="row">
                <div class="col-md-6">{image} ' . $refreshLink . '</div>
                <div class="col-md-6">{input}</div>
            </div>';
        parent::init();
    }

    /**
     * Registers the JavaScript script to handle the refresh link click action.
     */
    public function registerClientScript()
    {
        $view = $this->getView();
        $view->registerJs("
            $('#refresh-captcha').on('click', function(e){
                e.preventDefault();
                $('#verify-code img').yiiCaptcha('refresh');
            });
        ");
        parent::registerClientScript();
    }
}

Step 2: Create the CaptchaRefreshableAction Class

Next, create a custom action class to handle captcha validation. This class ensures validation is not checked on AJAX requests, preventing the captcha code from expiring when the refresh button is clicked.

Advertisement
<?php
namespace common\models;

use Yii;

class CaptchaRefreshableAction extends \yii\captcha\CaptchaAction
{
    public function validate($input, $caseSensitive)
    {
        // Skip validation if the request is from AJAX.
        if (Yii::$app->request->isAjax) {
            return true;
        }
        return parent::validate($input, $caseSensitive);
    }
}

Step 3: Integrate the Widget into the Login View

In the login view file (views/site/login.php), replace the standard captcha widget with the custom CaptchaRefreshable widget.

<?php
use yii\widgets\ActiveForm;
// ... other code ...

<div class="site-login">
    <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

        <?= $form->field($model, 'username') ?>
        <?= $form->field($model, 'password')->passwordInput() ?>

        <?= $form->field($model, 'verifyCode')->widget(\common\models\CaptchaRefreshable::class) ?>

        <?= $form->field($model, 'rememberMe')->checkbox() ?>

        <div class="form-group">
            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>
        </div>

    <?php ActiveForm::end(); ?>
</div>

Step 4: Register the Action in SiteController

Register the CaptchaRefreshableAction in the actions() method of SiteController. This replaces the default captcha configuration.

<?php
namespace app\controllers;

class SiteController extends Controller
{
    // ... other code ...

    public function actions()
    {
        return [
            'error' => [ // ... ],
            'captcha' => [
                'class' => 'common\models\CaptchaRefreshableAction', // Custom action
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
        ];
    }
}

Step 5: Auto Refresh Captcha on Page Reload

The above implementation adds a refresh button. However, the captcha image does not change when the page is reloaded. To solve this, call the action to generate a new code at the beginning of the login action in the controller. This ensures Yii2 captcha refresh also happens automatically.

<?php
public function actionLogin()
{
    // Generate a new captcha code every time the login action is accessed.
    $this->createAction('captcha')->getVerifyCode(true);

    // ... rest of the login process code ...
}

By following these five steps, you have successfully implemented a complete refresh Yii2 captcha feature. This solution provides two main improvements: a refresh button for users and automatic renewal on page reload. This implementation also maintains compatibility with the Yii2 framework’s built-in validation and security.

Latest Articles