Yii2 validation in separate actions for render and post form - php

I have tried different approach for create action.
Usually i have one action which renders, validates and saves data.
Now i want two separate actions. One for rendering view and second for validation and data storage.
View
$form = ActiveForm::begin([
'action' => ['ew/eshop-create'],
'method' => 'post',
]);
echo $form->field($model, 'input')->textarea([
'rows' => '20'
]);
echo Html::submitButton(
'<i class="glyphicon glyphicon-send"></i> OdoslaƄ',
[
'class' => 'btn btn-success',
'name' => 'create-button'
]
);
ActiveForm::end();
Model
class EshopCreate extends Model
{
public $input;
public function attributeLabels()
{
return [
'input' => 'JSON vstup'
];
}
public function rules()
{
return [
['input', 'required'],
['input', 'validateInput'],
];
}
public function validateInput()
{
// validate json
$this->addError('input', 'Something is wrong');
}
}
Controller
class EwController extends Controller
{
public function actionEshopCreateForm()
{
$model = new EshopCreate();
return $this->render('eshop-create-form', [
'model' => $model
]);
}
public function actionEshopCreate()
{
$model = new EshopCreate();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
exit('create');
}
return $this->redirect(['ew/eshop-create-form']);
}
}
Edit:
So i had problem with validation. I switched model->load and model->validate in actionEshopCreate.
So it works fine, but message from validateInput is not displayed. Also when i turn off clientvalidation, there are no error messages at all. So my question is how to pass errors from one action to another.
Thanks.

You are redirecting to a different action when the form is submitted and fail to pass the validation
return $this->redirect(['ew/eshop-create-form']);
the EshopCreate model will lose all validation messages when the redirection happens
Probably you want to do something like this
class EwController extends Controller
{
public function actionEshopCreateForm()
{
$model = new EshopCreate();
return $this->render('eshop-create-form', [
'model' => $model
]);
}
public function actionEshopCreate()
{
$model = new EshopCreate();
if ($model->load(Yii::$app->request->post()) && $model->validate())
{
//store the model data in session or somewhere for example where you can retrieve it later in the actionEshopCreateForm() action
return $this->redirect(['ew/eshop-create-form']);
}
return $this->render('eshop-create-form', [
'model' => $model
]);
}
}

Related

How to create a callback in laravel validation when it failed-validated and passed-validated?

I have here a validation in my custom request file.
class AuthRequest extends FormRequest
{
public function store()
{
return $this->validate([
'first_name' => ['required','min:2','max:30',new PersonNameRule],
'last_name' => ['required','min:2','max:30',new PersonNameRule],
'username' => ['required','confirmed',new UsernameRule]
]);
}
public function rules(){ return []; }
}
In my controller, this is how use it.
public function store(AuthRequest $request)
{
$data = $request->store();
return request()->all();
}
My question is how can I do these things below:
when validation failed - create a session / session(['attempt' => session('attempt')+1 ?? 1]);
when validation passed - destroy the session / session()->forget('attempt')
#mrhn is right you did not fill the rules inside the function, so the FormRequest will always return false. What you did instead, you prefer to create your own method(s) and by using the $this->validate().
Now here's how to achieve your problem, in file ..\Illuminate\Validation\Validator.php find the validate() function, and put those session you desired to perform, like these below.
public function validate()
{
if ($this->fails()) {
session(['attempt' => session('attempt')+1 ?? 1]);
throw new ValidationException($this);
}else{
session()->forget('attempt');
}
return $this->validated();
}
The solution above is global which means it will perform everytime you use $this->validate().
You can use Validator instance instead of calling validate()
$validator = Validator::make($request->all(), [
'first_name' => ['required','min:2','max:30',new PersonNameRule],
'last_name' => ['required','min:2','max:30',new PersonNameRule],
'username' => ['required','confirmed',new UsernameRule]
]);
if ($validator->fails()) {
// create a session
} else {
// destroy the session
}
You can see more in the doc here: https://laravel.com/docs/7.x/validation#manually-creating-validators
Firstly i will convert your validation to a form request, this will automatically resolve when injected into a controller.
UserCreateRequest extends FormRequest {
public function rules() {
'first_name' => ['required','min:2','max:30',new PersonNameRule],
'last_name' => ['required','min:2','max:30',new PersonNameRule],
'username' => ['required','confirmed',new UsernameRule]
}
}
To use it inject it like so.
public create(UserCreateRequest $request) {
...
}
Here you can utilize two callback methods passedValidation() and failedValidation(), in your form request.
protected function failedValidation(Validator $validator) {
session(['attempt' => session('attempt')+1 ?? 1]);
return parent::failedValidation($validator);
}
protected function passedValidation() {
session()->forget('attempt')
return parent::passedValidation();
}

Yii2 how to implementation Optimistic Locks

Yii2 how to implementation Optimistic Locks.
I'm trying to follow this official doc.
I thought I carefully follow the step.
but still error :
Here my procedure.
Create a column in the DB "version defualt velue = '0'
2.Model.php
use yii\behaviors\OptimisticLockBehavior;
class OptimisticTest extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'optimistictest';
}
public function rules()
{
return [
[['version'], 'required'],
[['created_by', 'updated_by','version'], 'integer'],
];
}
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'value' => new Expression('NOW()'),
],
[
'class' => BlameableBehavior::className(),
],
[
'class' => OptimisticLockBehavior::className(), //'getLockAttribute' =>$this->version
],
];
}
}
myController.php
public function actionUpdate($id)
{
$model = $this->findModel($id);
$tempDocs = $model->docs;
$modelRunning = $this->findModelRunning($model->running_id);
$model->scenario = 'update';
try {
if ($model->load(Yii::$app->request->post()) &&
$modelRunning->load(Yii::$app->request->post()) &&
Model::validateMultiple([$model,$modelRunning]))
{
if($modelRunning->save())
{
$this->CreateDir($model->ref);
$model->docs = $this->uploadMultipleFile($model,$tempDocs);
$model->save();
}
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
'modelRunning' => $modelRunning,
]);
}
} catch (StaleObjectException $e) {
// logic to resolve the conflict
Yii::$app->session->setFlash('danger',Yii::t('app', 'Record can not be updated, there is a user associated with it'));
return $this->redirect(['index']);
}}
Error is From Model.php in public function behaviors()
in step 1. Override this method to return the name of this column.
how to override this method.
what i'm missing.
Overriding optimisticLock() method means, that you have to implement the method in your model so it can be used instead of default implementation.
Your model should look like this
class OptimisticTest extends \yii\db\ActiveRecord
{
//... your other methods in model
public function optimisticLock()
{
//this method should return the name of version attribute
return 'version';
}
}

Insert record after Laravel form validation

I'm trying a Laravel 5.8 request validation. I managed to return errors and display them to my view. The problem is when I try to not trigger any validation rule, for whatever reason I cannot insert records into my table.
Error
Too few arguments to function
App\Http\Requests\FieldRequest::Illuminate\Foundation\Providers{closure}(),
0 passed and exactly 1 expected
Controller
class FormController extends Controller
{
public function create()
{
return view('create');
}
public function store(FieldRequest $req)
{
$validate_data = $req->validate();
Form::create($validate_data);
return redirect()->back()->with('message', 'Success!');
}
}
FormRequest
class FieldRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'item_name' => 'bail|required|max:255',
'sku_no' => 'required|alpha_num',
'price' => 'required|numeric',
];
}
public function messages()
{
return [
'item_name.required' => 'An Item Name is required',
'sku_no.required' => 'An SKU NO is required',
'price.required' => 'The price is required',
];
}
}
I'm expecting something to be inserted in my table. Do I need to perform the validation in my controller or not to achieve this? Thanks in advance!
public function store(FieldRequest $req)
{
$data = $req->all();
Form::create($data);
return redirect()->back()->with('message', 'Success!');
}
when you are working with form request you no need to use validate() function because your request goes in form request to validate your data then it will store records

I am unable to redirect user in Yii after login

I am new to Yii so i decided to write a simple login script just to put to test what i have learnt so far.
For some reasons my code doesn't work as it should.
Ideally if username and password exist, it ought to redirect to the index view and also and if it doesn't exist it ought to redirect to the login view.
If username and password exist it redirects as it ought to and it also shows the right session value but if username and password doesn't exist i also get the same result.
Any idea what i am doing wrong?
Here is my controller code:
<?php
namespace app\controllers;
use app\models\Users;
use Yii;
class UsersController extends \yii\web\Controller
{
public function actionIndex()
{
$model = new users();
return $this->render('login', [
'model' => $model,
]);
}
public function actionLogin()
{
$model = new Users();
if ($model->load(Yii::$app->request->post())) {
// form inputs are valid, do something here
$request = Yii::$app->request;
$form_values = $request->post('Users');
//var_dump($form_values['email']); exit;
//echo $form_values['email'];
if($model !==NULL){
$model = $model->doLogin($form_values['email'],$form_values['password']);
$session = Yii::$app->session;
$session->setFlash('login', 'Login succesful.');
return $this->render('index', [
'model' => $model,
]);
}
elseif($model === NULL){
$session = Yii::$app->session;
$session->setFlash('login', 'Invalid Login.');
return $this->render('login', [
'model' => $model,
]);
}
}
}
}
This is because your condition does not make sense and your else branch is a dead code.
First you're initializing $model variable:
$model = new Users();
Then your testing if it is not null:
if($model !==NULL){
Which will always return true since you already initialized this variable to contain Users model, there is no chance it will be null. You should probably have something like this:
class UsersController extends \yii\web\Controller {
public function actionIndex() {
$model = Users::findOne(Yii::$app->user->id);
return $this->render('index', [
'model' => $model,
]);
}
public function actionLogin() {
$model = new Users();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
Yii::$app->session->setFlash('login', 'Login successful.');
return $this->redirect(['index']);
}
return $this->render('login', [
'model' => $model,
]);
}
}
And handle login (validating username and password) in Users::login() method. See example in basic application template.

Yii2 ActiveRecord Model best practice

I have these classes:
Model:
namespace app\models;
use \yii\db\ActiveRecord;
class MyModel extends ActiveRecord {
public function rules() {
return [
[['name'], 'required'],
[['id'], 'default', 'value' => null]
];
}
}
Controller:
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\MyModel;
class MymodelController extends Controller{
public function actionEdit($id = null){
$model = new MyModel();
if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->save()) {
Yii::$app->session->setFlash('msg', 'Model has been saved with ID ' . $model->id);
}
return $this->render('edit', [
'model' => $model
]);
}
}
View:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php if(Yii::$app->session->hasFlash('msg')): ?>
<div class="alert alert-success"><?= Yii::$app->session->getFlash('msg'); ?></div>
<?php endif; ?>
<?php $form = ActiveForm::begin(); ?>
<?= Html::activeHiddenInput($model, 'id'); ?>
<?= $form->field($model, 'name') ?>
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
<?php ActiveForm::end(); ?>
I want to use this view for editing and for inserting. Editing does not work as it is, because I am creating a new object instead of changing an existing one in the Controller. I am unsure what is the best practice here or if I am missing some already existing built in function?
Should I create my own model class and implement the logic model <-> active record in the controller
or
Should I just re query the database with the $model->id in the controller and copy all properties if needed?
you should use two actions for edit and insertion
for edit first find the model
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('edit', [
'model' => $model,
]);
}
protected function findModel($id)
{
if (($model = MyModel::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
if you use CRUD for generating your controller you don't have to write these actions.
For CRUD (Create, Read/View, Update and delete) you can use gii. This power tool generate automatically all you need for an ActiveRecord, Controller with the basic action (index, view, create, update, delete, find) and related view.
In gii you first genearate model class and then generate CRUD for this class.
But the most informant things all this information are coerently related each other
see this doc is very useful and the best pratice for Yii2 is embetted in the tool
http://www.yiiframework.com/doc-2.0/guide-start-gii.html
In Your Create action:
public function actionCreate()
{
$model = new Yourmodel();
if ($model->load(Yii::$app->request->post())) {
if($model->save()){
return $this->redirect(['view']);
}
}
return $this->render('create', [
'model' => $model,
]);
}
In your Update action:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
]);
}

Categories