Laravel 5.5 Validation change format of response when validation fails - php

In Laravel 5.4, we created a class that all our requests for validation inherited because we needed to customize our response.
class APIRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return false;
}
/**
* Response on failure
*
* #param array $errors
* #return Response
*/
public function response(array $errors) {
$response = new ResponseObject();
$response->code = ResponseObject::BAD_REQUEST;
$response->status = ResponseObject::FAILED;
foreach ($errors as $item) {
array_push($response->messages, $item);
}
return Response::json($response);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
A sample request that would extend this is shown below
class ResultsGetTermsRequest extends APIRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'school_id' => 'required|integer',
'student_id' => 'required|integer',
];
}
}
And then our sample response on failure would be
{
"status": "FAILED",
"code": "400",
"messages": [
[
"The school id field is required."
],
[
"The student id field is required."
]
],
"result": []
}
However, this doesn't work anymore with Laravel 5.5. I noticed they replaced with response method with failedValidation. This however isn't returning any response when the request isn't validated. If I un-comment the print_r, it is something is returned. It seems the only line that is never executed is the return statement. What am I missing?
public function failedValidation(Validator $validator) {
$errors = (new ValidationException($validator))->errors();
$response = new ResponseObject();
$response->code = ResponseObject::BAD_REQUEST;
$response->status = ResponseObject::FAILED;
foreach ($errors as $item) {
array_push($response->messages, $item);
}
//print_r($response);
return Response::json($response);
}

I guess as per laravel upgrade guide we should return HttpResponseException
protected function failedValidation(Validator $validator)
{
$errors = $validator->errors();
$response = new ResponseObject();
$response->code = ResponseObject::BAD_REQUEST;
$response->status = ResponseObject::FAILED;
foreach ($errors as $item) {
array_push($response->messages, $item);
}
throw new HttpResponseException(response()->json($response));
}

If you want to do this from the FormRequest classes, potentially something like this:
protected function buildResponse($validator)
{
return response->json([
'code' => ResponseObject::BAD_REQUEST,
'status' => ResponseObject::FAILED,
'messages' => $validator->errors()->all(),
]);
}
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator, $this->buildResponse($validator));
}
That would add that response you are building to the validation exception. When the exception handler tries to render this it will check if response was set and if so it will use that response you passed instead of trying to convert the ValidationException to a response itself.
If you want 'ALL' validation exceptions to end up being rendered in this format I might just do this at the exception handler level, as the exception handler already has the ability to convert these exceptions to Json, so you could alter the format in the handler itself and basically not have to make any adjustments to the default FormRequest at all.

If you are in laravel 5+ you can easily achieve this, by overriding the invalid() or invalidJson() method in the App/Exceptions/Handler.php file
In my case, I was developing an API and the api responses should be in a specific format, so I have added the following in the Handler.php file.
/**
* Convert a validation exception into a JSON response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Validation\ValidationException $exception
* #return \Illuminate\Http\JsonResponse
*/
protected function invalidJson($request, ValidationException $exception)
{
return response()->json([
'code' => $exception->status,
'message' => $exception->getMessage(),
'errors' => $this->transformErrors($exception),
], $exception->status);
}
// transform the error messages,
private function transformErrors(ValidationException $exception)
{
$errors = [];
foreach ($exception->errors() as $field => $message) {
$errors[] = [
'field' => $field,
'message' => $message[0],
];
}
return $errors;
}
credit : Origianal Answer

Related

File not uploading when validation with form requests in Laravel

Description
Hi guys,
I have a API endpoint, and im validation incoming data with form requests,
It validated correctly but when i requesting file it says file doesn't exists.
Code
controller method
public function store(StoreRequest $request)
{
$owner = $request->user();
$garage = $owner->garages()->findOrFail($request->garage_id);
$certificate = $garage->certificates()->create($request->validated());
$certificate->addMedia($request->file('image'))->toMediaCollection('certificateImage');
return $this->noContent();
}
FormRequest
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required|filled',
'garage_id' => ['bail', 'required', 'exists:multi-vendor.garages,id', new GarageIsOwn],
'image' => 'required|mimes:jpeg,png,bmp'
];
}
Error
{
"message": "خطای سرور",
"errors": [
"The file \"C:\\xampp\\tmp\\phpAB2C.tmp\" does not exist"
]
}

Laravel FormRequest on PUT request failing

I am building a RESTful API with Laravel 5.8 I am using FormRequests to validate the users input to my POST & PUT requests. The POST works absolutely perfectly, but the PUT request are failing with the following error,
"message": "Too few arguments to function App\Http\Requests\ProjectStoreRequest::Illuminate\Foundation\Providers\{closure}(), 0 passed and exactly 1 expected"
The method my PUT requests gets routed looks like this (and the URL is /api/projects/{id}),
public function update(ProjectStoreRequest $request, $id)
{
$validated = $request->validate();
$project = Project::find($id);
$project->title = $request->title;
$project->due_date = Carbon::parse(strtotime($request->due_date))->format('Y-m-d');
$project->save();
return response()->json(['message' => 'Project updated', 'data' => $project], 200);
}
And the ProjectStoreRequest looks like this,
class ProjectStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required|string',
'due_date' => 'date'
];
}
}

Catching Exceptions in Zend Framework 3

I use the ZF3 skeletion application.
I wonder where I am supposed to catch exceptions globally.
Example:
Right now, if I access an invalid route (mysite.com/invalid-route), the application reports an uncaught expection and HTTP response code 200
Fatal error: Uncaught Zend\View\Exception\RuntimeException: No RouteMatch instance provided
I would expect the build-in 404 error page to be triggered.
What am I missing? Can someone point me to the right direction?
The exception is logged properly using the following code:
class Module implements ConfigProviderInterface
{
const VERSION = '3.0.3-dev';
public function onBootstrap()
{
$logger = new Logger();
$writer = new Writer\Stream(__DIR__ . '/../../../data/log/error.log');
$logger->addWriter($writer);
// Log PHP errors
Logger::registerErrorHandler($logger, true);
// Log exceptions
Logger::registerExceptionHandler($logger);
}
This is something you can catch using a Listener, triggered on the early event MvcEvent::EVENT_ROUTE.
I would suggest using a dedicated class & Factory to separate concerns over the usage of the onBootstrap function. You'd do this by registering and "activating" a Listener class, like so:
'listeners' => [
// This makes sure it "gets listened to" from the very start of the application (onBootstrap)
RouteExistsListener::class,
],
'service_manager' => [
'factories' => [
// This is just what you think it is
RouteExistsListener::class => InvokableFactory::class,
],
],
You can use just the InvokableFactory for this Listener, as there are no special requirements.
class RouteExistsListener implements ListenerAggregateInterface
{
/**
* #var array
*/
protected $listeners = [];
/**
* #param EventManagerInterface $events
*/
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $listener) {
if ($events->detach($listener)) {
unset($this->listeners[$index]);
}
}
}
/**
* #param EventManagerInterface $events
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
$events->attach(MvcEvent::EVENT_ROUTE, [$this, 'doesRouteExist'], 100);
}
/**
* #param MvcEvent $event
*
* #return void|Response
* #throws Exception
*/
public function doesRouteExist(MvcEvent $event)
{
/** #var TranslatorAwareTreeRouteStack|TreeRouteStack $router */
$router = $event->getRouter();
/** #var Request $request */
$request = $event->getRequest();
/** #var RouteMatch|null $routeExists */
$routeExists = $router->match($request); // Return RouteMatch|null
if ($routeExists instanceof RouteMatch) {
return; // Route exists - nothing to do
}
$url = $router->assemble([], ['name' => 'home']); // Name of your redirect route (ie. not_found/404, or something)
/** #var Response $response */
$response = $event->getResponse();
$response->getHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302);
$response->sendHeaders();
$event->getApplication()->getEventManager()->attach(
MvcEvent::EVENT_ROUTE,
function (MvcEvent $event) use ($response) {
$event->stopPropagation();
return $response;
},
-10000
);
return $response;
}
}
NOTE: Used an existing class of my own for the above and modified as I think it should work. However, might contain an error or two ;-) Still, should point you in the right direction I think.

Laravel Changing Request data in Form Request Class

I'm processing a form with multiple date input which is not in A.D. For validation purpose i'm using Form Request.
Before validation and inserting in my database date input must be converted into A.D, so that i can do the proper validation & then if validation succeed date input is stored in A.D .
here is my code for converting date input in A.D
<?php
abstract class Request extends FormRequest
{
public function all()
{
$input = parent::all()
foreach ($input as $key=>$value)
{
if (substr($key, -5) == "_date")
{
$input[$key] = Helper::convert_in_ad($value);
}
}
return $input;
}
}
Now the problem is suppose you have failed validation and redirect back to the previous action and you then use old() or some other method to access the request data from the session, it will be modified, and i cannot get the the original data.
How can i change the date input in A.D when before validation so that i can properly validate in A.D and then store all the date input in A.D. by solving failed validation problem having modified input.
Edit Question
update:
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Repositories\Contracts\CourseInterface;
use App\Repositories\Contracts\ClassInterface;
use App\Http\Requests\ClassRequest;
use App\Helpers\Helper;
class ClassController extends Controller
{
public function __construct(ClassInterface $class, CourseInterface $course)
{
$this->class = $class;
$this->course = $course;
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$classes = $this->class->paginate();
return view('backend.class.index')->with([
'classes' => $classes
]);
/*return view('backend.class.index')->with([
'classes' => $classes
]);*/
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
$courses = $this->course->all();
return view('backend.class.create')->with([
'courses' => $courses
]);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(ClassRequest $request)
{
// dd($request->all());
$this->class->create($request->all());
return redirect()->route('classes.index');
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
$class = $this->class->find($id);
$courses = $this->course->all();
return view('backend.class.edit')->with([
'class' => $class,
'courses' => $courses
]);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(ClassRequest $request, $id)
{
$class = $this->class->update($request->all(), $id);
return redirect()->back();
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->class->delete($id);
return redirect()->route('classes.index');
}
public function delete($id)
{
$class = $this->class->find($id);
return view('backend.class.delete')->with([
'class' => $class
]);
}
}
My class Request File
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Helpers\Helper;
class ClassRequest extends Request
{
public function all()
{
$input = parent::all();
foreach ($input as $key=>$value)
{
if (substr($key, -5) == "_date")
{
$input[$key] = Helper::convert_in_ad($value);
}
}
return $input;
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
//$this->sanitize();
switch($this->method())
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'name' => 'required',
'course_id' => 'required',
'start_date' => 'required|date',
'end_date' => 'date|after:start_date',
];
}
case 'PUT':
case 'PATCH':
{
return [
'name' => 'required',
'course_id' => 'required',
'start_date' => 'required|date',
'end_date' => 'date|after:start_date',
];
}
default:break;
}
}
}
For validation purpose i need to change the date from B.S in A.D because laraval validation don't recognize B.S date. If i convert date in request file the problem is if validation fails i get the modified request back in form after redirect.
So how can i validate the date by converting it into A.D. The date in database table must be stored in A.D format for that i can use Accessors and Mutators the main problem is how to validate the data which user input in B.S format.
Edit After the suggestion i got
Thank you all for the suggestion, thank you very much for your help. One way i can validate is by making a custom validation rule as suggested. Right now i have another idea for making this work.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use App\Helpers\Helper;
abstract class Request extends FormRequest
{
/**
* Sanitize input before validation
*
* #return array
*/
public function validator($factory)
{
return $factory->make(
$this->sanitizeInput(), $this->container->call([$this, 'rules']), $this->messages()
);
}
protected function sanitizeInput()
{
if (method_exists($this, 'sanitize'))
{
return $this->container->call([$this, 'sanitize']);
}
return $this->all();
}
/**
* Check for input having _date for converting it into AD
*
* #return array
*/
public function sanitize()
{
$input = $this->all();
foreach ($input as $key=>$value)
{
if (substr($key, -5) == "_date")
{
$input[$key] = Helper::convert_in_ad($value);
}
}
return $input;
}
}
By using the following code request data is not changed. And There will be no need for creating custom validation and this will be easy if i later decided to take date in A.D from user then changing every request file for updating validation rule wont be necessary.
What do you think about this?
As has been mentioned in the comments you should try and avoid editing the data in your FormRequest.
What you could do is define a new validation rule specifically for this: https://laravel.com/docs/5.3/validation#custom-validation-rules
So, in your app/Providers/AppServiceProvider (or another registered ServiceProvider) you could have something like:
Validator::extend('bs_date', function ($attribute, $value, $parameters, $validator) {
$date = date_parse(Helper::convert_in_ad($value));
return checkdate($date['month'], $date['day'], $date['year']);
}, 'Your error message');
Validator::extend('bs_after', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
$before = Helper::convert_in_ad($data[$parameters['0']]);
$after = Helper::convert_in_ad($value);
return (new \DateTime($before)) < (new \DateTime($after));
}, 'Your error message');
Rules
'start_date' => 'required|bs_date',
'end_date' => 'date|bs_after:start_date',
Obviously, don't forget to import Validator and Helper in the ServiceProvider.
This should mean that you don't have to edit your input anymore.
Hope this helps!

Laravel request validation throwing HttpResponseException in version 5.2

I'm trying to update Laravel from version 5.1 to 5.2 in my project, I have followed this upgrade guide from the documentation, but now I'm getting this HttpResponseException when a validation fails
* Handle a failed validation attempt.
*
* #param \Illuminate\Contracts\Validation\Validator $validator
* #return mixed
*
* #throws \Illuminate\Http\Exception\HttpResponseException
*/
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException($this->response(
$this->formatErrors($validator)
));
}
In 5.1, the framework redirected to the previous url automatically with the validation errors.
This is my validation request
namespace domain\funcao\formRequest;
use autodoc\Http\Requests\Request;
class StoreFuncaoRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'codigo' => 'required|max:255|unique:funcao,codigo,'.$this->input('id').',id,deleted_at,NULL',
'nome' => 'required|max:255|unique:funcao,nome,'.$this->input('id').',id,deleted_at,NULL'
];
}
}
I have already updated my Exceptions Handler according to the guide
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
\Illuminate\Auth\Access\AuthorizationException\AuthorizationException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Foundation\ValidationException\ValidationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
];
...
}
Did someone had this problem??
I found the cause for this error, I was calling PrettyPageHandler from Whoops manually in my Exception Handler class.
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
\Illuminate\Auth\Access\AuthorizationException\AuthorizationException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Foundation\ValidationException\ValidationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
];
...
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
// I just needed to remove this call to get rid of the problem
if (config('app.debug'))
{
return $this->renderExceptionWithWhoops($e);
}
return parent::render($request, $e);
}
/**
* Render an exception using Whoops.
*
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
protected function renderExceptionWithWhoops(Exception $e)
{
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler());
return new \Illuminate\Http\Response(
$whoops->handleException($e),
$e->getStatusCode(),
$e->getHeaders()
);
}
}
I'm still using Whoops, but now automatically through Laravel Exceptions
This was the first question I came across when I had this problem although for me it was with Sentry not Whoops.
Here's a couple of exceptions and how I dealt with them:
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
return redirect()->back()->withInput()->with('error', 'Your Session has Expired');
}
if ($e instanceof HttpResponseException) {
return $e->getResponse();
}
return response()->view('errors.500', [
'sentryID' => $this->sentryID,
], 500);
}
I handle the HttpResponseException by simply returning the response the same way the ExceptionHandler class does.
All you have to do is, just write your business logics inside the protected failedValidation() inside your custom FormRequest class like follows
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
/**
* [failedValidation [Overriding the event validator for custom error response]]
* #param Validator $validator [description]
* #return [object][object of various validation errors]
*/
public function failedValidation(Validator $validator) {
// write your business logic here otherwise it will give same old JSON response
throw new HttpResponseException(response()->json($validator->errors(), 422));
}

Categories