I'm have created a custom form request in my laravel 5.6 something like this:
<?php
namespace Noetic\Plugins\blog\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest 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 [
];
}
}
When I do not place anything in rules, I get the controllers working, and when I put any rule inside suppose I put
return [
'title' => 'required',
'body' => 'required',
];
It works until it gets validated true, I mean if title and body is passed it gets validated, but when I don't send any data for title or body I'm not getting errors as response, I see the home page belonging to web middleware, I want to return the error data as response.
My controller is something like this:
public function store( StorePostRequest $request )
{
if ($request->fails()) {
return $this->errorResponse($request->errors()->all());
}
$data = $request->only('title', 'body');
$post = Post::create($data);
return response()->json(['post'=> $post ],200);
}
Help me out with these. Thanks
In your controller function you don't need to catch the validation just try with success path.
Handler will handle your validation
public function store( StorePostRequest $request )
{
$data = $request->only('title', 'body');
$post = Post::create($data);
return response()->json(['post'=> $post ],200);
}
In your Handler
use Illuminate\Validation\ValidationException;
if ($exception instanceof ValidationException)
{
return response($exception->errors())->header('Content-Type', 'application/json');
}
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
after that
protected function failedValidation(Validator $validator) {
throw new HttpResponseException(response()->json($validator->errors(), 422));
}
Related
On validation fail, I have 2 situations
one when I display a new error page (this is how it was done before in the app)
I have a form and I redirect back with input (my problem)
The app is catching the ValidationException in the exceptions Handler - so there is no back with errors for me if a request fails.
Now, I need to return back to the input with errors - from the request class, before it throws the standard ValidationException.
How do I do that from the request class pls?
The code is very basic
I have some rules...
I imagine I need a hook like - validationFailed().. in the request class?
Edit: - not API - regular monolithic PHP
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Auth;
class LocationCodeRequest 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()
{
$user = Auth::user();
return [
'new_location_code' => 'required|string|max:255|exists:xxx,code',
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'new_location_code.not_in' => 'xxx',
'new_location_code.exists' => 'yyy',
];
}
//I need something like this
public function validationFailed()
{
return redirect()->back()->withErrors($this->validator)->withInput();;
}
}
Normally the FormRequest Class will redirect back to the Form Page with inputs and validation errors. That's the default functionality.
public function validationFailed() {
return redirect()->back()->withErrors($this->validator)->withInput();;
}
You don't require the above code if you are passing the FormRequest class in the POST call of the Form like so
use App\Http\Requests\LocationCodeRequest;
public function store(LocationCodeRequest $request) {
// Code after validation
}
You can try by adding this in the LocationCodeRequest file.
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
protected function failedValidation(Validator $validator) {
// Add redirection here. Errorbag and Inputs shud be available in session or can be passed here
//throw new HttpResponseException(response()->json($validator->errors(), 422));
}
I'm trying to follow this tutorial for add post params validation: https://odan.github.io/2020/10/08/slim4-respect-validation.html
I add a lib, and use it:
private function validateProperty(array $data): void
{
$validator = new v();
$validator->addRule(v::key('name', v::allOf(
v::notEmpty()->setTemplate('The property name must not be empty'),
v::length(3, 150)->setTemplate('Invalid length')
))->setTemplate('The key "Name" is required'));
$validator->addRule(v::key('address', v::allOf(
v::notEmpty()->setTemplate('The address must not be empty'),
v::length(3, 50)->setTemplate('Invalid length')
))->setTemplate('The key "address" is required'));
$validator->addRule(v::key('original_name', v::allOf(
v::notEmpty()->setTemplate('The original_name must not be empty'),
v::length(3, 255)->setTemplate('Invalid length')
), false));
$validator->assert($data);
}
Now, if I sent a correct values, all work fine.
And when I send incorrect name for example, throw error (500):
Type: Respect\Validation\Exceptions\ValidatorException
Code: 0
Message: These rules must pass for `{ "name": "tx", "address": "street1", "original_name": "Original" }`
File: C:\MAMP\htdocs\api2\vendor\respect\validation\library\Factory.php
Line: 235
Here I do't know how to get a message of a invalit name, not this general message error.
Ok, after I create a middleware on src/middleware/RespectValidationMiddleware.php.
<?php
namespace App\Middleware;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Respect\Validation\Exceptions\NestedValidationException;
final class RespectValidationMiddleware implements MiddlewareInterface
{
/**
* #var ResponseFactoryInterface
*/
private $responseFactory;
/**
* The constructor.
*
* #param ResponseFactoryInterface $responseFactory The response factory
*/
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->responseFactory = $responseFactory;
}
/**
* Invoke middleware.
*
* #param ServerRequestInterface $request The request
* #param RequestHandlerInterface $handler The handler
*
* #return ResponseInterface The response
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
try {
return $handler->handle($request);
} catch(NestedValidationException $exception) {
$messages = [];
/** #var ValidationException $message */
foreach($exception->getIterator() as $message) {
$key = $message->getParam('name');
if($key === null) {
continue;
}
$messages[$key] = $message->getMessage();
}
$response = $this->responseFactory->createResponse();
$result = [
'error' => [
'message' => $exception->getMessage(),
'details' => $messages,
],
];
$response->getBody()->write(json_encode($result));
$response->withHeader('Content-Type', 'application/json');
return $response->withStatus(422);
}
}
}
And try to add after error middleware:
<?php
use Selective\BasePath\BasePathMiddleware;
use Slim\App;
use Slim\Middleware\ErrorMiddleware;
use App\Middleware\RespectValidationMiddleware;
return function (App $app) {
// Parse json, form data and xml
$app->addBodyParsingMiddleware();
// Add the Slim built-in routing middleware
$app->addRoutingMiddleware();
$app->add(BasePathMiddleware::class);
// Catch exceptions and errors
$app->add(ErrorMiddleware::class);
$app->add(RespectValidationMiddleware::class); // <-- here
};
The RespectValidationMiddleware line, cause a 500 error when I try to post data ,without error message.
I read that respect/validation throw a NestedValidationException, but in the first try I'm getting a ValidatorException. I don't know if can be the problem..
Remove the middleware and try $validator->check($data);.
I have a very simple Rule method in request class like below.
public function rules()
{
return [
'Subject' => 'required|max:50',
'Description' => 'required|max:500',
'DepartmentID' => 'required|integer|min:1',
'PriorityID' => 'required|integer|min:1'
];
}
Inside Controller Action method, below is the code.
private function SaveChanges(\App\Http\Requests\TicketRequest $request) {
$v = \Validator::make($request->all(), [
]);
$DepartmentAdmins = $this->getDepartmentAdmins();
//Check if department admin missing then no need to add the record
if($DepartmentAdmins == null || count($DepartmentAdmins) == 0) {
$v->errors()->add('MissingAdmins', 'Department admin missing.');
return redirect()->back()->withErrors($v->errors());
}
}
Question:
As we can see in the rule method there are 4 form fields. Is there any way to shift the check for Department Admin existence from Controller Action method to request class?
Laravel's Request has after hook that can be run after normal validation completes. This is how you can use it in your case:
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Models\Property;
use Illuminate\Validation\Validator;
class SomeRequest extends Request
{
/**
* Get the validator instance for the request.
*
* #return Validator
*/
protected function getValidatorInstance()
{
$instance = parent::getValidatorInstance();
$instance->after(function ($validator) {
$this->validateDepartmentAdmins($validator);
});
return $instance;
}
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'Subject' => 'required|max:50',
'Description' => 'required|max:500',
'DepartmentID' => 'required|integer|min:1',
'PriorityID' => 'required|integer|min:1'
];
}
/**
* #param Validator $validator
*/
public function validateDepartmentAdmins(Validator $validator)
{
$DepartmentAdmins = $this->getDepartmentAdmins();
//Check if department admin missing then no need to add the record
if($DepartmentAdmins == null || count($DepartmentAdmins) == 0) {
$validator->errors()->add('MissingAdmins', 'Department admin missing.');
}
}
That way you won't have to do any validation in your SaveChanges controller method.
This code is used in Laravel 5.1, but I believe it will work the same in 5.2.
The Form Request Class basically has two methods. "authorize" and "rules". the best way to shift the check for Department Admin existense is to add your own custom validator(for example named "adminCountValidator") and implement your logic for checking the number of administrators there. Then use yoir newly defined validator in "rules" method like this:
public function rules()
{
return [
'Subject' => 'required|max:50',
'Description' => 'required|max:500',
'DepartmentID' => 'required|integer|min:1|adminCountValidator',
'PriorityID' => 'required|integer|min:1'
];
}
if you define a custome validation rule, you can also define the associated error message and your controller action will be much more cleaner. here is the link for defining your own custom validator
custom-validation-rules
here is a sample code for adding a custom validator within a service provider
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Validator::extend('adminCountValidator', function($attribute, $value, $parameters, $validator) {
/*
implement your getDepartmentAdmins()
function here and return true or false
*/
});
}
I am creating Rest Full Api for mobile application, I am validating request it redirects me to the login page with errors.
Here is my ApiController (I have created for all api):
use App\User as UserModel;
use App\Fb_friend as FbFriendsModel;
use App\Http\Requests\UserRequest;
class ApiController extends Controller
{
/**
* Create a new movie model instance.
*
* #return void
*/
public function __construct(UserModel $user, FbFriendsModel $fb_friends){
$this->user = $user;
$this->fb_friends = $fb_friends;
}
public function createUser (UserRequest $request) {
// some code here
}
Route:
Route::post('createUser', ['as' => 'createUser', 'uses' => 'ApiController#createUser']);
UserRequest.php:
public function rules()
{
return [
'fb_id' => 'required|unique:users',
'username' => 'required|unique:users',
'email' => 'required|unique:users',
'image' => 'required',
'device_id' => 'required',
'status' => 'required',
];
}
I have override a function Request.php for error formatting:
abstract class Request extends FormRequest
{
protected function formatErrors(Validator $validator)
{
return [$validator->messages()->toJson()];
}
}
When I try to call service via postman, it returns me error in json format but it also print the login page, I m not getting why?
If you are using Postman for testing API's, it is not necessary to override the response() in Request class, One can follow the following steps,
make return type in authorize() in your custom Request as true,
public function authorize()
{
//make it true
return true;
}
Go to headers section in your Postman and define Accept type,
Accept:application/json
Now hit the endpoint of your API and bam..working fine for me.
It has been done by override the response method in app/Http/Requests/Request.php
public function response(array $errors) {
if ($this->ajax() || $this->wantsJson() || Request::isJson()) {
$newError = [];
$newError['result'] = false;
$newError['errors'] = $errors;
// in the above three lines I have customize my errors array.
return new JsonResponse($newError, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors);
}
We also need to use JsonResponse class at the top
use Illuminate\Http\JsonResponse;
Source: https://laracasts.com/discuss/channels/general-discussion/laravel-5-validation-formrequest
Can I ask what have I done wrong in my LoginRequest.php where I've set a condition to redirect to a custom login page if there is any sort of error in the login process? I have my codes as below:
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class LoginRequest 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 [
'login_email' => 'required',
'login_password' => 'required'
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'login_password.required' => 'Password cannot be blank'
];
}
public function redirect()
{
return redirect()->route('login');
}
}
The code is supposed to redirect users who login from a nav bar login form to the main login page, if there are any errors, but it doesn't seem to redirect.
if you want to redirect to a specific url, then use protected $redirect
class LoginRequest extends Request
{
protected $redirect = "/login#form1";
// ...
}
or if you want to redirect to a named route, then use $redirectRoute
class LoginRequest extends Request
{
protected $redirectRoute = "session.login";
// ...
}
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: Refer to Laravel Validation
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...
}
Found a solutions. All I need to do is to override the initial response from
FormRequest.php
like such and it works like a charm.
public function response(array $errors)
{
// Optionally, send a custom response on authorize failure
// (default is to just redirect to initial page with errors)
//
// Can return a response, a view, a redirect, or whatever else
if ($this->ajax() || $this->wantsJson())
{
return new JsonResponse($errors, 422);
}
return $this->redirector->to('login')
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
This works in Lara 7
Add an anchor to jump to the comment form if validation fails
protected function getRedirectUrl()
{
return parent::getRedirectUrl() . '#comment-form';
}
If you are using the validate() method on the Controller
$this->validate($request, $rules);
then you can overwrite the buildFailedValidationResponse from the ValidatesRequests trait present on the base Controller you extend.
Something along this line:
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->route('login');
}
Variations on this answer have already been offered, but overriding the getRedirectUrl() method in a custom request can enable you to define the route parameters, rather than just the name that the $redirectRoute property offers.