Yii2 apply validation rule on array of attributes - php

I use Yii2 and model rules for validation.
I have an $id attribute which is an hash value. The validation rule looks like this:
return [
['id', 'match', 'pattern' => '/^[a-f0-9]{16,32}$/i',
'on' => [self::SCENARIO_DEFAULT]],
[...some more validation rules...]
]
This works fine with with $id = '17e94c10df492a39'
But now, I have an array of ids, like this:
$id_list = ['17e94c10df492a39','27e94c10df492a39','37e94c10df492a39'];
Is there a way to use the existing validator rule for this array? Or is there a way to define a new rule using the match-validator on an array?
I know, that it's possible to write an own validator. But it would be nice, if this works with yii2 on-board resources.

What you are looking for is core validator each. This validator iterates through array and apply defined rule to each item in array.
You can define the rule for example like this:
return [
[
'id_list',
'each',
'rule' => ['match', 'pattern' => '/^[a-f0-9]{16,32}$/i'],
],
// ... other rules ...
];

Related

Using excludes_unless validation rule with arrays in Laravel

Laravel's exclude_unless,field,value rule doesn't seem to work in the instance where field is an array of values and value is contained within the field array.
Given the following:
$validator = Validator::make($request->all(), [
'some_array' => 'array',
'some_field' => 'exclude_unless:some_array,foo|alpha_num',
]);
Where some_array is equal to ['foo'], the validator will fail to exclude some_field. This seems to be because the comparison that lies beneath excludes_unless is in_array(['foo'], ['foo']) which returns false.
Is it possible to achieve the same logic of exclude_unless — excluding a field from validation if another field equals a certain value, but where the field being compared against is an array and we're checking that the value is in that array?
As long as you're using Laravel 8.55 or above, you could use Rule::when():
use Illuminate\Validation\Rule;
$someArray = $request->input('some_array', []);
$validator = Validator::make($request->all(), [
'some_array' => 'array',
'some_field' => Rule::when(!empty(array_intersect(['foo'], $someArray)), ['exclude']),
]);

rename attribute item array in request validation laravel

I am working with the laravel validations in form request and this time I need to validate a data array something like this:
public function rules()
{
$rules = [
'products.*.quantity' => 'required|integer',
'products.*.dimension' => 'required|min:5|max:16',
'products.*.description' => 'required|min:5',
];
return $rules;
}
where products is the array where I have each of the items, this works however it gives me a message more or less like this: The products.1.quantity field is required.
I need to change the name of the attribute, I know it is possible to change the name inside the messages method giving a new value to products.*. quantity for example products.*. quantity => "quantity", however I would also like to specify the key of the item that is failing and at the end have a message like this:
The quantity in item 1 field is required.
then is it possible to achieve this?
Search for this file resources/lang/xx/validation.php and modify custom entry with your custom messages
'custom' => [
'products.*.quantity' => [
'required' => 'Your custom message',
]
]

Get form/fieldset element name inside custom validator class in ZF3

I created my custom validator class that implemented ValidatorInterface. How to get element name of the fieldset or form that is validated?
I need this inside the validator class.
I am going to do some validating logic inside the class validator cause I am going to use the context array with all values and distinguish which is the current one.
Nope you can't. But you can use callable filter to re-design your value. I don't know if it's reasonable way to do it. I didn't face such a problem like this. But here's an example
$this->add([
/** other settings **/
"filters" => [
[
"name" => \Zend\Filter\Callable::class,
"options" => ["callback" => function($value){
return "fieldset-x:".$value;
}]
]
],
"validators" => [
[
"name" => \Zend\Validator\Callable::class,
"options" => ["callback" => function($value){
/** algorithm: split via ":". first element is freamwork **/
}]
]
]
])
I used callable filter and validator to do is. You may want to write your own filter/validator.

In Laravel 4, How are the merging rulesets implemented?

I'm using the dwightwatson/validating package to create validation rules in the model.
I particularly like the custom rulesets you can create for different routes.
Model
protected $rulesets = [
'set_up_all' => [
'headline' => 'required|max:100',
'description' => 'required'
],
'set_up_property' => [
'pets' => 'required'
],
'set_up_room' => [
'residents_gender' => 'required',
'residents_smoker' => 'required'
],
'set_up_roommate' => [
'personal_gender' => 'required',
'personal_smoker' => 'required'
]
];
Controller
$post = new Post(Input::all());
if($post->isValid('set_up_all', false)) {
return 'It passed validation';
} else {
return 'It failed validation';
}
In the above example, it works well in validating against the set_up_all ruleset. Now I would like to combine several rulesets and validate against all of them together.
According to the documentation, the package offers a way to merge rulesets. I just can't figure out how to integrate the example provided into my current flow.
According to the docs, I need to implement this line:
$mergedRules = $post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property');
This was my attempt, but it didn't work:
if($mergedRules->isValid()) { ...
I get the following error:
Call to a member function isValid() on array
I also tried this, but that didn't work either:
if($post->isValid($mergedRules)) { ...
I get the following error:
array_key_exists(): The first argument should be either a string or an integer
Any suggestions on how I would implement the merging rulesets?
From what I can see - mergeRulesets() returns an array of rules.
So if you do this - it might work:
$post = new Post(Input::all());
$post->setRules($post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property'));
if($post->isValid()) {
///
}
I've released an update version of the package for Laravel 4.2 (0.10.7) which now allows you to pass your rules to the isValid() method to validate against them.
$post->isValid($mergedRules);
The other answers will work, but this syntax is nicer (and won't override the existing rules on the model).

How can I share validation arrays between models?

For example, I have several models that have name and uid fields but some models do not have them. I want all models that have those fields to use the following rules, however, I don't want to add the following block of code to each model.
public $validate = array(
'name' => array(
array(
'rule' => array('between', 1, 25),
'message' => 'Name must contain %d to %d characters',
'required' => true
),
array(
'rule' => array('custom', AppModel::REGEX_NAME),
'message' => 'Name contains invalid characters.'
)
),
'uid' => array(
'rule' => 'uuid',
'message' => 'uid is not valid.',
'required' => true
),
);
I've considered adding the rules to AppModel by setting public $validate. This leads to the following problems.
Models without those fields always fail validation because required is true.
If you set public $validate in a model, it will not inherit the rules from AppModel.
I'm sure this can be handled by adding validation on-the-fly (I'm thinking beforeValidate() in AppModel) but I would like to know how others are handling this.
Does anyone know a better way?
Personally I would just copy/paste the validation array in each model.
If that isn't what you want to do, you could do something like extend AppModel to MyAppModel. Within MyAppModel set public $validate = array(...), and for the models needing those validation rules simply extend MyAppModel instead of AppModel in your class declaration.
As for problem #2, you need to call parent::validate to retrieve that array and then supplement it with further rules. My initial thought would be to create an array for the new rules not found in MyAppModel and then array_merge with that array + parent::validate.
This is usually handled by just copy/pasting the validation array or setting up your "normal" validation rules into your bake template. That way, they're there by default upon starting each project.
Or per the comment above, you can use a Behavior - though unless you have a LOT of models, personally, I think that's overkill.
You could add a method like mergeDefaultRules() to your AppModel that can be called from
within each models beforeValidate() that merges the default rules
with the model specific rules
You could use traits instead of using an AppModel method for that - if you can use php 5.4
You could use a behavior
I would go for 1 or 2.
// In your AppModel
public function mergeDefaultRules() {
$this->validate = array_merge($this->validate, array(/* Default rules here */));
}
// In your specific model
public function beforeValidate($options = array()) {
$this->mergeDefaultRules();
return true;
}

Categories