How can I make a custom rule using Validation, so that the field can be nullable 'since' call function the result is true, otherwise, the field becomes required.
Of course I tried to use the 'nullable', but even if the field is empty, the Validation should execute the checkAreasDiff() function to validate that the field can be empty during the update.
In my controller, I created a function:
private function validator_update(array $data) {
\Validator::extend('areas_diff', function($attribute, $value, $parameters, $validator) {
return checkAreasDiff();
}, 'VALIDATOR AREAS_DIFF OK.');
/**
* RULES
*/
$rules = [
'fiscalizoarea' => 'areas_diff',
];
/**
* Return \Validator
*/
return \Validator::make($data, $rules, $msgs);
}
If I understand the question correctly, you want one field to be required only if another is not null?
There is a Laravel rule for that already: required_with.
required_with:foo,bar,...
The field under validation must be present and not empty only if any
of the other specified fields are present.
Or, if I'm getting your logic back to front: required_without
required_without:foo,bar,...
The field under validation must be present and not empty only when any
of the other specified fields are not present.
I have the following custom validation rule...
Validator::extend('empty_with', function ($attribute, $value, $parameters, $validator) {
$other = array_get($validator->getData(), $parameters[0], null);
return ($value != '' && $other != '') ? false : true;
}, "The :attribute field is not required with the :other field.");
And am using it like...
$validator = Validator::make($request->all(), [
'officer' => 'sometimes|integer',
'station' => 'empty_with:officer,|integer',
]);
The current error message am getting is
The station field is not required with the:otherfield.
Versus what I would like to have;
The station field is not required with the officer field.
How do I set a the second parameter 'officer' in the error message, the same way :attribute is...??
You'll need to add in a custom replacer to go with your custom validation rule. See 'Defining the error message' here.
\Validator::replacer('empty_with', function ($message, $attribute, $rule, $parameters) {
return str_replace(':other', $parameters[0], $message);
});
This code tells Laravel that when the empty_with rule fails, the message should be run through that closure before being passed back to the user. The closure performs a simple string replacement and returns the amended error message.
For the most part, each validation rule has its own replacement rules for messages since it's dependent on the specific attributes and their order. Although :other being replaced with the first parameter happens for a few rules, it's not automatic and is defined explicitly for each rule that uses it. It's worth looking in the Illuminate\Validation\Concerns\ReplacesAttributes trait to get an idea of how Laravel deals with replacement for its built-in rules.
I have write this function rule in CustomRequest to check checkHackInputUser rule that define in provider:
Actually i want to check the value that pass in route
for example :
http://www.somedomain.com/user/{id}
I what do some operation on this $id variable
with my checkHackInputUser rule
Here is CustomRequest:
public function rules()
{
$request_id = $this->route('user');
$rules = [];
if($this->method() == "DELETE" || $this->method() == "GET" )
$rules = [
'role_list' => 'required|checkHackInputUser:'.$request_id,
];
return $rules;
}
The problem is,this rule(checkHackInputUser) doesn't work if i remove required role.
Here is the checkHackInputUser validation function in provider:
public function boot()
{
$this->app['validator']->extend('checkHackInputUser',function($attr,$value,$params){
//Some validation
return false or true;
});
}
You can conditionally validate input when present using sometimes.
In some situations, you may wish to run validation checks against a field only if that field is present in the input array. To quickly accomplish this, add the sometimes rule to your rule list:
$v = Validator::make($data, [
'email' => 'sometimes|required|email',
]);
Is there any way to dynamically retrieve a list of "legal" validation rules? I'm trying to have my models self-validate their own validation rule string, to make sure it is accurate. i.e. to make sure someone didn't type "requierd".
I see getRules() at http://laravel.com/api/class-Illuminate.Validation.Validator.html, but that only returns ruled used within the validation, not a list of all known rules.
There's no official API to do this, so you'll need to use reflection. If you look at the implementation of the validate method, you'll see that rules are simply methods on the validate object (that's returned from the static call to make)
#File: vendor/laravel/framework/src/Illuminate/Validation/Validator.php
protected function validate($attribute, $rule)
{
//...
$method = "validate{$rule}";
if ($validatable && ! $this->$method($attribute, $value, $parameters, $this))
{
$this->addFailure($attribute, $rule, $parameters);
}
//...
}
This means we can use reflection to grab a list of validate rules. Also, the method names are camel case with a leading capital letter ("studly case" in laravel speak) so we'll need to lower-case/underscore them ("snake case" in laravel speak) to get the actual validation rule name. We'll also identify which rules have : parameters. Unfortunately, there's no way to derive what each rule expects the parameter to be.
$validator = Validator::make(array(), array());
//
$r = new ReflectionClass($validator);
$methods = $r->getMethods();
//filter down to just the rules
$methods = array_filter($methods, function($v){
if($v->name == 'validate') { return false; }
return strpos($v->name, 'validate') === 0;
});
//get the rule name, also if it has parameters
$methods = array_map(function($v){
$value = preg_replace('%^validate%','',$v->name);
$value = Str::snake($value);
$params = $v->getParameters();
$last = array_pop($params);
if($last && $last->name == 'parameters')
{
$value .= ':[params]';
}
return Str::snake($value);
}, $methods);
var_dump($methods);
If a user has added validation rules by extending the validation class, this technique will pickup any custom methods. However, if a user has extended the validation class with the Validation::extend syntax, the technique above will not find those rule. To get those rules, you'll need to do something like this.
Validator::extend('customrule',function($attribute, $value, $parameters){
});
Validator::extend('anothercustom', 'FooValidator#validate');
$validator = Validator::make(array(), array());
$extension_methods = array();
foreach($validator->getExtensions() as $value=>$callback)
{
if(is_string($callback))
{
list($class, $method) = explode('#', $callback);
$r = new ReflectionClass($class);
$method = $r->getMethod($method);
}
else if(is_object($callback) && get_class($callback) == 'Closure')
{
$method = new ReflectionFunction($callback);
}
$params = $method->getParameters();
$last = array_pop($params);
if($last && $last->name == 'parameters')
{
$value .= ':[params]';
}
$extension_methods[] = $value;
}
How can we validate form fields that are arrays? Take a look at the following code
UserPhone Model:
public static $rules= array(
'phonenumber'=>'required|numeric',
'isPrimary'=>'in:0,1'
)
...........
UserController:
$validation = UserPhone::validate(Input::only('phonenumber')));
if($validation->passes())
{
$allInputs = Input::only('phonenumber','tid');
$loopSize = sizeOf($allInputs);
for($i=0;$i<$loopSize;$i++)
{
$phone = UserPhone::find($allInputs['tid'][$i]);
$phone->phonenumber = $allInputs['phonenumber'][$i];
$phone->save();
}
return Redirect::to('myprofile')->with('message','Update OK');
}
else
{
return Redirect::to('editPhone')->withErrors($validation);
}
}
the $validation comes from a BaseModel which extends Eloquent.
In my view:
<?php $counter=1; ?>
#foreach($phones as $thephone)
<section class="col col-12">
<label class="label">Phone Number {{$counter++}}</label>
<label class="input">
<i class="icon-append icon-phone"></i>
{{Form::text('phonenumber[]',$thephone->phonenumber)}}
{{Form::hidden('tid[]',$thephone->id)}}
</label>
</section>
#endforeach
Everything is working fine and I get all the phone numbers I want in the Update Form, but I cannot update the model because the validation fails with the message "Phonenumber must be a number".
I know that there is not a simple solution for validating array form fields and I tried to extend the validator class but with no success.
How can I validate this kind of fields?
Here's the solution I use:
Usage
Simply transform your usual rules by prefixing each. For example:
'names' => 'required|array|each:exists,users,name'
Note that the each rule assumes your field is an array, so don't forget to use the array rule before as shown here.
Error Messages
Error messages will be automatically calculated by the singular form (using Laravel's str_singular() helper) of your field. In the previous example, the attribute is name.
Nested Arrays
This method works out of the box with nested arrays of any depth in dot notation. For example, this works:
'members.names' => 'required|array|each:exists,users,name'
Again, the attribute used for error messages here will be name.
Custom Rules
This method supports any of your custom rules out of the box.
Implementation
1. Extend the validator class
class ExtendedValidator extends Illuminate\Validation\Validator {
public function validateEach($attribute, $value, $parameters)
{
// Transform the each rule
// For example, `each:exists,users,name` becomes `exists:users,name`
$ruleName = array_shift($parameters);
$rule = $ruleName.(count($parameters) > 0 ? ':'.implode(',', $parameters) : '');
foreach ($value as $arrayKey => $arrayValue)
{
$this->validate($attribute.'.'.$arrayKey, $rule);
}
// Always return true, since the errors occur for individual elements.
return true;
}
protected function getAttribute($attribute)
{
// Get the second to last segment in singular form for arrays.
// For example, `group.names.0` becomes `name`.
if (str_contains($attribute, '.'))
{
$segments = explode('.', $attribute);
$attribute = str_singular($segments[count($segments) - 2]);
}
return parent::getAttribute($attribute);
}
}
2. Register your validator extension
Anywhere in your usual bootstrap locations, add the following code:
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new ExtendedValidator($translator, $data, $rules, $messages);
});
And that's it! Enjoy!
Bonus: Size rules with arrays
As a comment pointed out, there's seems to be no easy way to validate array sizes. However, the Laravel documentation is lacking for size rules: it doesn't mention that it can count array elements. This means you're actually allowed to use size, min, max and between rules to count array elements.
It works best to extend the Validator class and re-use the existing Validator functions:
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validation($translator, $data, $rules, $messages);
});
class Validation extends Illuminate\Validation\Validator {
/**
* Magically adds validation methods. Normally the Laravel Validation methods
* only support single values to be validated like 'numeric', 'alpha', etc.
* Here we copy those methods to work also for arrays, so we can validate
* if a value is OR an array contains only 'numeric', 'alpha', etc. values.
*
* $rules = array(
* 'row_id' => 'required|integerOrArray', // "row_id" must be an integer OR an array containing only integer values
* 'type' => 'inOrArray:foo,bar' // "type" must be 'foo' or 'bar' OR an array containing nothing but those values
* );
*
* #param string $method Name of the validation to perform e.g. 'numeric', 'alpha', etc.
* #param array $parameters Contains the value to be validated, as well as additional validation information e.g. min:?, max:?, etc.
*/
public function __call($method, $parameters)
{
// Convert method name to its non-array counterpart (e.g. validateNumericArray converts to validateNumeric)
if (substr($method, -7) === 'OrArray')
$method = substr($method, 0, -7);
// Call original method when we are dealing with a single value only, instead of an array
if (! is_array($parameters[1]))
return call_user_func_array(array($this, $method), $parameters);
$success = true;
foreach ($parameters[1] as $value) {
$parameters[1] = $value;
$success &= call_user_func_array(array($this, $method), $parameters);
}
return $success;
}
/**
* All ...OrArray validation functions can use their non-array error message counterparts
*
* #param mixed $attribute The value under validation
* #param string $rule Validation rule
*/
protected function getMessage($attribute, $rule)
{
if (substr($rule, -7) === 'OrArray')
$rule = substr($rule, 0, -7);
return parent::getMessage($attribute, $rule);
}
}
each()
It's not in the docs, but the 4.2 branch may have a simple solution around line 220.
Just like the sometimes($attribute, $rules, callable $callback) function, there is now an each($attribute, $rules) function.
To use it, the code would be something simpler than a sometimes() call:
$v->each('array_attribute',array('rule','anotherRule')); //$v is your validator
Caveats
sometimes() and each() don't seem to be easily chainable with each other so if you want to do specifically conditioned rules on array values, you're better off with the magic solutions in other answers for now.
each() only goes one level deep which isn't that different from other solutions. The nice thing about the magic solutions is that they will go 0 or 1 level deep as needed by calling the base rules as appropriate so I suppose if you wanted to go 1 to 2 levels deep, you could simply merge the two approaches by calling each() and passing it a magic rule from the other answers.
each() only takes one attribute, not an array of attributes as sometimes() does, but adding this feature to each() wouldn't be a massive change to the each() function - just loop through the $attribute and array_merge() $data and the array_get() result. Someone can make it a pull request on master if they see it as desirable and it hasn't already been done and we can see if it makes it into a future build.
Here's an update to the code of Ronald, because my custom rules wouldn't work with the array extension. Tested with Laravel 4.1, default rules, extended rules, …
public function __call($method, $parameters) {
$isArrayRule = FALSE;
if(substr($method, -5) === 'Array') {
$method = substr($method, 0, -5);
$isArrayRule = TRUE;
}
//
$rule = snake_case(substr($method, 8));
// Default or custom rule
if(!$isArrayRule) {
// And we have a default value (not an array)
if(!is_array($parameters[1])) {
// Try getting the custom validation rule
if(isset($this->extensions[$rule])) {
return $this->callExtension($rule, $parameters);
}
// None found
throw new \BadMethodCallException("Method [$method] does not exist.");
} // Array given for default rule; cannot be!
else return FALSE;
}
// Array rules
$success = TRUE;
foreach($parameters[1] as $value) {
$parameters[1] = $value;
// Default rule exists, use it
if(is_callable("parent::$method")) {
$success &= call_user_func_array(array($this, $method), $parameters);
} else {
// Try a custom rule
if(isset($this->extensions[$rule])) {
$success &= $this->callExtension($rule, $parameters);
}
// No custom rule found
throw new \BadMethodCallException("Method [$method] does not exist.");
}
}
// Did any of them (array rules) fail?
return $success;
}
There are now array validation rules in case this helps anybody. It doesn't appear that these have been written up in the docs yet.
https://github.com/laravel/laravel/commit/6a2ad475cfb21d12936cbbb544d8a136fc73be97