Laravel 5.1: custom validation with custom error message - php

I have custom validation which I added to my form request as:
public function rules()
{
return [
...
"firstCheckbox" => "required_with_one_of:secondCheckbox,thirdCheckbox",
...
];
}
Rule is that if the user selects firstCheckbox then he needs to select at least one more checkbox from given parameters: secondCheckbox or thirdCheckbox.
To make this happen I added to AppServiceProvider.php's boot function following code:
Validator::extend('required_with_one_of', function($attribute, $value, $parameters, $validator)
{
$data = $validator->getData();
foreach ($parameters as $p) {
if ( array_get($data,$p) != null) {return true;}
}
return false;
});
Validator::replacer('required_with_one_of', function($message, $attribute, $rule, $parameters) {
return str_replace(':values', implode(' / ', $parameters), $message);
});
All of this is working fine but if I'm not thinking of something please point it out so I can fix it.
My real problem is with the message. The message I receive is:
"You must select at least one of the fields: secondCheckbox, thirdCheckbox"
But what I want is to get message:
"You must select at least one of the fields: Second Checkbox, Third Checkbox"
To my lang\en\validation.php attributes[] I have added
'secondCheckbox' => 'Second Checkbox'
'thirdCheckbox' => 'Third Checkbox'
values, but this doesn't work because I never replace $parameters values in the Validator::replacer().
I have looked at Validator class and there is simple function that handle all of this
$parameters = $this->getAttributeList($parameters);
but I can't call that from Validator::replacer().
Is there a simple way to call this function from Validator::replacer() or should I just create CustomValidator class extending the Validator.
If the second way is the way to go can someone give me instructions how to do this. I've never really used PHP at this level so, although I have rough idea what to do, I'm not really sure how to do this.

Related

Laravel validation rule "different"

I am having a hard time understanding this validation rule. Basically, I have two fields, and they are both nullable. But, once both fields are filled, they have to be different from each other. I cannot enter test in both of them, for example. This validation rule works, if I fill both fields.
But, when I only fill one of the fields, the validation fails and says the fields should be different from each other with the following message:
The name and replace must be different.
I checked what is being submitted to my Form Request, and this is the following:
"name" => null
"replace" => "test"
Stripped version of my validation rules:
public function rules()
{
return [
'name' => 'different:replace|nullable',
'replace' => 'different:name|nullable',
];
}
Can somebody explain to me what I am misunderstanding with this validation rule? Do null values not count with this rule?
If you take a look at the validateDifferent function from Illuminate\Validation\Concerns\ValidatesAttributes (vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php:432) rule:
public function validateDifferent($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'different');
foreach ($parameters as $parameter) {
$other = Arr::get($this->data, $parameter);
if (is_null($other) || $value === $other) {
return false;
}
}
return true;
}
As you can see in the if case, the rule will fail if the other value is null.
if (is_null($other) || $value === $other)

Lumen provide a code for validation errors

Currently in lumen when you use the $this->validate($request, $rules) function inside of a controller it will throw a ValidationException with error for your validation rules(if any fail of course).
However, I need to have a code for every validation rule. We can set custom messages for rules, but I need to add a unique code.
I know there's a "formatErrorsUsing" function, where you can pass a formatter. But the data returned by the passed argument to it, has already dropped the names of the rules that failed, and replaced them with their messages. I of course don't want to string check the message to determine the code that should go there.
I considered setting the message of all rules to be "CODE|This is the message" and parsing out the code, but this feels like a very hacked solution. There has to be a cleaner way right?
I've solved this for now with the following solution:
private function ruleToCode($rule) {
$map = [
'Required' => 1001,
];
if(isset($map[$rule])) {
return $map[$rule];
}
return $rule;
}
public function formatValidationErrors(Validator $validator) {
$errors = [];
foreach($validator->failed() as $field => $failed) {
foreach($failed as $rule => $params) {
$errors[] = [
'code' => $this->ruleToCode($rule),
'field' => $field,
];
}
}
return $errors;
}

How to pass variable to Laravel Validator Rule

The Issue
Consider this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile,:country_code'
);
In the above example, the value of country_code needs to change dependant on a variable defined prior to the validation taking place.
With this in mind, is it possible to pass a variable into a Laravel Validation rule?
Please bear in mind, this is how I call my validation:
if(!$this->some_validator->with($data)->passes()){
// Get the validation errors and throw the exception
$error_info = $this->some_validator->formatErrorMessages();
throw new ExampleException(ExampleExceptionType::$VALIDATION_ERROR,$error_info);
}
You are allowed to create custom validation rules.
It looks like this:
Validator::extend('country_code', function($attribute, $value, $parameters, $validator) {
if ($value === 'US') {
return false;
}
return true;
});
And then just use it like this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile|country_code'
);
After re-reading your message I realized that you meant a bit different thing. But take a look at the $parameters argument.
And read documentation. It's quite well covered there https://laravel.com/docs/5.3/validation#custom-validation-rules
With the new way of doing this, if you extend a new rule and you have a variable that you want to pass to the closure, you can do it like this:
Validator::extend(nameOfTheRule, function ($attribute, $value, $parameters, $validator) use ($priorVariable) {
//code
}

Laravel custom validation - get parameters

I want to get the parameter passed in the validation rule.
For certain validation rules that I have created, I'm able to get the parameter from the validation rule, but for few rules it's not getting the parameters.
In model I'm using the following code:
public static $rules_sponsor_event_check = array(
'sponsor_id' => 'required',
'event_id' => 'required|event_sponsor:sponsor_id'
);
In ValidatorServiceProvider I'm using the following code:
Validator::extend('event_sponsor', function ($attribute, $value, $parameters) {
$sponsor_id = Input::get($parameters[0]);
$event_sponsor = EventSponsor::whereIdAndEventId($sponsor_id, $value)->count();
if ($event_sponsor == 0) {
return false;
} else {
return true;
}
});
But here I'm not able to get the sponsor id using the following:
$sponsor_id = Input::get($parameters[0]);
As a 4th the whole validator is passed to the closure you define with extends. You can use that to get the all data which is validated:
Validator::extend('event_sponsor', function ($attribute, $value, $parameters, $validator) {
$sponsor_id = array_get($validator->getData(), $parameters[0], null);
// ...
});
By the way I'm using array_get here to avoid any errors if the passed input name doesn't exist.
http://laravel.com/docs/5.0/validation#custom-validation-rules
The custom validator Closure receives three arguments: the name of the
$attribute being validated, the $value of the attribute, and an array
of $parameters passed to the rule.
Why Input::get( $parameters ); then? you should check $parameters contents.
Edit.
Ok I figured out what are you trying to do. You are not going to read anything from input if the value you are trying to get is not being submitted. Take a look to
dd(Input::all());
You then will find that
sponsor_id=Input::get($parameters[0]);
is working in places where sponsor_id was submited.

Laravel validation on checkbox which named as array

I've some areas in my form something like:
<ul>
<li>
<input type="checkbox" name="post_categories[]" value="16">English First Main Category<br>
<ul>
<li><input type="checkbox" name="post_categories[]" value="17">English First Subcategory<br></li>
</ul>
</li>
</ul>
When I try to validate them as required fields or something else, Laravel did not validate rules. My rules something like below (In /application/models/posts.php):
public static $rules = array(
'post_title' => 'required',
'post_body' => 'required',
'content_language'=>'required|alpha',
'post_categories'=>'array_numeric',
'post_sequence_number'=>'numeric'
);
public static function validate($data){
return Validator::make($data, static::$rules);
}
In /application/library/validate.php I've a function that validates the array is numeric or not:
Class Validator extends Laravel\Validator {
public function validate_array_numeric($attribute, $value, $parameters){
$numeric_values = array_filter($value, create_function('$item', 'return (is_numeric($item));'));
return count($numeric_values) == count($value);
}
}
Rules works perfectly, except post_categories[]. I get the error:
Method [array_numeric] does not exist.
Cheers.
I had to solve the same problem. Here's what I did:
I created a custom class that extends the default Laravel\Validator class. I wanted to be able to tell the validator when I'm dealing with multiple values (like in your case). In my implementation this could be done by appending '_array' to every validation rule for a certain field name. What my class does is to check if the rule name has this suffix and if it does the value parameter (which is an array in this case) is broken down to its contained items and passed to the default validation functions in Laravel.
<?php
class Validator extends Laravel\Validator {
public function __call($method, $parameters)
{
if (substr($method, -6) === '_array')
{
$method = substr($method, 0, -6);
$values = $parameters[1];
$success = true;
foreach ($values as $value) {
$parameters[1] = $value;
$success &= call_user_func_array(array($this, $method), $parameters);
}
return $success;
}
else
{
return parent::__call($method, $parameters);
}
}
protected function getMessage($attribute, $rule)
{
if (substr($rule, -6) === '_array')
{
$rule = substr($rule, 0, -6);
}
return parent::getMessage($attribute, $rule);
}
}
As stated in the posts above you will have to make the following change so that your custom Validator class can be found by the Autoloader:
Then you need to remove the following line from
application/config/application.php file:
'Validator' => 'Laravel\Validator'
When this is done you'll be able to use all of Laravel's validation rules with the '_array' suffix. Here's an example:
public static $rules = array(
'post_categories'=>'required_array|alpha_array'
);
I don't know if this issue has been solved in Laravel 4. Maybe you can try it.
But what I'm doing right now is extending the validation class.
You can create a new library that extends the validation class.
To validate if all the items in the array has numeric values. This is in application/libraries:
class Validator extends Laravel\Validator {
public function validate_arraynumeric($attribute, $value, $parameters){
$numeric_values = array_filter($value, create_function('$item', 'return (is_numeric($item));'));
return count($numeric_values) == count($value);
}
}
To change the default error message when the validation fails. Go to application/language/en/validation.php. Just use the name of the function as the key for the new array item:
"arraynumeric" => "The :attribute contains non-numeric values",
update
Then you need to remove the following line from application/config/application.php file:
'Validator' => 'Laravel\\Validator'
To use the new validation:
public static $rules = array(
'post_categories'=>'array_numeric'
);
Now for the required checkbox. I assume you're just requiring one checkbox to be checked. You can just check in the function for the new validation the count of the checked checkboxes if it is at least 1.
You're doing something strange here.
Using post_categories[] as form names generates an array. This means you cannot validate it with 'post_categories[]'=>'required|numeric'. Instead you have to loop through the array and validate each field on it's own.

Categories