Yii2 custom client validation with ajax rendering in modal - php

I have a model with a custom validation method. For testing it always returns an error message.
public function rules()
{
return [
...
['staff_ids', 'each', 'rule' => ['string']],
[['staff_ids'], 'validateStaffIds'],
...
];
}
public function validateStaffIds($attribute, $params, $validator) {
$this->addError($attribute, 'There is an error in the staff ids');
}
In the view.php is the modal element
<p>
<?= Html::button('Add Ensemble Staff',
['value' => Url::to(['ensemble/add', 'id' => $model->id]),
'title' => 'Adding New Ensemble Staff',
'class' => 'showModalButton btn btn-primary']);
?>
</p>
<?php
Modal::begin([
'closeButton' => [
'label' => 'x',
],
'headerOptions' => ['id' => 'modalHeader'],
'id' => 'modal',
'size' => 'modal-lg',
]);
echo "<div id='modalContent'></div>";
Modal::end();
?>
The js code which fires everything up...
$(function(){
$(document).on('click', '.showModalButton', function(){
if ($('#modal').data('bs.modal').isShown) {
$('#modal').find('#modalContent')
.load($(this).attr('value'));
} else {
//if modal isn't open; open it and load content
$('#modal').modal('show')
.find('#modalContent')
.load($(this).attr('value'));
}
//dynamiclly set the header for the modal
...
});
});
And the ensemble controller which handles the add action
public function actionAdd($id)
{
$model = $this->findModel($id);
// in the post ( 'ensembleStaff_ids' => [0 => '2']); where the id actually is staff_id
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $id]);
} else {
return $this->renderAjax('add', [
'model' => $model,
]);
}
}
And the form which is injected by the js into the model (Url::to(['ensemble/add', 'id' => $model->id]), )
<?php $form = ActiveForm::begin(['id' => 'add-theater-stuff-form']); ?>
<?= $form->field($model, 'staff_ids')->widget(Select2::className(), [
'model' => $model,
'data' => ArrayHelper::map(app\models\TheaterStaff::find()->where(['theater_id' => $model->theater_id])->all(), 'staff_id', 'staff.fullname'),
'options' => [
'multiple' => true,
'prompt' => 'Ensemble Staff',
],
'pluginOptions' => [
'tags' => true
]
]); ?>
<div class="form-group">
<?= Html::submitButton('Add', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
Clicking on the Add Ensemble Staff Button works fine and brings up the modal window. The form itself works fine so far; also the default validation works. Even the custom validation is called, but return $this->renderAjax(...) isn't load in the modal window anymore; it is separately.
A picture showing the modal loaded, the result after submit and a modal with default validation.
I found a similar problem here. But adding an id to the form, doesn't solve the problem. So how to get the default validation showing up properly in the modal window? Does anyone have a clue?
Solution
Thanks for the response. For me the solution was:
Enable ajax in the form
<?php $form = ActiveForm::begin(['id' => 'add-ensemble-stuff-form', 'enableAjaxValidation' => true]); ?>
And to add the following logic in the controller
public function actionAdd($id)
{
$model = $this->findModel($id);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
} else {
// in the post ( 'ensembleStaff_ids' => [0 => '2']); where the id actually is staff_id
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $id]);
} else {
return $this->renderAjax('add', [
'model' => $model,
]);
}
}
}

if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}else{/* your code */}
add this in controller use yii\web\Response

Related

Input validation error not showing to user

I have this in my action to validate an ajax post
if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())){
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
in my view
<?php
$form = ActiveForm::begin([
'id' => $model->formName().'-form',
'action' => Url::to(['/payment/stripe/payment-session']),
'scrollToError' => false,
'validateOnSubmit' => true,
'enableAjaxValidation' => true
]);
echo $form->field($model, 'number')->textInput([ 'class' => 'form-control form-control-lg text-uppercase', 'placeholder'=>'Enter Number', 'id' => 'reports-number'])->label(false);
....
....
ActiveForm::end();
?>
in my model
public function rules() {
...
[['number'], ENumberValidator::class, 'on' => ['check-number']],
...
}
when i enter 456!! in the input, i get this ajax returned. which is correct.
{
"reports-number": [
"Number is an invalid format."
]
}
but for some reason no error message shows under the input, the input has a check mark in green and form can still be submitted. Any idea what I'm missing here? Thanks

Yii2 redirecting to previous page after login

how to redirect the users after login,
i tried to use all of these :
Yii::$app->request->getReferrer(); // printing the refferrer url to screen
$this->redirect(\Yii::$app->request->referrer)
return \Yii::$app->getResponse()->redirect(\Yii::$app->getUser()->getReturnUrl($defaultUrl));
none of these above is working
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
$this->goHome();
// $this->goHome();
}
/** #var LoginForm $model */
$model = \Yii::createObject(LoginForm::className());
$event = $this->getFormEvent($model);
$this->performAjaxValidation($model);
$baseurl = \Yii::$app->request->getAbsoluteUrl();
$this->trigger(self::EVENT_BEFORE_LOGIN, $event);
if ($model->load(\Yii::$app->getRequest()->post()) && $model->login()) {
$this->trigger(self::EVENT_AFTER_LOGIN, $event);
// return $this->redirect(\Yii::$app->request->referrer);
// return \Yii::$app->getResponse()->redirect(\Yii::$app->getUser()->getReturnUrl($defaultUrl));
// return \Yii::$app->request->getReferrer(); // printing the refferrer url to screen :(!!
return $this->redirect($baseurl);
// return $this->redirect(\Yii::$app->request->referrer);; //this one is returning everthing to main page, because of line 147
// return $this->goBack();
}
return $this->render('login', [
'model' => $model,
'module' => $this->module,
]);
}
In the web.php config i have this :
$config =[
..//
'modules' => [
'user' => [
'class' => 'dektrium\user\Module',
'enableUnconfirmedLogin' => true,
'confirmWithin' => 21600,
'cost' => 12,
'enableFlashMessages' => true,
'admins' => ['a'],
],
//..
all of these above is just sending the user after login to the home page
what should i do please , i have searched every where read the documentations
After 4 hours of trying i tried to "Echo" the URL after requesting referrer ;
the referrer is working fine the problem is after login the page loads more than one time and here is the problem why it's not sending back to that page but sending people to the current page (login page) then if he is a user he is automatically sending home.
The problem is common and it is related to the issue that at the time when you post login, your actual referrer is login page - actionLogin(), so you are redirected back again and off course you get passed throughout the
condition that you are not the Guest. In order to handle this, you have to assign a referrer to a modal field, so it can be posted with the login information. So at the time when login is validated, you have the required referrer url in your field. Check if you have this field identified in your form:
<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>
<?= $form->field($model, 'referer')->hiddenInput()->label(false) ?>
Controller
$form = new LoginForm();
//get previos viewed page url and store in the new model
$form->referer = Yii::$app->request->referrer;
if ($form->load(Yii::$app->request->post())) {
if($form->login()){
return $this->goBack((($form->referer) ? $form->referer : null));
}
}
LoginForm() model
public $referer;
/**
* {#inheritdoc}
*/
public function rules()
{
return [
//...
['referer', 'string'],
];
}
After that, when it will be post request, this field will contain a referrer, which you will pass in your controller.
Further, if you use the yii2-user module, now it is possible and necessary in the config in the controllerMap to remove all forced redirects for the event "after logging in" (I commented them out):
...
'modules' => [
'user' => [
'class' => \dektrium\user\Module::className(),
'admins' => ['adminname'],
'enableConfirmation' => false,
'modelMap' => [
'User' => 'app\models\User',
'UserSearch' => 'app\models\UserSearch',
'Profile' => 'app\models\Profile',
],
'controllerMap' => [
'profile' => 'app\controllers\user\ProfileController',
'security' => [
'class' => \dektrium\user\controllers\SecurityController::className(),
'on ' . \dektrium\user\controllers\SecurityController::EVENT_AFTER_LOGIN => function ($e) {
/*if (Yii::$app->user->can('student free')) {
Yii::$app->response->redirect(array('/course'))->send();
}
if (Yii::$app->user->can('admin')) {
Yii::$app->response->redirect('http://site.ru/user/')->send();
}*/
//Yii::$app->response->redirect(Yii::$app->request->referrer)->send();
// Yii::$app->response->redirect(array('/user/'.Yii::$app->user->id))->send();
//Yii::$app->end();
}
],
],
],
...
I did it, with the help of #serghei Leonenco,
but i had these problems :
The page reloaded after pressing the button submit so the referrer was the login page always and this is why it kept sending users to main page.
I had to get the referrer before the form begin.
I had to pass the value of the referrer to a hidden input.
I had to check if the referrer is from my website or the guest got on login page from a 3rd party site.
Passed the value after checking, used redirect and Url::to('link', true) because the value i got from referrer was a full URL and i couldn't just redirect to a full link this is why i used Url::to with the true condition which means creating a new URL.
View
Notice that i used the referrer value before the form.
<div class="panel-body arab">
**<?php $referer = \Yii::$app->request->referrer;?>**
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'enableAjaxValidation' => true,
'enableClientValidation' => false,
'validateOnBlur' => false,
'validateOnType' => false,
'validateOnChange' => false,
]) ?>
**<?= $form->field($model, 'referer')->hiddenInput(['value' => $referer])->label(false) ?>**
<?php if ($module->debug): ?>
<?= $form->field($model, 'login', [
'inputOptions' => [
'autofocus' => 'autofocus',
'class' => 'form-control',
'tabindex' => '1']])->dropDownList(LoginForm::loginList());
?>
<?php else: ?>
<?= $form->field($model, 'login',
['inputOptions' => ['autofocus' => 'autofocus', 'class' => 'form-control', 'tabindex' => '1']]
);
?>
<?php endif ?>
<?php if ($module->debug): ?>
<div class="alert alert-warning">
<?= Yii::t('user', 'Password is not necessary because the module is in DEBUG mode.'); ?>
</div>
<?php else: ?>
<?= $form->field(
$model,
'password',
['inputOptions' => ['class' => 'form-control', 'tabindex' => '2']])
->passwordInput()
->label(
Yii::t('user', 'Password')
. ($module->enablePasswordRecovery ?
' (' . Html::a(
Yii::t('user', 'Forgot password?'),
['/user/recovery/request'],
['tabindex' => '5']
)
. ')' : '')
) ?>
<?php endif ?>
<?= $form->field($model, 'rememberMe')->checkbox(['tabindex' => '3']) ?>
<?= Html::submitButton(
Yii::t('user', 'Sign in'),
['class' => 'btn btn-success btn-block', 'tabindex' => '4']
) ?>
<?php ActiveForm::end(); ?>
Model
Notice I used regex to make sure that the user got to login page using my website.
public function getReferer()
{
$getLink = \Yii::$app->request->post('login-form')['referer'];
if(preg_match('/tajrobtak/', $getLink)){
return $getLink;
} else {
return "";
}
}
Controller
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
$this->goHome();
}
/** #var LoginForm $model */
$model = \Yii::createObject(LoginForm::className());
$referery = $model->getReferer();
$event = $this->getFormEvent($model);
$this->performAjaxValidation($model);
$this->trigger(self::EVENT_BEFORE_LOGIN, $event);
if ($model->load(\Yii::$app->getRequest()->post()) && $model->login()) {
$this->trigger(self::EVENT_AFTER_LOGIN, $event);
$this->redirect(Url::to($referery,true));
}
return $this->render('login', [
'model' => $model,
'module' => $this->module,
]);
}

Yii2 - Save form in modal with Ajax

I have two models one is "register-products" (where I have a dropdown with all brands) and another form is "brands". I need to render form "brands" inside the form "register-products" to create a new brand using bootstrap Modal widow and it works, but I need the newly created brand to be available inside the dropdown of the register-products form so that I don't have to refresh the page each time.
At this moment what happens is that after creating a new brand is redirecting to the view with details of the new brand.
register-products form
<?php
Modal::begin([
'header' => 'Test',
'id' => 'modal',
'size' => 'modal-lg',
]);
echo "<div id='modalContent'></div>";
Modal::end();
?>
Html::a('+', ['/brands/create'], ['id' => 'open_modal', 'class' => 'btn btn-success']);
js
$(function(){
$('#open_modal').click(function (){
$.get($(this).attr('href'), function(data) {
$('#modal').modal('show').find('#modalContent').html(data)
});
return false;
});
BrandsController
public function actionCreate()
{
$model = new Brands();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} elseif (Yii::$app->request->isAjax) {
return $this->renderAjax('create', [
'model' => $model
]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}

Missing Required Parameter in Yii2 Gridview Action Button

I'm trying to add my own action button in Yii2-Kartik Gridview.
This is my custom button:
This is my code in index.php
[
'class' => 'yii\grid\ActionColumn',
'template' => '{edit}',
'buttons' => [
'edit' => function ($url, $model) {
return Html::a('<button type="button" class="btn btn-edit-npwp"><i class="glyphicon glyphicon-plus-sign"></i></button>', $url, [
'title' => Yii::t('app', 'Edit'),
'data-toggle' => "modal",
'data-target' => "#myModal",
'data-method' => 'post',
]);
},
],
'urlCreator' => function ($action, $model, $key, $index) {
if ($action === 'edit') {
$url = Url::toRoute(['vatout-faktur-out/add-data', 'id' => $model->fakturId], ['data-method' => 'post',]);
return $url;
}
}
],
and this is my action in controller.php
public function actionAddData($id) {
$model = new VatoutFakturOut();
return $this->render('addData', [
'model' => $model,
]);
}
I want to process the data from the row that I've clicked the button.
But, this return error
Missing required parameters: id
Why does this happen? And how to fix this?
Thanks
In urlCreator you used if statement to check, if action is edit, and if it is, you add param id to your button url. Otherwise it doesnt have one, so there's two solutions:
Remove if statement from:
'urlCreator' => function ($action, $model, $key, $index) {
$url = Url::toRoute(['vatout-faktur-out/add-data', 'id' => $model->fakturId]);
return $url;
}
Or remove$id from your actionAddData() - because youre not using it at all:
public function actionAddData() {
$model = new VatoutFakturOut();
return $this->render('addData', [
'model' => $model,
]);
}

Filling the foreign key field in one form from another form of related models in yii2

I got two related tables(models) [Sub with primary key id] and [Case with foreign key sub_id]. I created Sub with id=4. I want to create data of Case model in view.php(form) of Sub model. I did a "Create Case" Button which refer to the actionCreate of Case model.
This is my "Create Case" button in sub/view.php:
<?= Html::a(Yii::t('app','Create Case'), ['/case/create', 'sub_id' => $model->id], ['class' => 'btn btn-primary']) ?>
It looks like in the
picture
This button referred me to the create form of Case model, where i should get the field sub_id = 4. Now my _form.php has
<?= $form->field($model, 'sub_id')->textInput() ?>
What should i change to get the automatically filled field sub_id with id of parent model?
UPDATE: I added relevant code from the appropriate view, controller files.
I didn't changed model files.
CaseController.php file looks like shown below
class CaseController extends Controller
{
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
public function actionIndex()
{
$searchModel = new CaseSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
public function actionCreate($sub_id)
{
$model = new Case();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'parent' => $sub_id
]);
}
}
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,
]);
}
}
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
protected function findModel($id)
{
if (($model = Case::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
sub/view.php file:
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
$this->title = $model->id . ": " . $model->fullname;
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Subs'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="sub-view">
<h3><?= Html::encode($this->title) ?></h3>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'address_id',
'address.region.name',
[
'label' => 'address',
'value' => 'Street: ' . $model->address->street . ' House ' . $model->address->house . ' Flat ' . $model->address->flat
],
],
]) ?>
<p>
<?= Html::a(Yii::t('app', 'Create Case'), ['/case/create', 'sub_id'=>$model->id], ['class' => 'btn btn-success']) ?>
</p>
</div>
case/_form.php file:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
<div class="case-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'id')->textInput() ?>
<?php if($model->isNewRecord && isset($parent_id)) {
$model->sub_id = $parent_id;
} ?>
<?= $form->field($model, 'sub_id')->textInput(['readonly' => true, 'value' => $model->sub_id]) ?>
<?= $form->field($model, 'case_date')->textInput() ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
With lack of any further information, to the best of my understanding this is what you are asking -
Taking the example in your picture, if user clicks on Create Case button, then a new form (Create Case) will open. In that Create Case form, among other input fields, there is a field for sub_id and it should be populated by default with the value 4 (since in the picture the ID of the User Harry Potter is 4).
Based on the above you simply need to do the following -
In your action (within the CaseController) for create case, you pass the sub_id like below -
/* ** CaseController ** */
public function actionCreate($sub_id)
{
//....other code
return $this->render('create', ['model' => $model,'parent_id' => $sub_id]);
}
And then inside the _form.php where you are showing the Create Case form you simply do like this -
/* ** _form.php ** */
//... other code
//if you are using _form.php for Edit Form as well,
//this prevents the value from the DB being over-written
if($model->isNewRecord && isset($parent_id)) {
$model->sub_id = $parent_id;
}
<?= $form->field($model, 'sub_id')->textInput() ?>
//... other code
This should be enough to display the value passed from the parent form.

Categories