How to Use unique rules in active record yii2 - validation

I want to set the values of my table column set as unique value, how i can use to set error if in insert form, I insert the same value as data in my database?
Is it true?
public function rules()
{
return [
[['nama_barang', 'harga', 'stok', 'id_satuan'], 'required'],
[['harga', 'stok', 'id_satuan'], 'integer'],
['nama_barang', 'unique', 'targetAttribute' => ['nama_barang' => 'nama_barang']],
[['foto'], 'safe']
];
}

Remember: model, view, controller.
Model
add unique validator in your model rules like
...
[['nama_barang'], 'unique'],
...
View
Enable ajax validation in your form view
...
<?php $form = ActiveForm::begin(['enableAjaxValidation' => true]); ?>
...
Controller
Add ajax validation in your controller
Create Action
...
public function actionCreate()
{
$model = new Product();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
if ($model->load(Yii::$app->request->post())) {
...
and Update Action
...
public function actionUpdate($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);
}
if ($model->load(Yii::$app->request->post())) {
...
PS: if not present, add required classes in your controller.
use yii\web\Response;
use yii\widgets\ActiveForm;

Try this way
public function rules()
{
return [
[['nama_barang', 'harga', 'stok', 'id_satuan'], 'required'],
[['harga', 'stok', 'id_satuan'], 'integer'],
['nama_barang', 'unique', 'targetAttribute' => ['nama_barang'], 'message' => 'Username must be unique.'],
[['foto'], 'safe']
];
}

Just set unique in the rules [['name'], 'unique'],
Below is the complete function.
public function rules()
{
return [
[['name', 'description', 'comp_id'], 'required'],
[['description'], 'string'],
[['comp_id'], 'integer'],
[['name'], 'string', 'max' => 100,],
[['name'], 'unique'],
[['comp_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::className(), 'targetAttribute' => ['comp_id' => 'comp_id']],
];
}

I had a similar problem whereby when I insert a record with an existing unique field the framework remained silent returning my view back without any error.
So, the trick around this was to do a success-redirect only when $model->save() has a boolean value of true, else render back the _form.php through your view.php

Related

Laravel - How to validate Unique Rule Request as case sensitive

I have this Rules in my Laravel-5.8
Rules: create
public function rules()
{
return [
'location_name' => [
'required',
'string',
'min:3',
'max:80',
Rule::unique('hr_work_locations', 'location_name', 'company_id')
],
];
}
Rules: edit
public function rules()
{
return [
'location_name' => [
'required',
'string',
'min:3',
'max:80',
Rule::unique('hr_work_locations', 'location_name', 'company_id')->ignore($this->work_location)
],
];
}
from the rules,location_name is unique for each company (company_id). Also in the edit rules,
ignore($this->work_location)
is for the route
Controller : create
public function store(StoreWorkLocationRequest $request)
{
try {
$worklocation = HrWorkLocation::create([
'location_name' => $request->location_name,
'is_active' => 1,
]);
Session::flash('success', 'Work Location is created successfully');
return redirect()->route('hr.work_locations.index');
}
catch (Exception $exception)
{
Session::flash('error', 'Action failed!');
return redirect()->route('hr.work_locations.index');
}
}
I observe that it allows location_name as England or england.
How do I make Rule::unique as case sensitive?
Thank you.
Add a custom validation to AppServiceProvider.php under the boot() method:
Validator::extend('iunique', function ($attribute, $value, $parameters, $validator) {
$query = DB::table($parameters[0]);
$column = $query->getGrammar()->wrap($parameters[1]);
return ! $query->whereRaw("lower({$column}) = lower(?)", [$value])->count();
});
This can be extended further to accept the other parameters similar to the unique rule
Now my $rules looks like this:
protected $rules = [
'username' => 'required|alpha_dash|min:5|max:18|iunique:users,username',
];
see an issue reported here
You can make the input lower-cased so you don't have to worry of what the user input in the form.
use Illuminate\Support\Str;
After that, you can call it without the namespace prefix:
Str::lower($request->location_name);

How to use unique validation in Yii 2

I want to make usernames and email addresses unique.
I am using yii base to develop my App. It doesnt not work for me.
My Model:
public function rules()
{
return [
[['username', 'email', 'password'], 'required'],
[['username', 'email'], 'unique']
];
}
My Controller:
public function actionCreate()
{
$model = new Userapp();
$post = Yii::$app->request->post('UserApp');
if (Yii::$app->request->isPost && $model->validate()) {
$model->email = $post['email'];
$model->username = $post['username'];
$model->password = $model->setPassword($post['password']);
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('create', [
'model' => $model,
]);
}
Yii2 has a bunch of built in validators see.
One of which is unique
From Yii2 docs.
// a1 needs to be unique in the column represented by the "a1" attribute
['a1', 'unique'],
// a1 needs to be unique, but column a2 will be used to check the uniqueness
of the a1 value
['a1', 'unique', 'targetAttribute' => 'a2'],
Update:
In your rules array, add the unique validator to email and username like so:
public function rules()
{
return [
[['username', 'email', 'password'], 'required'],
[['username', 'email'], 'unique'],
];
}
Then before saving the model:
if(!$model->validate()){
return false;
}
Update 2:
You are trying to validate the model before any attributes have been assigned. Update your controller code to the following:
public function actionCreate()
{
$model = new Userapp();
$post = Yii::$app->request->post('UserApp');
if (Yii::$app->request->isPost) {
$model->email = $post['email'];
$model->username = $post['username'];
$model->password = $model->setPassword($post['password']);
if($model->validate() && $model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
else {
return false;
}
}
return $this->render('create', [
'model' => $model,
]);
}
the idea was to do a re-direction only when $model->save() is true otherwise render back the original model to be created/updated

Test the exist validator in Yii2 without database with mockeryBuilder()

I want to test my AR model without connect to database in Yii 2 so I use mockBuilder() but I dont know how can I pass the mock object to the model exist validator, for example:
class Comment extends ActiveRecord
{
public function rules()
{
return [
[['id', 'user_id', 'post_id'], 'comment'],
['comment', 'string',
'max' => 200
],
['user_id', 'exist',
'targetClass' => User::className(),
'targetAttribute' => 'id'
],
['post_id', 'exist',
'targetClass' => Post::className(),
'targetAttribute' => 'id'
]
];
}
}
class CommentTest extends TestCase
{
public function testValidateCorrectData()
{
$user = $this->getMockBuilder(User::className())
->setMethods(['find'])
->getMock();
$user->method('find')->willReturn(new User([
'id' => 1
]));
$post = $this->getMockBuilder(Post::className())
->setMethods(['find'])
->getMock();
$post->method('find')->willReturn(new Post([
'id' => 1
]));
// How can I pass to $user and $post to exist validator in Comment model?
$comment = new Comment([
'user_id' => 1,
'post_id' => 1,
'comment' => 'test...'
]);
expect_that($comment->validate());
}
}
ok, It's not a best code just I'd like to introduce what I want to do.
Yii2 ExistValidator uses ActiveQuery::exists() for check existence and you should replace generated validator to mockobject where the method createQuery returns mockobject of ActiveQuery where ::exists() return something you want (true/false) e.g.
$activeQueryMock = $this->getMockBuilder(ActiveQuery::className())
->disableOriginalConstructor()
->setMethods(['exists'])
->getMock();
$activeQueryMock->expects($this->any())
->method('exists')
->willReturn($value); //Your value here true/false
$model = $this->getMockBuilder(Comment::className())
->setMethods(['getActiveValidators'])
->getMock();
$model->expects($this->any())
->method('getActiveValidators')
->willReturnCallback(function () use ($activeQueryMock) {
$validators = (new Comment())->activeValidators;
foreach ($validators as $key => $validator) {
if (($validator instanceof ExistValidator) && ($validator->attributes = ['user_id'])) {
$mock = $this->getMockBuilder(ExistValidator::className())
->setConstructorArgs(['config' => get_object_vars($validator)])
->setMethods(['createQuery'])
->getMock();
$mock->expects($this->any())
->method('createQuery')
->willReturn($activeQueryMock);
$validators[$key] = $mock;
break;
}
}
return $validators;
});
$model->validate();

yii2 Unique Validation(non-ajax) not working

I see some links says that Yii2 unique validation is for Ajax validation only(But I dont find any best practice to follow.).So I'm asking this for non ajax validation in Yii2
my model rules are
['name', 'unique', 'targetAttribute' => 'function_id','message' => 'name must be unique.'],
My controller functions are normal actionCreate()
Anybody have solution for this? Thanks in Advance
Edit: For Ajax
ok.I Did,My controller code is now
$model = new JobFunctionRole();
$model->scenario = 'insert';
$jobFunction=JobFunction::find()->all();
if(Yii::$app->request->post()){
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
if ($model->save()) {
return $this->redirect(['view', 'id' => $model->job_function_role_id]);
}
} else {
return $this->render('create', [
'model' => $model,'jobFunction'=>$jobFunction
]);
}`
But page redirects and shows blank page.
Could be you must check for both togheter
['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]
[
'name', 'unique',
'targetAttribute' => ['name','function_id'],
'message' => 'name must be unique.'
],

Cakephp 3.x Custom Validation Rules Creation and Using

In cakephp3 Custom Validation Rules:
How to Use a global function validation method.
$validator->add('title', 'custom', [
'rule' => 'validate_title'
]);
Please any one done before? Pls Provide me the some example program.
http://book.cakephp.org/3.0/en/core-libraries/validation.html#custom-validation-rules
I tried the above but it doesn't work..?
here is an Example for validation using global function concept:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
public function validationDefault(Validator $validator) {
$validator->add('title',[
'notEmptyCheck'=>[
'rule'=>'notEmptyCheck',
'provider'=>'table',
'message'=>'Please enter the title'
]
]);
return $validator;
}
public function notEmptyCheck($value,$context){
if(empty($context['data']['title'])) {
return false;
} else {
return true;
}
}
<?php
namespace App\Model\Table;
use App\Model\Entity\Member;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class MembersTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->table('members');
}
public function validationDefault(Validator $validator) {
$validator
->add("cedula", [
"custom" => [
"rule" => [$this, "customFunction"], //add the new rule 'customFunction' to cedula field
"message" => "Enter the value greater than 1000"
]
]
)
->notEmpty('cedula');
return $validator;
}
public function customFunction($value, $context) {
return $value > 1000;
}
}
Use $context variable to comare current value with other fields like $value >= $context['data']['another_field_name'];
?>
Use $context variable to comare current value with other fields like $value >= $context['data']['another_field_name'];
This actually work for me (Cakephp 3.x). It's a good way if your condition is simple:
<?php
namespace App\Form;
use Cake\Form\Form;
use Cake\Validation\Validator;
class addPostForm extends Form {
protected function _buildValidator(Validator $validator) {
return $validator->allowEmpty('my_input', function ($context) {
return (#context['data']['an_other_input'] != "");
});
}
public function setErrors($errors) {
$this->_errors = $errors;
}
}
Here the form input my_input is allow to be empty only if a second input an_other_input is completed.
You can get form data with the variable $context['data'].
This is what worked for me for CakePHP 3.0. The important parameter here is the 'provider', which is not very clear in the document examples.
$validator->add('title', 'custom', [
'rule' => 'validate_title',
'provider' => 'table',
'message' => 'some error message'
]);
Then define your function. The variable passed to the function is
$check='title'
:
public function validate_title($check)
{
...
}
here is an Example for validation.
In your Table.
public function validationDefault(Validator $validator)
{
$validator = new Validator();
$validator
->notEmpty('username', 'A username is required')
->add('username', [
'emailValid' => [
'rule' => ['email', true],
'message' => 'You must provide a valid email'
],
'emailUnique' => [
'message' => 'The email you provided is already taken. Please provide another one.',
'rule' => 'validateUnique',
'provider' => 'table'
]
]);
return $validator;
}

Resources