How to add a custom validation method in Laravel - php

As we know that in Laravel, we can validate a request by $request->validate() method that automatically redirect the user if validation failed.
I am trying to develop a function with a similar feature only difference is that, instead of redirecting the user, this function will send a JSON response on validation failure.
Here is my code
<?php
namespace App\Libs;
use Illuminate\Http\Request as RequestCore;
use Illuminate\Support\Facades\Validator;
class Request extends RequestCore {
public function validateAjax($data){
$input = $this->all();
$validator = Validator::make($input,$data);
if ($validator->fails()) {
return response()->json([
"errors" => $validator->errors(),
"message" => "Invalid Input"
], 401);
}
return true;
}
}
Problem is, when i run the code, It doesn't stop on error. What am i missing?

You should make new Request class (php artisan make:request ExampleRequest). And after add this function in that class for JSON response
protected function failedValidation(Validator $validator)
{
$errors = (new ValidationException($validator))->errors();
throw new HttpResponseException(
response()->json(['status' => 'error', 'errors' => $errors], JsonResponse::HTTP_UNPROCESSABLE_ENTITY)
);
}

Related

Call to a member function fails() on array

I have a problem with the laravel validation.
Call to a member function fails() on array
Symfony\Component\Debug\Exception\FatalThrowableError thrown with message "Call to a member function fails() on array"
Stacktrace:
`#0 Symfony\Component\Debug\Exception\FatalThrowableError in
C:\laragon\www\frontine\app\Http\Controllers\authController.php:37
public function postRegister(Request $request)
{
$query = $this->validate($request, [
'user' => 'string|required|unique:users|min:4|max:24',
'email' => 'email|string|required|unique:users',
'pass' => 'string|required|min:8',
'cpass' => 'string|required|min:8|same:pass',
'avatar' => 'image|mimes:jpeg,jpg,png|max:2048',
]);
if ($query->fails())
{
return redirect('/registrar')
->withErrors($query)
->withInput();
}
}
The error is because what the ->validate() method returns an array with the validated data when applied on the Request class. You, on the other hand, are using the ->fails() method, that is used when creating validators manually.
From the documentation:
Manually Creating Validators
If you do not want to use the validate method on the request, you may
create a validator instance manually using the Validator facade. The
make method on the facade generates a new validator instance:
use Validator; // <------
use Illuminate\Http\Request;
class PostController extends Controller
{
public function store(Request $request)
{
$validator = Validator::make($request->all(), [ // <---
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
}
The ->fails() is called in the response of the Validator::make([...]) method that return a Validator instance. This class has the fails() method to be used when you try to handled the error response manually.
On the other hand, if you use the validate() method on the $request object the result will be an array containing the validated data in case the validation passes, or it will handle the error and add the error details to your response to be displayed in your view for example:
public function store(Request $request)
{
$validatedData = $request->validate([
'attribute' => 'your|rules',
]);
// I passed!
}
Laravel will handled the validation error automatically:
As you can see, we pass the desired validation rules into the validate
method. Again, if the validation fails, the proper response will
automatically be generated. If the validation passes, our controller
will continue executing normally.
What this error is telling you is that by doing $query->fails you're calling a method fails() on something (i.e. $query) that's not an object, but an array. As stated in the documentation $this->validate() returns an array of errors.
To me it looks like you've mixed a bit of the example code on validation hooks into your code.
If the validation rules pass, your code will keep executing normally;
however, if validation fails, an exception will be thrown and the
proper error response will automatically be sent back to the user. In
the case of a traditional HTTP request, a redirect response will be
generated, [...]
-Laravel Docs
The following code should do the trick. You then only have to display the errors in your view. You can read all about that, you guessed it, in... the docs.
public function postRegister(Request $request)
{
$query = $request->validate($request, [
'user' => 'string|required|unique:users|min:4|max:24',
'email' => 'email|string|required|unique:users',
'pass' => 'string|required|min:8',
'cpass' => 'string|required|min:8|same:pass',
'avatar' => 'image|mimes:jpeg,jpg,png|max:2048',
]);
}

Laravel custom validation and ajax

The validation on my Laravel 4.2 project was done with Ardent package. After going to Laravel 5.5 I have eliminated Ardent and wanted to do Laravel's native validation with form requests.
The problem I have is that the Ajax call was validated before like this:
public function postRegisterAjax(A)
{
try {
...
} catch (ExceptionBag $e) {
$msg = $e->getMessageBag()->all(':message');
$status = Status::ERROR;
}
return $this->responseJson($status, $msg);
}
Now I introduced UserValidationRequest class and I would like Ajax call to throw me an error message without the need to reload the page. In order to do that, I need to forward status and message as Json response.
I somehow tried to do that with after validation hooks, but it doesn't work:
protected function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
if ($validator->fails()) {
\Log::info($validator->errors());
$msg = $validator->errors();
$status = Status::ERROR;
return response()->json(['response' => [
'status' => $status,
'msg' => $msg,
]]);
}
return $validator;
}
The code fails on return response() saying that Method passes does not exist (Illuminate/Support/Traits/Macroable.php:96).
Does anyone know what seems to be the issue?
From Laravel 5.x version (not sure), failedValidation() method was introduced in form requests instead of what Laravel 4.x had as response().
I resolved my issue by tailoring the response to my needs by overriding that method in my form request:
public function failedValidation(Validator $validator)
{
if ($validator->fails()) {
$status = Status::ERROR;
throw new HttpResponseException(response()->json(["response" => [
'msg' => $validator->errors()->all(':message'),
'status' => $status
]]));
}
return response()->json(["response" => [
'msg' => 'User successfully registered',
'status' => Status::SUCCESS
]]);
}

Api validation in laravel 5.4

Hi guys I'm working on API but I need to validate some data at backend, in this case, we only need a postman to send parameters and headers.
All is working fine now but I need more useful data in each request, Laravel has by default a Validator Form Request Validation, but I donĀ“t know how to use at API side.
I need to send the error message in JSON to Postman.
I found two options, one, make validations into a controller but seems to much code and the other make a php artisan make:request StoreObjectPost and then import the request into the controller and change the request of the store method, which one do you recommend, Ty!
You could instantiate the validator yourself like this:
$validator = Validator::make($request->all(), [
'name' => 'min:5'
]);
// then, if it fails, return the error messages in JSON format
if ($validator->fails()) {
return response()->json($validator->messages(), 200);
}
$PostData = Input::all();
$Validator = Validator::make(array(
'name' => $PostData['name']
), array(
'name' => 'required'
));
if ($Validator->fails()) { //if validator is failed
return false;
} else {
// do some thing here
}
Hope this may help you
You should override response(array $errors) method of FormRequest.
public function response(array $errors)
{
//Format your error message
if ($this->expectsJson()) {
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
I prefer the second option. We will be able to keep our controller clean that way, even when we use many validations for the data or using custom error message in the validation

Laravel validator throws an exception instead of redirecting back

After I upgraded to Laravel 5.2 I encountered a problem with the laravel validator. When I want to validate data in a controller take for example this code.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class ContactController extends Controller
{
public function storeContactRequest(Request $request)
{
$this->validate($request, [
'_token' => 'required',
'firstname' => 'required|string'
'lastname' => 'required|string'
'age' => 'required|integer',
'message' => 'required|string'
]);
// Here to store the message.
}
}
But somehow when I enter unvalid data it will not redirect me back to the previous page and flash some messages to the session but it will trigger an exception and gives me a 500 error page back.
This is the exception I get.
I have read in the documentation that the ValidationException is new instead of the HttpResponseException but I don't know if it has anything to do with this.
[2016-01-05 11:49:49] production.ERROR: exception 'Illuminate\Foundation\Validation\ValidationException' with message 'The given data failed to pass validation.' in /home/vagrant/Code/twentyre-webshop/vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesRequests.php:70
And when I use a seperate request class it will just redirect back with the error messages. It seems to me only the validate method used in a controller is affected by this behaviour.
Update your App\Exceptions\Handler class
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Validation\ValidationException;
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
I also recommend you to read the docs how to migrate to laravel 5.2, because there were some breaking changes. For example this, ValidatesRequests trait throws
Illuminate\Foundation\Validation\ValidationException instead of Illuminate\Http\Exception\HttpResponseException
Documentation how to migrate from Laravel 5.1 to 5.2
Example from laravel docs. You can use Validator facade, for custom validation fails behaviour
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
This is how I handle it in Laravel 5.3 (by modifying Handler.php)
https://stackoverflow.com/a/42852358/3107185
For laravel 5.2 I had to add this line:
if ($e instanceof ValidationException)
{
return redirect()->back()->withInput();
}
In App\Exceptions\Handler.php,and the following headers:
use Illuminate\Session\TokenMismatchException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\AuthenticationException;
For my purpose, I was bulding a fully API based application in Laravel 5.3 which I had manually upgraded from Laravel 5.1. and I just needed Laravel to respond back with the validation errors that needed fixing on my FormRequest.
Adding this line:
elseif ($e instanceof ValidationException)
{
return $this->convertValidationExceptionToResponse($e, $request);
}
after this one:
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
In App\Exceptions\Handler.php did the trick for me and returned expected validation errors when using FormRequest validation.
Please see my comments here: #ratatatKE's comments on github
might save someone time, Another issue is that you are calling validator->validate() in the view, not in the controller
i was calling in the view because i have a lazy load component that triggered on the view
I had the same problem when upgrading 4.2 to 5.3.
This answer worked for me.
Override the method in app/Exceptions/Handler.php
protected function convertExceptionToResponse(Exception $e)
{
if (config('app.debug')) {
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
return response()->make(
$whoops->handleException($e),
method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500,
method_exists($e, 'getHeaders') ? $e->getHeaders() : []
);
}
return parent::convertExceptionToResponse($e);
}
Answer found here: https://laracasts.com/discuss/channels/laravel/whoops-20-laravel-52

REST API in Laravel when validating the request

I'm currently trying out on how to build a RESTful API with Laravel and I'm currently in the process of creating a new user. This is just a test and I'm getting some result when trying to validate the request using validation in Laravel; here is the result:
I've been trying to create a new one by this code:
public function store()
{
$validation = Validator::make(Request::all(),[
'username' => 'required|unique:users, username',
'password' => 'required',
]);
if($validation->fails()){
} else{
$createUser = User::create([
'username' => Request::get('username'),
'password' => Hash::make(Request::get('password'))
]);
}
}
but then I don't know how to return the error in validation. But it keeps on giving me that HTML as showed in the image when I was trying to do the if with validation->fails(). Is there a way to get the validation in JSON format?
these code will help you, working for me.
$response = array('response' => '', 'success'=>false);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$response['response'] = $validator->messages();
} else {
//process the request
}
return $response;
You should probably return errors (which is an instance of Illuminate\Support\MessageBag) and encode that. A MessageBag instance allows you to convert it directly to its JSON representation.
$errors = $validation->errors();
return $errors->toJson();
Now not to toot my own horn but I've recently developed a RESTful API package for Laravel which does all of this for you and all you need to do is throw a simple exception. See my dingo/api package and the Wiki on returning errors. Basically, instead of returning the errors you would throw an exception.
throw new Dingo\Api\Exception\StoreResourceFailedException('Could not create a new user.', $validation->errors());
It would be represented by the following JSON.
{
"message": "Could not create a new user.",
"errors": {
"username": ["The username is already in use."]
}
}
Laravel provides out of the box a validation method that you can call from your Controller.
if you check the Laravel Controller abstract class you will find it uses a trait called ValidatesRequests
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
So you can use a method $this->validate(Request $request, array $rules); as you long as your controller class extends the Controller
the full method declaration is
public function validate(Request $request, array $rules, array $messages = [], array $customAttributes = [])
{
$validator = $this->getValidationFactory()->make($request->all(), $rules, $messages, $customAttributes);
if ($validator->fails()) {
$this->formatValidationErrors($validator);
}
}
If The $validator fails, the method will throw an error depending on the request type, if it is ajax (in this case you should include in the request headers (Accept application/json) it will return a JSON response containing the validation errors.
For laravel 5.5 and up, see docs: AJAX Requests & Validation
TL;DR: On failed validation a json response with a 422 is returned along with the validation error messages. It took me a bit of time to find those validation errors in the response object, so to see the error messages if you're using axios, try this in your browser console:
axios.post('/api/your-route-here')
.then(response => {
console.log(response.data);
}).catch(error => {
console.log(error.response.data.errors)
});
There are many ways to get a validator response first is to get an all validation error at the same time i.e you will get a response like below
$validator = \Validator::make($request->all(), [
'username' => 'required|unique:users, username',
'password' => 'required',
]);
if ($validator->fails()) {
$responseArr = CustomHelper::returnRespArr("");
$responseArr['message'] = $validator->errors();;
$responseArr['token'] = '';
return response()->json($responseArr, Response::HTTP_BAD_REQUEST);
}
Response you will get is:
{
"status": false,
"data": [],
"message": {
"username": [
"The username field is required."
],
"password": [
"The password field is required."
]
},
"is_valid": 0,
"token": ""
}
The second way to get a validation response. In this, you will get a one validator error a time.
if ($validator->fails()) {
$responseArr = CustomHelper::returnRespArr("");
$responseArr['message'] = $validator->messages()->first();;
$responseArr['token'] = '';
return response()->json($responseArr,Response::HTTP_BAD_REQUEST);
}
The response you will get
{
"status": false,
"data": [],
"message": "The username field is required.",
"is_valid": 0,
"token": ""
}
I am using Laravel 9.x and found a quite simple way to validate errors with REST APIs:
public function store(Request $request)
{
$input = $request->all();
$validator = Validator::make($input, [
'title' => 'required|string|max:50'
]);
// Will return an error, if validation fails.
// https://laravel.com/api/9.x/Illuminate/Foundation/Validation/ValidatesRequests.html#method_validateWith
$this->validateWith($validator, $request);
// Only use the properties that were validated.
$input = $validator->validated();
// Create a new event model, with the data provided.
$event = Event::create($input);
return new EventResource($event);
}
In order to return a json error message, make sure to set the Accept header of the client to application/json. I make the mistake to not set this in my debug client, so I only saw html/xml messages.
You can also force the output to json.

Categories