How to validate request value when it should be string or array both valid?
'val' => 'bail|required|string'
'val' => 'bail|required|array'
What would be the the validation expression?
I don't think there is a way to achieve it using the validation rules out of the box. You will need to use Custom Validation Rules to achieve this:
In AppServiceProvider's boot method, add in
public function boot()
{
Validator::extend('string_or_array', function ($attribute, $value, $parameters, $validator) {
return is_string($value) || is_array($value);
});
}
Then you can start using it:
'val' => 'bail|required|string_or_array'
Optionally you can set the custom validation error messages, or using a custom validation class to achieve it. Check out the doc link above for more information.
The solution provided by #lionel-chan is most elegant way but you can also do it by checking type of the variable in order to add the appropriate rule as:
$string_or_array_rule = is_array($inputs['val']) ? 'array' : 'string'
"val" => "bail|required|{$string_or_array_rule}"
only changed #Krezo's answer to ternary operator
'val' => ['bail','required',function($attribute, $value, $fail) {
is_array($value) || is_string($value)?: $fail("Must be array or string");
}],
Just use closure
"val" => [function($attribute, $value, $fail) {
if (!(is_array($value) || is_string($value))) $fail("$attribute must be array or string");
}]
Related
I have a form that posts a structure field as an array. The structure array contains definitions of database table columns.
$validator = Validator::make($request->all(), [
'structure' => 'required|array|min:1',
'structure.*.name' => 'required|regex:/^[a-z]+[a-z0-9_]+$/',
'structure.*.type' => 'required|in:integer,decimal,string,text,date,datetime',
'structure.*.length' => 'nullable|numeric|required_if:structure.*.type,decimal',
'structure.*.default' => '',
'structure.*.index' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_nullable' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_primary' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_auto_increment' => 'required_if:structure.type,integer|boolean',
'structure.*.is_unique' => 'required_if:is_auto_increment,false|boolean',
'structure.*.decimal' => 'nullable|numeric|required_if:structure.*.type,decimal|lt:structure.*.length',
]);
Without going into explanation of all the rules, one thing should be made sure that the length field is always null when the type is not string or decimal as you cannot assign a length to columns other than these types. So, I am trying to use the sometimes method on the $validator instance.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
// how to access the structure type here?
});
My question is inside the closure, how do I make sure that the length is null only for the array element that has the type set to other than string or decimal.
I have tried the dd function and it seems the whole input array is passed to the closure.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
dd($input);
});
Here is the output of the dd method.
I can use a foreach construct but wouldn't that be inefficient? Checking all the elements for a single element?
How do I check the type only for the array element under consideration?
Is there a Laravel way to do this?
How about thinking opposite? if the Type is String or Decimal, the Length field will become Required.
$validator->sometimes('structure.*.length', 'required', function ($input) {
return $input->type == 'string' or $input->type == 'decimal';
});
This is a great question. I took a look at the API for sometimes(). It seems, what you want to do, is currently not possible with it.
A possible alternative could be to use an After Validation Hook. For example:
$validator->after(function ($validator) {
$attributes = $validator->getData()['structure'];
foreach($attributes as $key => $value) {
if(! in_array($value['type'], ['string', 'decimal']) && ! is_null($value['length'])) {
$validator->errors()->add("structure.{$key}.length", 'Should be null');
}
}
});
In Laravel form input validation, you can specify a minimum and a maximum length of a given input field.
$inputRules = [
'postCode' => 'min:3|max:8'
];
Validation outcome
'BA45TZ' = true
'FF' = false
'GH22 55XYZ' = false
However if I do the same for a number, it will validate whether the input is less than or greater than the input, then return on that.
$inputRules = [
'cv2' => 'min:3|max:4'
];
Validation outcome
'586' = false
'4' = true
'2' = false
'3' = true
I actually want to validate a numbers length not it's numerical value. I can't figure out how to do this. Google has been no help, or I am not searching the right thing.
Anybody had any experience with this?
EDIT: I have answered my own question. I had missed Laravels digits_between.
Like an idiot, I missed Laravels digits_between validator rule. I swear I had scoured those rules, but here we are.
Thank you everyone for your help.
$inputRules = [
'cv2' => 'required|numeric|digits_between:3,4'
];
That's the expected behavior of Laravel's size, min and max validations. You can't change it.
For string data, value corresponds to the number of characters. For numeric data, value corresponds to a given integer value. For files, size corresponds to the file size in kilobytes.
To solve this you have to create a custom validation rule.
Something like this:
Validator::extend('min_length', function($attribute, $value, $parameters){
return strlen($value) >= $parameters[0];
});
Validator::extend('max_length', function($attribute, $value, $parameters){
return strlen($value) <= $parameters[0];
});
And you can use them in the same way:
'cv2' => 'min_length:3|max_length:4'
For length in number you've to use size, but you can't put ranges in it. So, the best thing to do is making a custom validation.
Here is the documentation in laravel: http://laravel.com/docs/5.0/validation#custom-validation-rules
And an example:
Validator::extend('minsize', function($attribute, $value, $parameters)
{
return strlen($value) >= $parameters[0];
});
And do the same with maxsize.
I'm wrapping a model function in Yii 1.8 that has the signature:
public save($runValidation=true, array $attributes=NULL)
With a function:
public xSave(array $params)
That allows the addition of a flag and optional message that causes the wrapper function to throw an Exception in the case that the delegated save() function returns false.
I toyed with the idea of overwriting save() with:
public save(
$runValidation=true,
array $attributes=NULL,
$exception_on_error=false,
$exception_message=false
)
but would like to allow the specification of the last two parameters independently from the first and like the idea of allowing the extra readability of passing in an array with string keys.
I have so far:
/**
* Enhanced save function.
* Delegates to standard model save to allow exceptions to be thrown
* in the case where the model was not saved.
**/
public function xSave(array $params=array()){
$_params=array(
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
);
array_merge($_params, $params);
// Call the save method.
$is_saved=$this->save($_params['run_validation'],$_params['attributes']);
// Throw exception to if flag set and model not saved.
if($_params['exception_on_failure'] && !$is_saved){
// If no exception message was passed in, use the default.
if($_params['exception_message'] === false){
throw new CException('
Could not '.($this->isNewRecord()?'create':'update').' '.get_class($this).';
Errors: '.CJSON::encode($this->errors)
);
}
// Else throw using the one provided.
throw new CException($exception_message);
}
// Return result of standard save method.
return $is_saved;
}
Firstly I'd like to know if this is a sensible choice, as I may well use it for other parts of the system. I'm currently not too worried about the typing of the parameters although I agree this could be an issue in the future.
Secondly I would also like the ability to throw an exception in the case that $params has a key that is not defined in $_params with a message specifying that key, and would like to do this as part of the array_merge if possible.
To 1), yes, passing arrays is the usual lame workaround in languages that don't support named arguments (see jQuery etc). With the new array syntax, this is even almost readable:
$some->save([
$runValidation => true,
$attributes => ['foo', 'bar']
]);
Inside a function, you can use extract to avoid ugly $params[foobar] references.
For better taste though, persuade #NikiC to get this patch ready ))
To 2), if you plan to use argument arrays systematically, consider a helper function like this:
function parseArgs($args, $defaults) {
foreach($args as $k => $v) {
if(!array_key_exists($k, $defaults))
throw new Exception("invalid argument: $k");
// might want to add some type checking, like
// if(gettype($v) != gettype($defaults[$k])) bang!
}
return $args + $defaults;
}
Usage:
public function xSave(array $params=array()){
extract(parseArgs($params, [
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
]));
if ($run_validation)
etc....
The decision to either use single parameters or parameter-arrays is opinion based. It depends on the situation. At least I would keep the design consistent across the whole project.
To decide if there had been unknown parameters passed, you can use array_diff():
$a = array(
'test' => 'foo',
'name' => 'bar'
);
$b = array(
'test' => 'foo',
'name' => 'bar',
'abcd' => '123'
);
$d = array_diff(
array_keys($b), array_keys($a)
);
echo "The following keys can't be understood: " . implode(', ', $d) . PHP_EOL;
However, I would skip that check as it will not "harm" if there are unknown parameters.
In laravel, we can get the input value via Input::get('inputname'). I try to change the value by doing this Input::get('inputname') = "new value";. But then, I get the error message saying Can't use function return value in write context.
Is it possible for us change the input value so that when later calling on Input::get('inputname') will get the new amended value?
Thanks.
You can use Input::merge() to replace single items.
Input::merge(['inputname' => 'new value']);
Or use Input::replace() to replace the entire input array.
Input::replace(['inputname' => 'new value']);
Here's a link to the documentation
If you're looking to do this in Laravel 5, you can use the merge() method from the Request class:
class SomeController extends Controller
{
public function someAction( Request $request ) {
// Split a bunch of email addresses
// submitted from a textarea form input
// into an array, and replace the input email
// with this array, instead of the original string.
if ( !empty( $request->input( 'emails' ) ) ) {
$emails = $request->input( 'emails' );
$emails = preg_replace( '/\s+/m', ',', $emails );
$emails = explode( ',', $emails );
// THIS IS KEY!
// Replacing the old input string with
// with an array of emails.
$request->merge( array( 'emails' => $emails ) );
}
// Some default validation rules.
$rules = array();
// Create validator object.
$validator = Validator::make( $request->all(), $rules );
// Validation rules for each email in the array.
$validator->each( 'emails', ['required', 'email', 'min: 6', 'max: 254'] );
if ( $validator->fails() ) {
return back()->withErrors($validator)->withInput();
} else {
// Input validated successfully, proceed further.
}
}
}
If you mean you want to overwrite input data, you can try doing:
Input::merge(array('somedata' => 'SomeNewData'));
Try this,it will help you.
$request->merge(array('someIndex' => "yourValueHere"));
I also found this problem, I can solve it with the following code:
public function(Request $request)
{
$request['inputname'] = 'newValue';
}
Regards
I'm using Laravel 8.
The following is working for me:
$request->attributes->set('name', 'Value');
I used Raham's answer to solve my problem. However, it was nesting the updated data within an array, when I needed it at the same level as other data. I used:
$request->merge('someIndex' => "yourValueHere");
A note other Laravel newbies, I used the merge method to account for an empty checkbox value in a Laravel 7 update form. A deselected checkbox on an update form doesn't return 0, it doesn't set any value in the update request. As a result that value is unchanged in the database. You have to check for a set value and merge a new value if nothing exists. Hope that helps someone.
Just a quick update. If the user doesn't check a box and I need to enter a value in the DB I do something like this in my controller:
if(empty($request->input('checkbox_value'))) {
$request->merge(['checkbox_value' => 0]);
}
I am making an application using Zend Framework 2. I am validating input using it's InputFilter. Is it possible, to make some Inputs required conditionally? I mean I have code like that:
$filter = new \Zend\InputFilter\InputFilter();
$factory = new \Zend\InputFilter\Factory();
$filter->add($factory->createInput(array(
'name' => 'type',
'required' => true
)));
$filter->add($factory->createInput(array(
'name' => 'smth',
'required' => true
)));
I want the field something, to be required, ONLY when type is equal 1. Is there a built-in way to do that? Or should I just create custom validator?
First of all, you may want to enable validation on empty/null values as of Empty values passed to Zend framework 2 validators
You can use a callback input filter as in following example:
$filter = new \Zend\InputFilter\InputFilter();
$type = new \Zend\InputFilter\Input('type');
$smth = new \Zend\InputFilter\Input('smth');
$smth
->getValidatorChain()
->attach(new \Zend\Validator\NotEmpty(\Zend\Validator\NotEmpty::NULL))
->attach(new \Zend\Validator\Callback(function ($value) use ($type) {
return $value || (1 != $type->getValue());
}));
$filter->add($type);
$filter->add($smth);
This will basically work when the value smth is an empty string and the value for type is not 1. If the value for type is 1, then smth has to be different from an empty string.
I couldn't quite get the example by Ocramius to work, as $type->getValue was always NULL. I changed the code slightly to use $context and this did the trick for me:
$filter = new \Zend\InputFilter\InputFilter();
$type = new \Zend\InputFilter\Input('type');
$smth = new \Zend\InputFilter\Input('smth');
$smth
->getValidatorChain()
->attach(new \Zend\Validator\NotEmpty(\Zend\Validator\NotEmpty::NULL))
->attach(new \Zend\Validator\Callback(function ($value, $context){
return $value || (1 != $context['type']);
}));
$filter->add($type);
$filter->add($smth);
You can also use setValidationGroup for this.
Create your own InputFilter class where you set validation groups depending on the data that is set inside the inputfilter before executing the actual validation.
class MyInputFilter extends InputFilter
{
setData($data){
if(isset($data['type']) && $data['type'] === 1){
// if we have type in data and value equals 1 we validate all fields including something
setValidationGroup(InputFilterInterface::VALIDATE_ALL);
}else{
// in all other cases we only validate type field
setValidationGroup(['type']);
}
parent::setData($data);
}
}
This is just a simple example to show what is possible with setValidatioGroup, you can create your own combinations for setting validation groups after your specific needs.
Unfortunately you'd have to set the required option based on your conditions like so:
$filter->add($factory->createInput(array(
'name' => 'smth',
'required' => (isset($_POST['type']) && $_POST['type'] == '1'),
)));