Is there any way I can just display validation errors in Yii2 without a redirect or refresh?
This is my default controller code performing the login action, I would like to highlight the text fields when the data entered does not match the data in the database, a simple validation against the database.
public function actionLogin()
{
/** #var \amnah\yii2\user\models\forms\LoginForm $model */
$model = $this->module->model("LoginForm");
// load post data and login
$post = Yii::$app->request->post();
if ($model->load($post) && $model->validate()) {
$returnUrl = $this->performLogin(
$model->getUser(), $model->rememberMe
);
return $this->redirect($returnUrl);
}
return $this->render('login', compact("model"));
}
You can do it this way:
form login
<?php $form = ActiveForm::begin([
'validationUrl' => Url::toRoute(['site/login-validate']),
'action' => Url::toRoute(['site/login']),
'options' => [
'class' => 'form login'
],
]); ?>
Site Controller
public function actionLoginValidate(){
if(Yii::$app->request->isAjax){
$model = new Login();
if($model->load(Yii::$app->request->post())){
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
}
throw new BadRequestHttpException("This action is not allowed.");
}
Related
I am trying to add captcha validation based on scenario, for which I am first retrieving number of failed attempts from database. For which I am using checkattempts() function. Based on the result I am displaying captcha in view and adding scenario condition in controller as below.
In LoginForm model:
public function rules()
{
return [
[['username', 'password'], 'required', 'on'=>'loginpage'],
[['username', 'password'], 'required', 'on'=>'withCaptcha'],
[['reference_url'], 'safe'],
[['verifyCode'], 'captcha', 'skipOnEmpty' => true,'on'=>'withCaptcha'],
['username','email', 'on'=>'loginpage', 'message'=> 'Please enter a valid email address'],
['password', 'validatePassword', 'on'=>'loginpage'],
['password', 'validatePassword', 'on'=>'withCaptcha'],
];
}
public function checkattempts($uname)
{
$user = \frontend\models\User::findByEmail($uname);
$ip = $this->get_client_ip();
if($user){
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $user->id])
->one();
if($data["attempts"] >=3){
return true;
}else{
return false;
}
}
return false;
}
in SiteController.php controller
public function actionLogin() {
if (!\Yii::$app->user->isGuest) {
return $this->redirect(Yii::$app->getUrlManager()->getBaseUrl() . '/../../');
}
$model = new \common\models\LoginForm();
$model->scenario = 'loginpage';
$captcha = false;
if(Yii::$app->request->post()){
$post_variables =Yii::$app->request->post('LoginForm');
if ($model->checkattempts($post_variables['username'])) {
$model->scenario = 'withCaptcha';
$captcha = true;
}
}
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->login(); print_r($model->getErrors()); exit;
} else {
return $this->render('login', [
'model' => $model, 'captcha' => $captcha,
]);
}
In my login.php view:
<?php if($captcha) { ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(),
['template' => '<div class="captcha_img">{image}</div>'
. '<a class="refreshcaptcha" href="#">'
. Html::img('/images/imageName.png',[]).'</a>'
. 'Verification Code{input}',
])->label(FALSE); ?>
<?php } ?>
In my controller when I am tring to print model errors at $model->login() function it is giving below error everytime even though the verification code is correct.
Array ( [verifycode] => Array ( [0] => The verification code is incorrect. ) )
Why is it failing every time. Is there any mistake in the code written?
Thanks in advance
i'm still beginner in yii and php.
my problem is:
i want add a value that from db to my textfield.
my db table 'config' have 3 column, id;name;value;
i have tried code like this:
<?= $form->field($model, 'name')->textInput(['value'=>$model->value])->label('name',['class'=>'label-class'])?>
but it didn't show the value.
i want a update form for change value . example: name: title; value: hello world.
Your controller should be something like that:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
protected function findModel($id)
{
if (($model = Mymodel::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
and in view you simply write
<?= $form->field($model, 'name')->textInput()->label('name',['class'=>'label-class'])?>
Your database values will be in your field.
For simple CRUD you may use GII http://www.yiiframework.com/doc-2.0/guide-start-gii.html
I have a registration form with fields that are validated in User entity class. The validation works fine, however I can't return JsonResponse with form error messages in it.
My registration form controller method looks like this:
/**
* #Route("/register", name="register")
*/
public function registerAction(Request $request)
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($request);
$errors = "";
if ($form->isSubmitted())
{
if ($form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setIsActive(1);
$user->setLastname('none');
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return new JsonResponse(
array(
'message' => 'Success! User registered!',
), 200);
}
else
{
$errors = ($this->get('validator')->validate($form));
return new JsonResponse(
array(
'message' => 'Not registered',
'errors' => $errors,
), 400);
}
}
return $this->render(
'ImmoBundle::Security/register.html.twig',
array('form' => $form->createView(), 'errors' => $errors)
);
}
I get the following json response when I submit the registration form with invalid data:
{"message":"Not registered","errors":{}}
Actually I'm expecting that "errors":{} will contain some error fields, but it doesn't. Does anyone know what the problem here is?
UPD:
My RegistrationType looks like this:
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', TextType::class)
->add('email', EmailType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat password'),
'invalid_message' => "Passwords don't match!",
))
->add('register', SubmitType::class, array('label' => 'Register'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'ImmoBundle\Entity\User',
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'authenticate',
));
}
}
UPD2: Found the solution. I needed to do this iteration and then call for getMessage():
$allErrors = ($this->get('validator')->validate($form));
foreach ($allErrors as $error)
{
$errors[] = $error->getMessage();
}
Form validated when you call $form->handleRequest($request);
To get form errors use getErrors method
$errors = $form->getErrors(true); // $errors will be Iterator
to convert errors object to messages array you can use code from this response - Handle form errors in controller and pass it to twig
This is exapmle how i'm process errors in one of my projects
$response = $this->get('http.response_formatter');
if (!$form->isValid()) {
$errors = $form->getErrors(true);
foreach ($errors as $error) {
$response->addError($error->getMessage(), Response::HTTP_BAD_REQUEST);
}
return $response->jsonResponse(Response::HTTP_BAD_REQUEST);
}
It's worked for me.
And also this can help you - Symfony2 : How to get form validation errors after binding the request to the form
You must set error_bubbling to true in your form type by explicitly setting the option for each and every field.
renderPartial clientSide validation doesn't work. I want to render part of form with ajax. Ex.:
_form.php
$form = ActiveForm::begin([
'options' => [
'enableAjaxValidation' => true,
]
]);
$form->field($model, 'category_id')->dropDownList($category, [
'onchange'=>'
$.get( "'.Url::toRoute('/controller/params').'", { id: $(this).val() } )
.done(function( data ) {
$( "#offers-param-content" ).html( data );
}
);'
]);
Controller.php
public function actionParams($id)
{
$model = new Param();
$params = EavAttribute::find()->where(['category_id'=>$id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params];
}
_params.php
foreach($params as $item){
echo Html::activeTextInput('text', $model, $item->name);
}
If you want to enable client validation, then set this property to true.
$form = ActiveForm::begin([
'options' => [
'enableAjaxValidation' => true,
'enableClientValidation'=>true
]
]);
And use renderAjax() function in place of renderPartial() it will inject into the rendering result with JS/CSS scripts and files which are registered with the view
In your Controller.php you need to set layout to false and die the execution
public function actionParams($id)
{
$this->layout = false;
$model = new Param();
$params = EavAttribute::find()->where(['category_id'=>$id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params];
die;
}
Your not returning any validation errors from your controller to your view.
To archive that use
yii\widgets\ActiveForm::validate($yourModel);
If your not using activeform you can return errors by
$model->getErrors();//will return errors from $model->validate()
From your excerpt try this
public function actionParams($id) {
$model = new Param();
if ($model->load(Yii::$app->request->post())) {
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return ActiveForm::validate($model); /* Validation error messages are returned by this static function */
}
$params = EavAttribute::find()->where(['category_id' => $id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params]);
}
}
I'm having an issue validation whether a submitted Email Address is Unique in the database.
When the User registers I need to validate whether the email address exists all of the other validation is working fine.
Is there a step missing when you are validating a using an Ajax form in Yii 2.
A User clicks on CTA to register on site/index
use yii\bootstrap\Modal;
use frontend\models\Register;
use yii\helpers\Html;
use yii\helpers\Url;
...
Modal::begin([
'id' => 'modal',
'size'=>'modal-lg',
'clientOptions' => ['backdrop' => 'static', 'keyboard' => FALSE],
]);
echo "<div id='modalContent'></div>";
Modal::end();
?>
<?= Html::button('Register', ['value' => Url::to(['register/create']), 'title' => 'Register', 'class' => 'btn btn-success','id'=>'modalButton']); ?>
This opens up a modal (register/create)
Model Register
class Register extends User
{
...
public function rules()
{
return [
['Email', 'filter', 'filter' => 'trim'],
['Email', 'required'],
['Email', 'email'],
['Email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],
];
}
public function signup()
{
$user = new User();
if ($this->validate()) {
$user->Email = $this->Email;
if ($user->save()) {
return $user;
}
} else {
return;
}
}
Register Controller
public function actionCreate()
{
$model = new Register(['scenario' => 'signup']);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
Yii::error($model);
return $model->validate();
}
if ($model->load(Yii::$app->request->post())) {
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user)) {
return $this->goHome();
}
}
}
return $this->renderAjax('create', [
'model' => $model,
]);
}
The View file
<?php $form = ActiveForm::begin(['id'=> 'register', 'enableClientValidation'=>true, 'enableAjaxValidation'=>true, 'validateOnChange'=> true, 'validationUrl' => Url::to(['register/create'])] ); ?>
<div class="form-group">
<?= $form->field($model, 'Email') ?>
</div>
Javascript file
$script = <<< JS
$('body').on('beforeSubmit', 'form#register', function (event, jqXHR, settings) {
var form = $(this);
// return false if form still have some validation errors
if (form.find('.has-error').length) {
return false;
}
// submit form
$.ajax({
url: form.attr('action'),
type: 'GET',
data: form.serialize(),
success: function (response) {
// do something with response
$(document).find('#modal').modal('hide');
}
});
return false;
});
JS;
$this->registerJs($script);
I was facing a similar issue where my email field was not triggering the "unique" rule in the javascript validation, but the "unique" rule was getting triggered on the form submit.
It's awesome that you came up with a solution for your question that gets the job done, but I think what I just learned could also shed some insight on this question for others.
The key for me was learning/realizing that the "unique" validation rule must use a database lookup to verify if the user input is unique, thus it must use ajax. Other validation rules, such as "required" or "email" don't need ajax to validate. They just use javascript in the client to validate, so they are client validation, whereas the "unique" validator is actually ajax validation.
Note: my code below is not really addressing your code directly, but is intended to communicate the overall understanding and code structure that is needed to answer your question. Some of these steps you already have, but some are missing :)
First, you need a model with a field that requires a 'unique' rule, such as an email address for a user account.
In your Model
public function rules()
{
$rules = [
[['email'], 'unique'],
// ... and all your other rules ...
];
}
When using \yii\widgets\ActiveForm, client validation is enabled by default, but ajax validation is not.
So, next you need to directly turn on ajax validation in the view. This can be done either for the entire form, or just for a single field. The Yii2 Validation docs explain this best, but in my case, I chose to just enable ajax validation on my email field.
In your View
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($user, 'email', ['enableAjaxValidation' => true])->textInput();
//... other form fields and such here ...
<?php ActiveForm::end(); ?>
Next, you also need to handle the ajax validation in your controller, so your controller method could look something like this:
public function actionRegister()
{
$user = new User();
$post = Yii::$app->request->post();
$userLoaded = $user->load($post);
// validate for ajax request
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($user);
}
// vaidate for normal request
if ($userLoaded && $user->validate()) {
$user->save();
return $this->redirect(['view', 'id' => $user->id]);
}
// render
return $this->render('create', ['user' => $user]);
}
And then here's the catch ... everything above is what you would need when working with a normal (non-ajax) form. In your question, you are working on a form in a modal window that is being submit via ajax, so the above controller method will not work. With ajax forms, it becomes pretty tricky to handle the ajax form validation and the ajax form submit in the same controller method.
As usual, Yii has this all figured out for us, and the validationUrl form parameter will save the day. All you have to do is create a new method in your controller that is specifically for ajax validation, and reference the controller/action URL in your form. Something like this should do the trick:
In your View
<?php $form = ActiveForm::begin([
'id' => 'form-id', // always good to set a form id, especially when working with ajax/pjax forms
'validationUrl' => ['user/validate-email'], //['controller/action'],
]); ?>
In your Controller
public function actionRegister()
{
$user = new User();
$post = Yii::$app->request->post();
// vaidate for normal request
if ($user->load($post) && $user->validate()) {
$user->save();
return $this->redirect(['view', 'id' => $user->id]);
}
// render
return $this->render('create', ['user' => $user]);
}
public function actionValidateEmail()
{
// validate for ajax request
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
$user = new User();
$post = Yii::$app->request->post();
$user->load($post);
return ActiveForm::validate($user);
}
}
Cheers!
I managed to solve this myself by using Javascript to make an Ajax request and PHP to receive the quest and check whether the Email already exists in the database.
function checkEmail(){
var email_check;
// Get the value of the email input field
var input_value = document.getElementById('register-email').value;
// Send the value to a PHP page to check
$.ajax({
url: 'checkemail.php/',
type: 'POST',
data: {
email_check: input_value
},
success: function(response) {
// If we have a repsonse we need to check whether it is True or False
email_check = response;
if (email_check == 1) {
// If True add error class
$('.field-register-email').addClass('has-error');
$('.field-register-email .help-block').text('The email supplied has already been used');
} else {
$('.field-register-email').removeClass('has-error');
$('.field-register-email .help-block').text(' ');
}
}
});
};
This will send a POST request to the checkemail.php which will check whether the email address is in the database