in Yii framework, can I use unique validation rule to check uniqueness of an field within some condition (I know there is criteria, but this condition is a bit tricky)? Ie, i want to check num_days unique by property_id.
table:
NUM PROP_ID
3 4
3 5
validation should pass in case i try insert 3, 6, but fail in case of 3, 4
Check out UniqueAttributesValidator, and also this answer. In the links you'll see that they have used $this->attributename for the params array of the criteria option of CUniqueValidator, but for some reason $this->attributename was null for me. I believe that this is because the $this is not being passed correctly to the validator, so anyway it would be best to use the UniqueAttributesValidator class, because it has been made just for these kinds of situations.
The resulting sql will have a WHERE clause like this:
SELECT ... WHERE (`num`=:value) AND (`prop_id`=:prop_id) ...
which will easily fail for 3, 4 and pass for 3, 6. So it should work for your situation.
First Create unique field in you table
and in model add this to your rules()
array('field_name', 'unique'),
For combination of two fields unique use this code
public function rules() {
return array(
array('firstKey', 'unique', 'criteria'=>array(
'condition'=>'`secondKey`=:secondKey',
'params'=>array(
':secondKey'=>$this->secondKey
)
)),
);
}
Create your custom Validator or validation function, it's simple.
Related
In Laravel, I have a persons model that has a many-to-many relationship with its group. The person's name needs to be unique in its group but not on the persons table. How would ensure that?
My validation is done in the controller store method using $request->validate(['name => ...
I currently save the new person in a controller using simply - Person::create([...
My simple approach is using a composite primary keys on pivot table and use basic exception handling like try catch stuff whenever inserting data is fail due to migration
$table->foreignId('group_id') // Add any modifier to this column
$table->foreignId('person_id') // Add any modifier to this column
$table->primary(['group_id', 'person_id']);
If you want to do it on controller, make sure to setup relationship. Then just use Rule::notIn() Validation
'name' => [
'required',
Rule::notIn(/* put your logic here */),
],
You can use 'exist' rule in Laravel Validation like that:
'name' => 'exists:group,name,person_id,'.$id
For more info you can check here:
https://laravel.com/docs/9.x/validation#rule-unique
I have been trying to figure this one out for quite a whiale and have gone through every post from here and Laracast to figure this out but in vein.
I have done this before and it worked but I am not quite sure why it doesn't now.
Basically setting up a rule in your form request should follow the format below:
<?php
class MyFormRequest extends Request{
public function rules(){
return [
'field' => 'required|unique:table_name:field,' . $this->input('field');
];
}
}
This to me should work but according to my experience at the moment doesn't. I have tried to separate the rules by checking the incoming request method and assign rules based on whether the request is an update or create.
Shouldn't what I have suffice for this requirement? What would be the best way of re-using my FormRequest but making sure that I am able to validate uniqueness of as many table fields as I want regardless of their data types because I am sensing that perhaps this is related to the fact that Laravel seems to be doing an aggregate on the field being validated and if it is not an integer for example it will keep on displaying the error message.
Another possible way of solving this is if I implement my own validation method for the fields I am interested to validate. How would I go about doing this?
This rule works only if the primary key of "table_name" is "id" but if it is different then you have to write it as the fourth parameter to unique rule as follows:
<?php
class MyFormRequest extends Request{
public function rules(){
return [
'field' => 'required|unique:table_name:field,' . $this->input('field').','.'primary_key_field';
];
}
}
hope it help you !!!
You need to tell the validator which rows to exclude from duplicate search.
If your primary key is id, you need to pass its value:
'field' => 'required|unique:table_name,field,' . $this->input('id')
Your code has also some other errors:
'field' => 'required|unique:table_name:field,' . $this->input('field');
^ ^
The rule "unique" needs aditional arguments when updating. Those aditional arguments will be responsible for ignore the actual registry you're working on. If not applied, this rule will always invalidate, since the registry always will be found on database.
Your thirdy argument should be the ID you want to ignore. (you're passing a value...)
*Another thing: I picked up a misstype from your rule. Check below the right sintax with new arguments.
'field' => 'required|unique:table_name,field,'.$this->input('id')'
PS: if your id column from your table has a different name than "id", you need to specify this column name as the fourth parameter.
'field' => 'required|unique:table_name,field,'.$this->input('id').',column_name'
I want to validate a date field in my form and I also have a date field in a table and I want to check if the date in the form is after the date in the table.
Can I do this with the Validation function after:date?
I think with this function I need to define the date in the model (where I got the validation rules).
Or do I need to check it with a custom validation rule?
Thanks
You should be able to use the after:date validation if you retrieve the date to be checked against from the database table first, eg.
$date_from_table = call to retrieve date from table;
$validator = Validator::make(
array('date' => $date_from_form),
array('date' => 'after:'.$date_from_table)
);
Which as you note could go in the model if that is where you are defining the validation rules - in which case it would probably be slightly different to the above as you may not be making the validator instance in the model, just specifying the validation rules, eg.
protected $rules = array(
'date' => 'after:'.$this->date_from_table
);
If this works it probably isn't worth the bother of creating a custom validation rule unless you are going to be doing the same sort of thing in a number of different models.
Let's say I have a model with 2 tables : Owner (int: id) and Car (int: id, int:owner_id).
I'm trying to build a validation rule on Car in order to avoid non existing owner_ids to be bound to the Car.owner_id field. I'd like to have this validation rule in the code instead of just using the DB foreign key check, because it allows me to easily display error messages on the form instead on having to process DB exceptions later.
Thus, in my model, I'd like to have something like :
public function rules() {
return array(
array('owner_id', 'in', 'range' => array(11, 12, 13)),
);
}
where 11, 12, 13 are the existing Owner ids.
I can find those ids through code like :
$ars = Yii::app()->db->createCommand("SELECT id FROM owner")->queryAll();
$ids = array();
foreach($ars as $ar) {
$ids[] = $ar['id'];
}
However, I wondered if there is any built in method in Yii allowing to get this array in a more lazy way like "$ids = Owner::model()->findIdsAsArray()" or something similar.
You dont want to do validate a range. Yii provides CExistValidator with alias exist exactly for foreign key check. The range validator may fail if the owner ids are not continuous ie if we delete an intermediate user. So You can use a validation rule as follows
array('owner_id', 'exist', 'attributeName'=>'id', 'className'=>'Owner'),
The above code validates that there exist an owner_id in the model Owner and raises an error if its not exist in the DB.
I have table user which have fields username,password, and type. The type can be any or combination of these employee,vendor and client i.e a user can be vendor or client both or some another combination. For type field I have used the multiple checkbox, see the code below. This is the views/users/add.ctp file
Form->create('User');?>
Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('type', array('type' => 'select', 'multiple' => 'checkbox','options' => array(
'client' => 'Client',
'vendor' => 'Vendor',
'employee' => 'Employee'
)
));
?>
Form->end(__('Submit', true));?>
This is the code I have used in the model file. A callback method beforeSave
app/models/user.php
function beforeSave() {
if(!empty($this->data['User']['type'])) {
$this->data['User']['type'] = join(',', $this->data['User']['type']);
}
return true;
}
This code saves the multiple values as comma separated value in db.
The main problem comes when Im editing a user. If a user has selected multiple types during user creation I can't find the checkbox checked for that user types.
you should never be saving serialized data, json or csv in a field. This makes your life real hard later on down the line.
While habtm is one way to do things, if your binary maths is reasonable you might want to checkout bitmasks for this. here is a great post http://mark-story.com/posts/view/using-bitmasks-to-indicate-status
basics would be
1 = employee
2 = vendor
4 = client
// 8 = next_type
then, if the user was type employee & vendor the type would be 3 (1 + 2) and if it was a vendor & client the type would be 6 (2 + 4)
as you can see there is no way to mix it up, and bitwise works pretty good in mysql aswell so finds are pretty easy. See the post for much more detailed information
You should have a table types and a join table users_types.
What you're looking at is a HABTM relationship, so you should handle it like one.
In the joining UsersType model you should add a custom validation rule that checks if the current combination of types is allowed.
If you want to modify data after it's been found in the database, you can use the afterFind() callback in your model.
So in your case, put something like this is your user model:
function afterFind($results) {
$results['User']['type'] = explode(',', $results['User']['type']);
return $results;
}
There's more info on afterFind in the CakePHP manual.
That being said, it might be worth considering another approach, like a HABTM relationship as deceze first suggested above.