Validating the key in the data in Lumen - php

I am validating a request that looks like this:
{
"data": [
{
"id": 1,
"name": "Foo",
"values":{
"val1":"This",
"99":"That"
}
}
]
}
Here is my custom messages:
$messages = [
'data.id'=>'is required',
'data.name'=>'is required',
'data.values'=>'must be array',
'data.values.*'=>'must be numeric'
];
My validation rule is this:
$this->validate(
$request,
[
'data'=>'required|array',
'data.*.id'=>'required|numeric',
'data.*.name'=>'required',
'data.*.values'=>'array',
'data.*.values.*'=>'numeric'
],
$messages
);
The rule validates the values in the "values" array. I want to validate the key in the "values" array [val1, 99] instead.

Write a custom validation rule for data.*.values:
'data.*.values' => function($attribute, $value, $fail) {
//$value contains your array of $key => $value pairs for you to loop through
if( /* doesn't pass your rules */){
return $fail('custom validation failed');
}
},

Related

Creating two seperate validation messages for the same attribute

I'm trying to create two separate validation messages for the same validation attribute.
There are two rules that use "before_or_equal" - the end_time has to be "before_or_equal" start_time and also the end_time has to be after 5:00 (time). The validation works, but I can't seem to find a way to create a working custom message for the latter.
I tried to specify the rule by including it literally with the value, but it doesn't seem to work.
This is what the custom request validation looks like for the end_time at the moment.
public function rules()
{
return [
'end_time' => ['after_or_equal:start_time', 'after_or_equal:5:00'],
];
}
public function messages()
{
return [
'end_time.after_or_equal' => 'Message 1',
'end_time.after_or_equal:5:00' => 'Message 2',
];
}
You can use :date for your custom error messages.
Example:
public function rules()
{
return [
'end_time' => ['after_or_equal:start_time', 'after_or_equal:5:00'],
];
}
public function messages()
{
return [
'end_time.after_or_equal' => 'the :attribute time must be after :date',
];
}
The replaced value is actual value of first input of the validator
i don't know if i understand your question correctly, but are you looking for something like this ?
public function rules()
{
return $this->messages("end_time", [
"after_or_equal",
"after_or_equal:5:00",
]);
}
public function messages(string $Key, array $CustomAttributes)
{
$Exceptions = [
"end_time" => [
"after_or_equal" => "Message 1",
"after_or_equal:5:00" => "Message 2"
]
];
$Exception = [
$Key => []
];
foreach ($CustomAttributes as $Attribute) {
array_push($Exception[$Key], $Exceptions[$Key][$Attribute]);
}
return $Exception;
}

Laravel / nested validation

I am building a component-driven API for a basic page-builder system and have hit a stumbling block when it comes to validation.
First, I want to explain the use-case.
if we have a component (for example in Vue) in /components/ProfileCard.vue
<script>
export default {
props: {
name: String,
age: Number,
avatar: String
}
}
</script>
I am creating a component in the backend components.php config:
<?php
return [
'profile' => [
'component' => 'ProfileCard',
'rules' => [
'name' => [
'required',
],
'age' => [
'required',
'number',
],
'avatar' => [
'required',
]
],
],
];
Which checks and validates every time a profile card component is submitted.
Creating a custom validation rule for Component, I am able to say "the ProfileCard component is not valid" but I am not able to merge / nest the validation rules:
Component.php
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Validator;
class Component implements Rule
{
protected $validator = null;
public function passes($attribute, $value)
{
$components = config('components');
$component = $value['component'];
if (isset($components[$component])) {
return false;
}
$c = $components[$component];
$this->validator = Validator::make($value['data'], $c['rules'], $c['messages'] ?? '');
return $this->validator->passes();
}
public function message()
{
if (is_null($this->validator)) {
return 'The component does not exist';
}
return $this->validator->errors();
}
}
Has anybody got any experience doing anything like this or can anybody point me in the right direction towards a solution?
I am ideally looking for a solution which is applicable while using Laravel's FormRequest validation, like so:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Unique;
use App\Rules\Component;
class CreateUserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => [
'required',
'email',
new Unique('users', 'email'),
],
'profile' => [
'required',
new Component(),
]
];
}
}
The data would come in like so:
{
"email": "test#example.com",
"profile": {
"component": "ProfileCard",
"data": {
"name": "Test",
"age": 49,
"avatar": "https://example.com/avatar.png"
}
}
}
I have updated the question with the progress I have made myself, you can return a MessageBag in the messages method on the rule, however, this creates a slight problem, the response comes back as follows:
"message": "The given data was invalid.",
"errors": {
"profile": [
{
"name": [
"The name field is required."
],
"age": [
"The age field is required."
],
"avatar": [
"The avatar field is required."
],
},
":message"
]
}
Clearly this is an improvement but it's still not as usable, we don't have a ':message' and the validation errors are nested in an object in the "profile" array.
Your approach seems like you are over complicating a simple problem. I would never do a validation in a validation rule. Instead do rules that is dependent on the component and adjust it in the form request accordingly. You can easily do nested rules like so.
[
'profile.name' => 'string',
]
Do the rest of the logic in the form request. The strategy is to have rules based on the request input and your config file, based on what you already tried.
public function rules()
{
// i do not know how you determine this key
$componentKey = 'profile';
$rules = [
...,
$componentKey => [
'required',
]
];
$inputComponent= $this->input('profile')['component'];
$components = config('components');
// based on your data this seems wrong, but basically fetch the correct config entry
$component = $components[$inputComponent];
foreach ($component['rules'] as $key => $value) {
$rules[$componentKey . '.' . $key] => $value;
}
return $rules;
}
There is some parts of your code where i can't figure out what your data means, i do not know how you get the component key profile and your code based on the config and the component field seems wrong and should instead do a loop with a where condition. I think that this can get you in the right direction, this solution will solve your message problem and be way simpler.

Laravel Custom Error Validation JSON Response Object to Array

I try to create an API for the registration form if a user does not fill the required field. The validator show error in object format but i need json response in an array format.
$validator = Validator::make($request->all(), [
'name' => 'required',
'mobile' => 'required',
'address' => 'required',
]);
if ($validator->fails()) {
return response()->json(['error'=>$validator->errors()], 401);
}
Current output is
{
"error": {
"name": [
"The name field is required."
],
"mobile": [
"The mobile field is required."
],
"address": [
"The addressfield is required."
]
}
}
Expected output
{
"error": [
"The name field is required.",
"The mobile field is required.",
"The address field is required."
]
}
Correct answer is this one:
$err = array();
foreach ($validator->errors()->toArray() as $error) {
foreach($error as $sub_error){
array_push($err, $sub_error);
}
}
return ['errors'=>$err];
the inner foreach is added because maybe more than one validation condition fails for an input( like : password is too short & too weak).
and Mayank Pandeyz's answer for loop won't iterate because until we add toArray() to the end of $validator->errors().
For getting the expected result, iterate the $validator->errors() using foreach() loop and push all the values in an array and return that array like:
$err = array();
foreach ($validator->errors() as $error)
{
array_push($err, $error);
}
return response()->json(['error'=>$err], 401);

Laravel array validation in Form Request

I can't validate a field, which contains array elements, in Form Request class.
Rules method:
public function rules()
{
return [
"state" => 'required',
"state.0" => 'required',
"state.*" => 'required',
];
}
There is an array in request->all()
"state" => array:1 [
0 => ""
]
Zero element is empty. But validation is successful.
What am I doing wrong?
In order to handle the dynamic fields, you will need to loop through all the posted "items" and add a rule for each.
Here is an updated method demonstrating this:
public function rules() {
$rules = [
'state' => 'required',
];
foreach($this->request->get('state') as $key => $val) {
$rules['state.'.$key] = 'required';
}
return $rules;
}

Customizing The Flashed Error Format - Laravel 5.3

I want to customize the format of flashed error message, that is recieved after i do something like $this->validator($request->all())->validate();
For an ajax request, it responds with:
{
"name": [
"The name field is required."
],
"email": [
"The email field is required."
],
}
But i want it to look like
{
"status": "fail",
"errors": {
"name": [
"The name field is required."
],
"email": [
"The email field is required."
],
}
}
I read the documentation under Customizing The Flashed Error Format and added formatValidationErrors method in my Controller class, but it's not making any difference.
public function formatValidationErrors(Validator $validator)
{
return ['status' => 'fail', 'errors' => $validator->errors()->getMessages()];
}
It's not changing even if i change formatValidationErrors method in the original Illuminate\Foundation\Validation\ValidatesRequests trait
I'm forced have to build this format in all request calls and so there's code-duplication. It would be nice if i could just call $this->validator($request->all())->validate() and it automatically formats as per my requirement.
The way to change it is to create a class that extends Validator and override the addError method. Here's a sample code:
<?php
namespace App\Validators;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\Validator;
class RestValidator extends Validator {
/**
* Add an error message to the validator's collection of messages.
*
* #param string $attribute
* #param string $rule
* #param array $parameters
* #return void
*/
protected function addError($attribute, $rule, $parameters)
{
$message = $this->getMessage($attribute, $rule);
$message = $this->doReplacements($message, $attribute, $rule, $parameters);
$customMessage = new MessageBag();
$customMessage->merge(['code' => strtolower($rule.'_rule_error')]);
$customMessage->merge(['message' => $message]);
$this->messages->add($attribute, $customMessage);
}
}
Now you can structure the validation in your controllers like so:
$rules = [
// Your rules here
];
$attributes = [
// The attributes you're checking here
];
$validator = Validator::make($attributes, $rules);
if ($validator->fails()) {
$errorMessage = [
'status' => 'fail',
'errors' => $validator->errors()
];
return $errorMessage;
}
// The rest of your code goes here
Try this in your model,
public function response(array $errors)
{
if (($this->ajax() && !$this->pjax()) || $this->wantsJson()) {
$errors = array('status' => 'fail', 'errors' => $errors);
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
It will result as you expected,
{
"status": "fail",
"error": {
"name": [
"The name field is required."
],
"email": [
"The email field is required"
]
}
}

Categories