Captcha in a CForm?

Building a website using Yii1.1 and trying to use the Form Builder for all the forms. I can’t however seem to find any example of how to use the built-in CCaptcha in a CForm. All examples relate to the 1.0-way using CHtml. Could somebody please explain how to do this?

Thanks!

Same here. Find a way to do this?

Just add to the CForm config:




$this->widget('CCaptcha', array('buttonLabel' = >'<br />Generate new image'))



Add to your model $verifyCode property and make it a textfield in the CForm config.

But CForm doesn’t have a widget method…

I don’t see how… $this->widget in a CForm results in an error: CException: CForm does not have a method named “widget”.

If I’m not mistaken $this->widget only works in views. Works for the yii 1.0 type view-based forms, but not the 1.1 type Form Builder based CForms.

Also tried as part of the elements array of the CForm:


'verifyCode'=>array(

          'type'=>'system.web.widgets.captcha.CCaptcha',

),

This results in the following error: Method CForm::__toString() must no throw an exception

I’ve added Captcha to the controller actions and $verifyCode to the model.

Sorry, I forgot that $this->widget renders an image instantly…

Maybe we should not use captcha in the config, but the only way I see how to do it is to use output buffering:




ob_start();

Yii::app()->controller->widget('CCaptcha');

$captcha = ob_get_contents();

ob_end_clean();



And then in the config: ‘elements’ => array(… , $captcha)

But I think the better way is to use CForm methods renderBegin, renderContent and renderEnd:




echo $form->renderBegin();

echo $form->renderContent();


$this->widget('CCaptcha');


echo $form->renderEnd();



Thanks andy_s for pointing us in the right direction. Here’s what I’ve made of it (and it works):

The CForm:


return array(

    'title'=>yii::t('core','Please provide your user credentials'),


    'elements'=>array(

        'username'=>array(

            'type'=>'text',

        ),

        'password'=>array(

            'type'=>'password',

        ),

        'passwordConfirmation'=>array(

            'type'=>'password',

        ),

        'email'=>array(

            'type'=>'text',

        ),

        'verifyCode'=>array(

            'type'=>'text',

        ),

    ),


    'buttons'=>array(

        'register'=>array(

            'type'=>'submit',

            'label'=>yii::t('core','Create'),

        ),

    ),

);

The view:


<div class="form">

<?php

echo $form->renderBegin();

foreach($form->getElements() as $element)

{

    if ($element->name==='verifyCode')

    {

        $this->widget('CCaptcha');

    }

    echo $element->render();

}

echo $form->renderButtons();

echo $form->renderEnd();

?>

</div>

Also figured out why working with a Captcha is different from other views: The CCaptcha widget only renders an image and JQuery object (generate new image). You even need to add the input field related to the Captcha ($verifyCode in this case) yourself. Guess this means it can’t be part of the form definition, because no rendering should take place inside the form class definition (according to MVC logic).

This still feels kind of developer-unfriendly. I would like to support a captcha widget that can be used straight in a CForm (something like ‘type’=>‘zii.widgets.captcha’) and takes care of the image, refresh-image button and input field (perhaps even fully client-sided?) so we can configure this completely through the CForm and just put <?php echo $form; ?> in the view.

Been doing some more tweaking to my previous solution. I couldn’t find a function in the API reference that allows me to render the field label and the actual field seperately. This means I can only do a $element->render(); which means the Captcha image is located above/before the label of the field it is related to.

Three solutions to this problem:

[list=1][]Turn the layout of the form into two columns using CSS, with the Captcha aligned with the input field. It is however visually still located higher than the related label.[]Place $this->widget(’[color="#008000"]CCaptcha[/color]’); after the $element->render(); of its input field. This seems unintuitive for the user though.[*]Go back to the old fashioned CHtml-based form functions for the Captcha part. This means removing the [color="#008000"]‘verifyCode’=>array(‘type’=>‘text’,),[/color] part of the CForm and changing the view into:


<div class="form">

<?php

echo $form->renderBegin();

echo $form->renderElements();

// Add Captcha

echo CHtml::activeLabel($model,'verifyCode');

echo '<div>'; $this->widget('CCaptcha'); echo '</div>';

echo CHtml::activeTextField($model,'verifyCode');

echo '<p class="hint">'.yii::t('core','Please enter the letters as they are shown in the image above.<br/>Letters are not case-sensitive.').'</p>';

echo $form->renderButtons();

echo $form->renderEnd();

?>

</div>

[/list]I guess I’ll use the third option for now. Still, combining CHtml labels and fields with CForm feels messy. I think this makes the addition of a completely CForm compatible/based widget to Yii even more important.

Thanks for sharing, toMeloos!

I like the third solution too, because separating captcha widget from it’s related label and text field is not very logical…

Also I wonder if there is any easier way to include captcha in CForm (e.g. a configurable CFormCaptchaElement).

Guys, I think I have a solution.

The only reason it returns "Method CForm::__toString() must no throw an exception"

when you put it directly into




'elements' => array('verifyCode' => array('type' => 'CCaptcha'))



is that CCaptcha doesn’t have ‘model’ and ‘attribute’ properties, which CForm forcibly tries to set.

My solution:




<?php

class MyCaptcha extends CCaptcha

{

	public $model;

	public $attribute;

}



Now we can use:




'elements' => array('verifyCode' => array('type' => 'MyCaptcha'))



and of course that additional field that will take the input.

Actually I decided to render input field inside of MyCaptcha widget:




<?php

class MyCaptcha extends CCaptcha

{

	public $model;

	public $attribute;

		

	public function run()

	{

		parent::run();

		echo CHtml::activeTextField($this->model, $this->attribute);

	}

}



The code works, but the captcha image isn’t visible. What can be the problem?

Edit: I’m on a page of wizard-behavior

Actually, his method works great. I had the same problem with the image not showing, and found this to be very helpful:

http://www.sterlingsavvy.com/ss/documentation/?doc=ccaptcha

follow this wiki post -

http://www.yiiframework.com/wiki/733/how-to-show-a-captcha-in-cform/