I'm trying to add captcha to my login page on my yii2 application.
I have tried some tutorial, the problem is captcha is always correct like no validation.
I have tried this:
https://www.yiiframework.com/doc/api/2.0/yii-captcha-captcha
https://mumunotesss.blogspot.com/2015/06/implementation-captcha-on-yii2.html
http://yii2-user.readthedocs.io/en/latest/howto/adding-captcha.html
How to add captcha in Yii-2 application?
My code is:
LoginForm
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
public $captcha; // add this varible to your model class.
/**
* #return array the validation rules.
*/
public function rules() {
return [
// username and password are both required
[['username', 'password','captcha'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
['captcha', 'captcha','captchaAction'=>'/site/captcha'], // add this code to your rules.
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
SiteController
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
view login.php
<div class="form-goup">
<?= $form->field($model, 'captcha')->widget(yii\captcha\Captcha::className(), '
['template' => '<div class="row"><div class="col-lg-3" style="margin-right:25px;">{image}</div><div class="col-lg-6">{input}</div></div>',
]); ?>
</div>
Your code works but you have syntax errors in it, a letter (this is only cosmetic) and a surplus ' which is completely wrong:
<div class="form-group">
<?= $form->field($model, 'captcha')->widget(yii\captcha\Captcha::className(), ['template' => '<div class="row"><div class="col-lg-3" style="margin-right:25px;">{image}</div><div class="col-lg-6">{input}</div></div>',]); ?>
</div>
This should be in view before submit button <?php ActiveForm::end(); ?>
I know the topic is old but it might be the issue with your url rules. Have you tried adding:
'captcha'=>site/captcha'
in your config file, url section
Related
Hi everyone i'm having trouble with my software developed with yii2.
I Have a model called Anagrafica and with its primary key id. With this model everything works.
I also have a model called AnagraficaOpzioniCarriera which extend the first one.
I have a view anagrafica/index that show a Kartik grid with the data of people enrolled that you can find in anagrafica. Admin user can update the data of an Anagrafica model by clicking on an the attribute "cognome" that render to anagrafica/update.
this is the command that call the controller AnagraficaController to reach anagrafica/update
'cognome'=>Grid::Labels('cognome',['anagrafica/update'],\app\helpers\Permits::allow('anagrafica','update'),'id','btn','info','10%'),
This is AnagraficaController
public function actionUpdate($id,$error=0,$message='')
{
$id = (int)$id;
$model = Anagrafica::findOne(['id' => $id]);
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post())) {
if($model->validate()){
}
if($model->save(false)){
return $this->redirect(['anagrafica/update','id'=>$model->id]);
}
}
}
return $this->render('update', ['model' => $model, 'extended'=>true]);
}
i removed some portions of code to semplify it, but this is the core.
One time the view anagrafica/update is reached in this page i have an ActiveForm to modify data of the model and i have a render to a grid that show the attributes contained in AnagraficaOpzioniCarriera about the $model that i'm updating.
<?= $this->render('_opzioni_carriera',['parent'=>$model]); ?>
anagrafica/_opzioni_carriera view contain a Kartik grid that shows the column in the model AnagraficaOpzioniCarriera
<?php
use kartik\grid\GridView;
use kartik\select2\Select2;
use kartik\widgets\ActiveForm;
use kartik\editable\Editable;
use kartik\widgets\SwitchInput;
use yii\helpers\ArrayHelper;
use app\helpers\Autoconfigurazione;
use app\models\AnagraficaOpzioniCarriera;
use app\helpers\Grid;
use yii\helpers\Html;
use app\helpers\UserInfo;
/* #var $this yii\web\View */
/* #var $model app\models\AnagraficaOpzioniCarriera*/
$model = new AnagraficaOpzioniCarriera(['scenario'=>'search']);
?>
<div class="">
<?php
echo GridView::widget([
'options'=>[
'id'=>'opzioni_carriera',
],
'dataProvider'=> $model->search($parent->id,Yii::$app->request->queryParams),
'showPageSummary'=>false,
'headerRowOptions'=>['class'=>'kartik-sheet-style'],
'pjax'=>true, // pjax is set to always true for this demo
'pjaxSettings'=>[
'neverTimeout'=>true,
],
'toolbar'=> [
[
'content'=>''
],
],
'panel'=>[
'heading'=>false,
'footer'=>false,
'after'=>false,
],
'columns' => Grid::gridColumns([
'model'=>$model,
'checkbox'=>false,
'remove'=>Grid::gridRemove($model),
'extraOptions' =>[
'cashback' =>Grid::YNColumn('cashback',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'compensa'=>Grid::YNColumn('compensa',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'associazione'=>Grid::YNColumn('associazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'formazione'=>Grid::YNColumn('formazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
],
]);
?>
</div>
cashback, compensa etc.. are the attributes in the model AnagraficaOpzioniCarriera.
Here when i try to update this attributes everything looks fine, the function model->validate() and model->load returns true value, but at the end of the process doesn't works.
Honestly i don't know what i have to return from the function of the controller.
public function actionUpdate($id)
{
$model = AnagraficaOpzioniCarriera::findOne(['id_anagrafica' => $id]);
if (!$model) {
// Se l'anagrafica opzioni carriera non esiste, genera un'eccezione 404
throw new \yii\web\NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
}
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if(Yii::$app->request->post('cashback') != null) $model->cashback = Yii::$app->request->post('cashback');
if(Yii::$app->request->post('compensa') != null) $model->cashback = Yii::$app->request->post('compensa');
if(Yii::$app->request->post('associazione') != null) $model->cashback = Yii::$app->request->post('associazione');
if(Yii::$app->request->post('formazione') != null) $model->cashback = Yii::$app->request->post('formazione');
if ($model->save()) {
return Json::encode(["success" => true, 'message' => 'Dati aggiornati']);
}
}
// Mostra il form di modifica
return $this->render('_opzioni_carriera', [
'parent' => $model,
]);
}
anyone can help me? i hope i explained my problem in a good form, but my english is not the best, i know. Anyway thanks in aadvance to everyone who want to try to help me, if you need anything other you can easily ask.
I tried every everything, also a logger but nothing worked
Like someone suggest these are the rules of the model AnagraficaOpzioni, but like i said prevously model->validate() works, for this reason i think the problem is not over there
public function rules()
{
return [
[['id_anagrafica'], 'required'],
[['id_anagrafica'], 'integer'],
[['cashback', 'compensa', 'associazione', 'formazione'], 'required', 'on'=>['update']],
[['cashback', 'compensa', 'associazione', 'formazione'], 'integer'],
[['id_anagrafica', 'cashback', 'compensa', 'associazione', 'formazione',], 'safe', 'on'=>['search']],
];
}
When I click on the Signup button I receive the following error: Array to string conversion.
I think that the error occurs when I call fileInput() method but i don't know how to solve it.
This is the partial code of the view
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'email') ?>
<?= $form->field($modelUpload, 'imageFile')->fileInput() ?>
<div class="form-group">
<?= Html::submitButton('Signup', ['class' => 'btn', 'name' => 'signup-button']) ?>
</div>
<?php ActiveForm::end(); ?>
While this is the code for the controller:
<?php
class SiteController extends Controller {
/**
* Signs user up.
*
* #return mixed
*/
public function actionSignup() {
$model = new SignupForm();
$modelUpload = new UploadForm();
if ($model->load(Yii::$app->request->post()) && $modelUpload->load(Yii::$app->request->post())) {
$modelUpload->imageFile = UploadedFile::getInstances($modelUpload, 'imageFile');
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user) && $modelUpload->upload()) {
return $this->goHome();
}
}
}
return $this->render('signup', [
'model' => $model,
'modelUpload' => $modelUpload,
]);
}
}
This is the code of the model. It's the same of the official documentation.
<?php
class UploadForm extends Model {
/**
* #var UploadedFile
*/
public $imageFile;
public function rules() {
return [
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload() {
if ($this->validate()) {
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);
return true;
} else {
return false;
}
}
}
?>
Errors:
Instant Solution
Change your line inside the actionSignup() from below
UploadedFile::getInstances($modelUpload, 'imageFile');
to
UploadedFile::getInstance($modelUpload, 'imageFile');
Reason
It's only a single file you are uploading not multiple files so getInstances() should be getInstance()
About
getInstance : Returns an uploaded file for the given model
attribute. The file should be uploaded using
[[\yii\widgets\ActiveField::fileInput()]]
getInstances: Returns all uploaded files for the given model
attribute.
if you want to upload multiple files - having'maxFiles' > 1 in your model's FileValidator rules - change your attribute name from:
<?= $form->field($modelUpload, 'imageFile')->fileInput() ?>
to
<?= $form->field($modelUpload, 'imageFile[]')->fileInput() ?>
read this:
https://www.yiiframework.com/doc/guide/2.0/en/input-file-upload#uploading-multiple-files
I'm reviewing a basic contact form which is not associated with any model. I would like some advice on the best way to leverage Cake's automatic view rendering of field errors for this situation.
Controller
Performs validation through a custom Validator.
public function index()
{
if ($this->request->is('post')) {
// Validate the form
$validator = new EnquiryValidator();
$data = $this->request->data();
$errors = $validator->errors($data);
if (empty($errors)) {
// Send email, etc.
// ...
// Refresh page on success
}
// Show error
$this->Flash->error('Unable to send email');
}
}
View
<?= $this->Form->create(); ?>
<?= $this->Form->input('name', [
'autofocus' => 'autofocus',
'placeholder' => 'Your name',
'required'
]);
?>
<?= $this->Form->input('email', [
'placeholder' => 'Your email address',
'required'
]);
?>
<?= $this->Form->input('subject', [
'placeholder' => 'What would you like to discuss?',
'required'
]);
?>
<?= $this->Form->input('message', [
'label' => 'Query',
'placeholder' => 'How can we help?',
'cols' => '30',
'rows' => '10',
'required'
]);
?>
<div class="text-right">
<?= $this->Form->button('Send'); ?>
</div>
<?= $this->Form->end(); ?>
Currently the form will not show any errors next to the input fields. I assume it's because there is no entity associated with the form or something like that, but I'm not sure.
What is the best solution? Can the validation be performed in a better way to automatically provide field errors in the view?
Modelless forms
Use a modelless form. It can be used to validate data and perform actions, similar to tables and entities, and the form helper supports it just like entities, ie, you simply pass the modelless form instance to the FormHelper::create() call.
Here's the example from the docs, modified a little to match your case:
src/Form/EnquiryForm.php
namespace App\Form;
use App\...\EnquiryValidator;
use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;
class EnquiryForm extends Form
{
protected function _buildSchema(Schema $schema)
{
return $schema
->addField('name', 'string')
->addField('email', ['type' => 'string'])
->addField('subject', ['type' => 'string'])
->addField('message', ['type' => 'text']);
}
protected function _buildValidator(Validator $validator)
{
return new EnquiryValidator();
}
protected function _execute(array $data)
{
// Send email, etc.
return true;
}
}
in your controller
use App\Form\EnquiryForm;
// ...
public function index()
{
$enquiry = new EnquiryForm();
if ($this->request->is('post')) {
if ($enquiry->execute($this->request->data)) {
$this->Flash->success('Everything is fine.');
// ...
} else {
$this->Flash->error('Unable to send email.');
}
}
$this->set('enquiry', $enquiry);
}
in your view template
<?= $this->Form->create($enquiry); ?>
See also
Cookbook > Modelless Forms
LoginForm:
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// username should be a number and of 8 digits
[['username'], 'number', 'message'=>'{attribute} must be a number'],
[['username'], 'string', 'length' => 8],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* 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 have set up 2 rules for the same field as you can see above:
[['username'], 'number', 'message'=>'{attribute} must be a number'],
[['username'], 'string', 'length' => 8],
I would like the form to display different error messages for the following 3 scenarios situations:
The provided value is neither a number, nor 8 characters (digits).
The provided value is a number, but is not of 8 characters (digits).
The provided value is not a number, but is of 8 characters (digits).
My question is 2 fold:
A. Is there a way to combine these rules in any standard, Yii2 way.
B. In my previous question I have tried to set up a custom validator (the obvious way to solve this), but it was very simply ignored. The only way I could make it validate was if I added the username field to a scenario. However, once I added password too, it was again ignored. Any reason's for this that you can think of? EDIT: skipOnError = false changed nothing at all in this behaviour.
So please, when you answer, make sure you test it preferably in yii2/advanced; I barely touched the default set up, so it should be easy to test.
EDIT: for clarity, I would like to only allow numbers that are of 8 characters (digits), so they can potentially have a leading 0, eg. 00000001, or 00000000 for that matter. This is why it has to be a numeric string.
The best way to combine rules and display custom error messages for different situations is to create a custom validator. Now if you want that to work on client-side too (it was one of my problems detailed in question B above, thanks to #Beowulfenator for the lead on this), you have to create an actual custom validator class extended from the yii2 native validator class.
Here is an example:
CustomValidator.php
<?php
namespace app\components\validators;
use Yii;
use yii\validators\Validator;
class CustomValidator extends Validator
{
public function init() {
parent::init();
}
public function validateAttribute($model, $attribute) {
$model->addError($attribute, $attribute.' message');
}
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
messages.push('$attribute message');
JS;
}
}
LoginForm.php
<?php
namespace common\models;
use Yii;
use yii\base\Model;
use app\components\validators\CustomValidator;
/**
* Login form
*/
class LoginForm extends Model
{
public $username;
public $password;
public $custom;
private $_user;
/**
* #inheritdoc
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// username should be a number and of 8 digits
[['username'], 'number', 'message'=>'{attribute} must be a number'],
[['username'], 'string', 'length' => 8],
// password is validated by validatePassword()
['password', 'validatePassword'],
['custom', CustomValidator::className()],
];
}
// ...
login.php
<?php
/* #var $this yii\web\View */
/* #var $form yii\bootstrap\ActiveForm */
/* #var $model \common\models\LoginForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
$this->title = 'Login';
?>
<div class="site-login text-center">
<h1><?php echo Yii::$app->name; ?></h1>
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'fieldConfig' => ['template' => "{label}\n{input}"],
'enableClientValidation' => true,
'validateOnSubmit' => true,
]); ?>
<?= $form->errorSummary($model, ['header'=>'']) ?>
<div class="row">
<div class="col-lg-4 col-lg-offset-4">
<div class="col-lg-10 col-lg-offset-1">
<div style="margin-top:40px">
<?= $form->field($model, 'username') ?>
</div>
<div>
<?= $form->field($model, 'password')->passwordInput() ?>
</div>
<div>
<?= $form->field($model, 'custom') ?>
</div>
<div class="form-group" style="margin-top:40px">
<?= Html::submitButton('Login', ['class' => 'btn btn-default', 'name' => 'login-button']) ?>
</div>
</div>
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
Finally you need this :
the value is required
the value must be a string of 8 chars
the value must contains only digits
So you should simply try :
['username', 'required'],
['username', 'string', 'min' => 8, 'max' => 8],
['username', 'match', 'pattern' => '/^[0-9]{8}$/', 'message'=>'{attribute} must be a number'],
Yii2 ignores your validation rules may because you duplicated not only attribue but also types.
With number validation, i think you should use min/max option to validate number length.
For this case:
'min'=>10000000,'max'=>99999999
I would like to create a yii2 model without a database. Instead, the data is generated dynamically and not stored, just displayed to the user as a json. Basically, I would just like a get a simple, basic example of a non-database Model working but I can't find any documentation on it.
So how would I write a model without a database? I have extended \yii\base\Model but I get the following error message:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<name>PHP Fatal Error</name>
<message>Call to undefined method my\app\models\Test::find()</message>
<code>1</code>
<type>yii\base\ErrorException</type>
<file>/my/app/vendor/yiisoft/yii2/rest/IndexAction.php</file>
<line>61</line>
<stack-trace>
<item>#0 [internal function]: yii\base\ErrorHandler->handleFatalError()</item>
<item>#1 {main}</item>
</stack-trace>
</response>
To implement find(), I must return a database query object.
My Model is completely blank, I'm just looking for a simple example to understand the principal.
<?php
namespace my\app\models;
class Test extends \yii\base\Model{
}
This is a Model from one of my projects. This Model is not connected with any database.
<?php
/**
* Created by PhpStorm.
* User: Abhimanyu
* Date: 18-02-2015
* Time: 22:07
*/
namespace backend\models;
use yii\base\Model;
class BasicSettingForm extends Model
{
public $appName;
public $appBackendTheme;
public $appFrontendTheme;
public $cacheClass;
public $appTour;
public function rules()
{
return [
// Application Name
['appName', 'required'],
['appName', 'string', 'max' => 150],
// Application Backend Theme
['appBackendTheme', 'required'],
// Application Frontend Theme
['appFrontendTheme', 'required'],
// Cache Class
['cacheClass', 'required'],
['cacheClass', 'string', 'max' => 128],
// Application Tour
['appTour', 'boolean']
];
}
public function attributeLabels()
{
return [
'appName' => 'Application Name',
'appFrontendTheme' => 'Frontend Theme',
'appBackendTheme' => 'Backend Theme',
'cacheClass' => 'Cache Class',
'appTour' => 'Show introduction tour for new users'
];
}
}
Use this Model like any other.
e.g. view.php:
<?php
/**
* Created by PhpStorm.
* User: Abhimanyu
* Date: 18-02-2015
* Time: 16:47
*/
use abhimanyu\installer\helpers\enums\Configuration as Enum;
use yii\caching\DbCache;
use yii\caching\FileCache;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** #var $this \yii\web\View */
/** #var $model \backend\models\BasicSettingForm */
/** #var $themes */
$this->title = 'Basic Settings - ' . Yii::$app->name;
?>
<div class="panel panel-default">
<div class="panel-heading">Basic Settings</div>
<div class="panel-body">
<?= $this->render('/alert') ?>
<?php $form = ActiveForm::begin([
'id' => 'basic-setting-form',
'enableAjaxValidation' => FALSE,
]); ?>
<h4>Application Settings</h4>
<div class="form-group">
<?= $form->field($model, 'appName')->textInput([
'value' => Yii::$app->config->get(
Enum::APP_NAME, 'Starter Kit'),
'autofocus' => TRUE,
'autocomplete' => 'off'
])
?>
</div>
<hr/>
<h4>Theme Settings</h4>
<div class="form-group">
<?= $form->field($model, 'appBackendTheme')->dropDownList($themes, [
'class' => 'form-control',
'options' => [
Yii::$app->config->get(Enum::APP_BACKEND_THEME, 'yeti') => ['selected ' => TRUE]
]
]) ?>
</div>
<div class="form-group">
<?= $form->field($model, 'appFrontendTheme')->dropDownList($themes, [
'class' => 'form-control',
'options' => [
Yii::$app->config->get(Enum::APP_FRONTEND_THEME, 'readable') => ['selected ' => TRUE]
]
]) ?>
</div>
<hr/>
<h4>Cache Setting</h4>
<div class="form-group">
<?= $form->field($model, 'cacheClass')->dropDownList(
[
FileCache::className() => 'File Cache',
DbCache::className() => 'Db Cache'
],
[
'class' => 'form-control',
'options' => [
Yii::$app->config->get(Enum::CACHE_CLASS, FileCache::className()) => ['selected ' => TRUE]
]
]) ?>
</div>
<hr/>
<h4>Introduction Tour</h4>
<div class="form-group">
<div class="checkbox">
<?= $form->field($model, 'appTour')->checkbox() ?>
</div>
</div>
<?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
<?php $form::end(); ?>
</div>
The reason for using a model is to perform some kind of logic on data that you are getting from somewhere. A model can be used to perform data validation, return properties of the model and their labels, and allows for massive assignment. If you don't need these features for your data model, then don't use a model!
If you are not requiring data validation (i.e. you are not changing any data via forms or other external source), and you are not requiring access to behaviors or events, then you probably need to just use yii\base\Object. This will give you access to getters and setters for properties of the object, which seems to be all you need.
So your class ends up looking like this. I've included getting data from another model, in case that's what you want to do;
<?php
namespace my\app\models;
use \path\to\some\other\model\to\use\OtherModels;
class Test extends \yii\base\Object{
public function getProperty1(){
return "Whatever you want property1 to be";
}
public function getProperty2(){
return "Whatever you want property2 to be";
}
public function getOtherModels(){
return OtherModels::findAll();
}
}
You would then simply use it like this;
$test = new Test;
echo $test->property1;
foreach ($test->otherModels as $otherModel){
\\Do something
}
The function you have tried to use, find(),is only relevant to a database and so won't be available to your class if you've extended yii\base\Model, yii\base\Component or yii\base\Object, unless you want to define such a function yourself.
A lightweight way to create models without database backend is to use a DynamicModel:
DynamicModel is a model class primarily used to support ad hoc data validation.
Just write in your controller:
$model = new DynamicModel(compact('name', 'email'));
$model->addRule(['name', 'email'], 'string', ['max' => 128])
->addRule('email', 'email')
->validate();
and then pass $model to your view.
A full example can be found in http://www.yiiframework.com/wiki/759/create-form-with-dynamicmodel/.
This is perfect for user input for calling APIs, creating forms on the fly, etc.
As has been pointed out in the comments and other answers, your model needs to extend \yii\db\BaseActiveRecord. That said you can store your json as a nosql database such as MongoDb or in a key-value cache such as Redis instead. Both have Yii implementions: \yii\mongodb\ActiveRecord and \yii\redis\ActiveRecord