I am building a RESTful API with Laravel 5.1 - When a post request is made, I validate the input, if the input is not valid, I throw an exception.
Current Response Sample:
{
"message": "{\"email\":[\"The email field is required.\"]}",
"status_code": 400
}
How to make my response look like this:
{
"message": {
"email": "The email field is required."
},
"status_code": 400
}
Here's how I throw the exception:
$validator = Validator::make($this->request->all(), $this->rules());
if ($validator->fails()) {
throw new ValidationFailedException($validator->errors());
}
I think the best way to validate the form in laravel is using Form Request Validation .You can overwrite the response method in App\Http\Request.php class.
Request.php
namespace App\Http\Requests;
Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
public function response(array $errors)
{
return $this->respond([
'status_code' => 400 ,
'message' => array_map(function($errors){
foreach($errors as $key=>$value){
return $value;
}
},$errors)
]);
}
/**
* Return the response
*/
public function respond($data , $headers=[] ){
return \Response::json($data);
}
}
You can try this:
$messages = [
'email.required' => 'The :attribute field is required.',
];
$validator = Validator::make($input, $rules, $messages);
Here is a class I use:
<?php
namespace App;
class Hack
{
public static function provokeValidationException($field_messages){
$rules = [];
$messages = [];
foreach($field_messages as $field=>$message){
$rules[$field] = 'required';
$messages[$field. '.required'] = $message;
}
$validator = \Validator::make([], $rules, $messages);
if ($validator->fails()) {
throw new \Illuminate\Validation\ValidationException($validator);
}
}
}
Then any time I need to show custom errors, I do:
\App\Hack::provokeValidationException(array(
'fieldname'=>'message to display',
'fieldname2'=>'message2 to display',
));
I had this same issue and I resolved it by decoding my the $validator->errors() response
return (400, json_decode($exception->getMessage()));
Related
I have a method in parent class
Controller.php
public function invalidError($errors = [], $code = 422)
{
return response()->json([
'errors' => $errors
], $code);
}
I am passing this method the following:
if($validator->fails()) {
return $this->invalidError([
array('key' => 'Some key')
]);
}
When the response comes in the errors message is always empty array like the following:
{
"errors": []
}
What am I missing?
To get the errors array from a validator, use failed()
if($validator->fails()) {
return $this->invalidError($validator->failed());
}
If you wants the messages from the failed rules use messages()
if($validator->fails()) {
return $this->invalidError($validator->messages());
}
For more information, you can check the documentation
I have a situation and unfortunately not sure how to sort it out in proper way. I have below script
$validator = Validator::make(
$request->all(),
[
'game_id' => 'required|integer'
],
$messages
);
if ($validator->fails()) {
$response = $validator->messages();
}else{
$response = $gameService->setStatus($request);
}
Now each game has different type, I wanted to add validation on behalf of type. For example if a game is Task Based then I would add validation for time which would be mandatory only for Task based game otherwise it would be an optional for other types.
I have three types of games
1 - level_based
2 - task_based
3 - time_based
In the type table, each game has type.
So is there any way to add validation? I want to do it, inside validation function.
Thank you so much.
You can write your conditions before the validation.
$data = $request->all();
if ($data['game_id'] == 1) {
$rules = [
// level_based validation
];
} else if($data['game_id'] == 2) {
$rules = [
// task_based validation
];
} else {
$rules = [
// time_based validation
];
}
$validator = Validator::make($data, $rules);
Hope it helps. Cheers.
I would go with the required_if validation rule.
So in your case, will send two fields, the type can be a hidden field for example, then on the game_id you will add
'game_id' => 'required_if:type,1'
and so on.. And of course you can customize the error messages.
Try this code snippet
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class CreateGameRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
try {
$request = $this->request->all();
$rule_array = collect();
$rule1 = [
'game_id' => 'required|integer'
]
$rule_array = $rule_array->merge($rule1);
if(isset($request->task_id))
{
$rule2 = [
'task_id' => 'required|integer'
]
}
$rule_array = $rule_array->merge($rule2);
return $rule_array->all();
} catch (Exception $e) {
return $e;
}
}
public function messages(){
return [
'game_id' => 'Please select valid game',
'task_id' => 'Please select valid task'
];
}
}
then invoke this request class in controller function as
use App\Http\Requests\CreateGameRequest;
public function game(CreateGameRequest $request)
{
}
Before laravel 5.5 I used a form request like this with a customized format :
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
class StoreProductRequest extends FormRequest
{
public function authorize ()
{
return true;
}
public function rules ()
{
return [
'title' => 'required',
'desc' => 'required',
];
}
public function response (array $errors)
{
return response()->json($errors, 200);
}
protected function formatErrors (Validator $validator)
{
$result = ['success' => false, 'msg' => $validator->errors()->first()];
return $result;
}
}
Means when an error occured, only the first error returned as a json format like this :
{
"success" : "false",
"msg" : "title field is required "
}
But seem that in laravel 5.5 in this way could not format error like this.
Now I want to return error exactly same format I mentioned above in json format but I do not know How can
This functionality was changed in Laravel 5.5. From the upgrade guide "A Note On Form Requests":
If you were customizing the response format of an individual form request, you should now override the failedValidation method of that form request, and throw an HttpResponseException instance containing your custom response
Your updated Form Request might look something like this (pseudo-code, not tested):
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
class StoreProductRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required',
'desc' => 'required',
];
}
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(
response()->json(['success' => false, 'msg' => $validator->errors()->first()], 400)
);
}
}
protected function formatErrors (Validator $validator)
{
$result = ['success' => false, 'msg' => $validator->errors()];
return $result;
}
Expanding on #Aken Roberts answer. Since this is an error response for a form, I use the error key (input field name) to display the error beside the input.
You can get the first error key from the keys method. With Laravel 5.7 this works as expected:
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(
response()->json([
'success' => false,
'error' => (object) [
$validator->errors()->keys()[0] => $validator->errors()->first()
]
], 400)
);
}
I have a problem with Laravel 5.4 validator which is I'm trying to validate data comes from a mobile application but when validator fails it redirect to the home page. I need to prevent this redirect and return a json response with validation errors messages
Here's my route.php code
Route::group(['middleware' => 'web'], function() {
Route::post('/userSignUp', [
'uses' => 'UserController#userSignUp',
'as' => 'userSingUp'
]);
});
And this is my controller code
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
class UserController extends Controller
{
public function userSignUp(Request $request){
$fullName = $request->input('fullName');
$email = $request->input('email');
$phone = $request->input('phone');
$password = $request->input('password');
$userType = $request->input('userType');
$profilePic = $request->input('profilePic');
$validator = $this->validate($request, [
'fullName' => 'required|max:255',
'email' => 'required|email',
'phone' => 'required'
]);
if ($validator->fails()) {
return response()->json($validator->messages(), 200);
}
}
}
So can anyone help me solving this issue I need to use a laravel 5.4 validator in a web service for a mobile application so I need to prevent the validator redirecting as it does in the above code it redirecting to home page when validation is failed
thanks in advance
if the validation fails when you call $this->validate($request,$rules) laravel will throw an exception and a failed validation response will be sent back by this method define in Illuminate\Foundation\Validation\ValidatesRequests :
/**
* Create the response for when a request fails validation.
*
* #param \Illuminate\Http\Request $request
* #param array $errors
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->to($this->getRedirectUrl())
->withInput($request->input())
->withErrors($errors, $this->errorBag());
}
So it seems that Laravel does handle that by checking $request->expectsJson() so you need to specify the Accept header in you request to JSON, then a JSON formatted response with code 422 will be returned.
return response()
->json(['name' => 'Abigail', 'state' => 'CA'])
->withCallback($request->input('callback'));
from the official doc https://laravel.com/docs/5.4/responses#json-responses
And maybe try to make your validation in your model directly
class User extends Eloquent
{
private $rules = array(
'fullName' => 'required|max:255',
'email' => 'required|email',
'phone' => 'required|numeric'
);
public function validate($data)
{
// make a new validator object
$v = Validator::make($data, $this->rules);
// return the result
return $v->passes();
}
}
and in your controller you can make
$new = Input::all();
$u = new User();
if ($b->validate($new))
{
}
else
{
}
I'm building an api using laravel, the issue is when the client requests my api by calling create() function, and the create()function will call a getValidatedData() function which I want to return validation errors to the client if validation fails or return the validated data to insert database if validation passes, my getValidatedData function is like below so far
protected function getValidatedData(array $data)
{
// don't format this class since the rule:in should avoid space
$validator = Validator::make($data, [
'ID' => 'required',
'weight' => 'required',
]);
if ($validator->fails()) {
exit(Response::make(['message' => 'validation fails', 'errors' => $validator->errors()]));
}
return $data;
}
I don't think exit() is a good way to return the errors message to clients. are there any other ways I can return the laravel Response to clients directly in an inner function. use throwing Exception?
This was what worked for me in Laravel 5.4
protected function innerFunction()
{
$params = [
'error' => 'inner_error_code',
'error_description' => 'inner error full description'
];
response()->json($params, 503)->send();
}
What you can do is using send method, so you can use:
if ($validator->fails()) {
Response::make(['message' => 'validation fails', 'errors' => $validator->errors()])->send();
}
but be aware this is not the best solution, better would be for example throwing exception with those data and adding handling it in Handler class.
EDIT
As sample of usage:
public function index()
{
$this->xxx();
}
protected function xxx()
{
\Response::make(['message' => 'validation fails', 'errors' => ['b']])->send();
dd('xxx');
}
assuming that index method is method in controller you will get as response json and dd('xxx'); won't be executed
You can use this method
public static function Validate($request ,$rolse)
{
// validation data
$validator = Validator::make($request->all(),$rolse);
$errors = $validator->getMessageBag()->toArray();
$first_error = array_key_first($errors);
if (count($errors) > 0)
return 'invalid input params , ' . $errors[$first_error][0];
return false;
}
in controller :
$validate = ValidationHelper::Validate($request,
['title' => 'required']);
if ($validate)
return response()->json(['message' =>'validation fails' , 'error'=> $validate], 403);