I am trying to use form request in my REST API built using laravel 5.2. My controller is
public function save(SbcEntityFormRequest $request)
{
$requestData = Input::all();
try {
list($success, $message) = $this->sbcService->saveSbcEntity($requestData);
if ($success) {
return $this->successJsonResponse($request, ['id' => $message]);
}
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, [$message]);
} catch (Exception $e) {
AppLog::write($e);
$message = [config('messages.save_failed')];
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, $message);
}
}
My form request is
namespace App\Http\Requests;
use Illuminate\Http\Request;
class SbcEntityFormRequest 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 [
'logo' => 'Required',
'bio' => 'Required|Max:150'
];
}
}
My validation rules are never called. I put a die statement in authorize() function and it is neither called. When I printed $request->all() in the controller it shows empty array. Any Idea on what is wrong here?
Related
i'm new to laravel , hope someone could help me with this problem ,
i've created a request class to validate my inputs . But when the validation fails it doesn't return any error messages instead showing a 404 error.
my request class , recoverIdRequest
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class recoverIdRequest 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 [
'dob' => 'required',
'email' => 'required',
];
}
}
and here's my controller : testController
class testController extends Controller
{
/**
*
* #param \App\Http\Requests\recoverIdRequest $request
* #return Illuminate\Http\Response
*/
public function test(recoverIdRequest $request)
{
$validated = $request->validated();
$dob = $request->input('dob');
$new_dob = Carbon::parse($dob)->format('Y-m-d');
$email = $request->input('email');
$exist =Appl_students::where('email', $email)
->whereBetween('dob', [$new_dob, $new_dob])
->select('id', 'first_name')
->first();
if ($exist == null) {
return response()->json(['data' => $exist, 'success' => false, 'message' => "User not found"]);
} else {
$date_of_birth = Carbon::parse($dob)->format('d-m-Y');
$institute =Master::select(
'institution_name', 'website_url', 'institution_place',
'institution_post', 'institution_district', 'institution_pin',
'institution_state', 'institution_phone', 'institution_email')
->first();
return $institute;
Mail::to($email)->send(new RecoverApplicationId($exist->id, $date_of_birth, $exist->first_name, $institute->institution_name));
return response()->json(['data' => $exist, 'success' => true, 'message' => "Application ID has seen sent to registered mail"]);
}
}
}
and this is the response in postman when the validation fails :
routes/api.php
Route::group([ 'prefix'=>'modelTesting', ], function() {
Route::post('test/{id}' [testController::class,'test'])->middleware(['auth:api', 'scope:admin']);
});
Resolved
it was a problem with postman headers,i was able to fix the issue using the following headers :
Accept: application/json
X-Requested-With: XMLHttpRequest
You should follow the naming convention first in all your classes.
As the information not very much clear but it should return 422 error status. It might be the problem that when validation is failed then it is trying to redirect none existence URL. Please check the type of method you are using in postman to call the api. If it not resolved please paste the request data from the postman. And the routes.php
It does not give 404, if the validation process is not successful, validation redirects back to the previous page, in your case it gives 404 because there is no rest API and a previous page ... let's agree here
it's very natural and you just have to write a small validation method for it
try it, add this method to your form request class(recoverIdRequest) and try again
/**
* Returns validations errors.
*
* #param Validator $validator
* #throws HttpResponseException
*/
protected function failedValidation(Validator $validator)
{
// you can debug with dd() in this method
if ($this->wantsJson() || $this->ajax()) {
throw new HttpResponseException(response()->json($validator->errors(), 422));
}
parent::failedValidation($validator);
}
second step you should change handler
app/exceptions/Handler.php
public function render($request, Exception $e)
{
if ($request->ajax() || $request->wantsJson())
{
$json = [
'success' => false,
'error' => [
'code' => $e->getCode(),
'message' => $e->getMessage(),
],
];
return response()->json($json, 400);
}
return parent::render($request, $e);
}
How to add errors after FormRequests validation?
password_repository->update() will return an error if the current passwords entered do not match.
password_repository->update() calls an external API.
I want to add an error in the controller depending on the return value of the repository.
In PasswordRequest, validation after calling the external API cannot be described, so I am in trouble.
For this reason I want to add an error in the controller after doing password_repository->update().
PasswordController.php
public function completeEdit(PasswordRequest $request)
{
$input = $request->only(['password', 'new_password']);
$data = $this->password_repository->update($input);
//I want to add an error at this point!!!
return view('pages.password.edit.complete');
}
}
PasswordRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PasswordRequest 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 [
'password' => 'required',
'new_password' => 'required|confirmed',
'new_password_confirmation' => 'required',
];
}
}
Redirect with errors could help you.
return redirect()->back()->withErrors([
'Password not correct',
]);
Or return to a specific route.
return redirect()->route('password.create')->withErrors([
'Password not correct',
]);
I'm using a custom request class for laravel form validations.
This is my request class
class ContactUsRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'lname' => 'required'
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'lname.required' => 'please enter the last name'
];
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
}
And this is where I call it,
public function send(ContactUsRequest $request) {
$validator = $request->validated();
if ($validator->fails()) {
return redirect('/contactus')
->withErrors($validator)
->withInput();
} else {
ContactUs::create($request->all());
return redirect('/contactus');
}
}
But when I input correct values I get this,
Symfony \ Component \ Debug \ Exception \ FatalThrowableError
(E_ERROR) Call to a member function fails() on array
That's because request object automatically do that for you and you don't need to redirect back manually and $validator variable contains validated inputs so in your case you don't need to do anything and you can remove if and redirect safely
public function send(ContactUsRequest $request) {
ContactUs::create($request->validated());
return redirect('/contactus');
}
}
Using form request classes
If validation fails, a redirect response will be generated automatically to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
In order to capture validation failure you may use the Validator facade
E.g
use Illuminate\Support\Facades\Validator;
//...
public function send(Request $request) {
$validator = Validator::make($request->all(), [
'lname' => 'required'
// ...
]);
if ($validator->fails()) {
return redirect('/contactus')
->withErrors($validator)
->withInput();
}
ContactUs::create($request->all());
return redirect('/contactus');
}
Form Request Validation Documentation
Manually Creating Validators Documentation
And we can keep ContactUsRequest like this.
public function send(ContactUsRequest $request) {
$validator = $request->validated();
ContactUs::create($request->all());
return redirect('/contactus');
}
I would like to ask how should I handle validation on multiple scenarios using FormRequest in L5? I know and I was told that I can create saparate FormRequest files to handle different validations but it is very redundant and also noted that I would need to inject it into the controller manually using the use FormRequest; keyword. What did previously in L4.2 is that I can define a new function inside my customValidator.php which then being called during controller validation via trycatch and then the data is being validated by service using the below implementation.
class somethingFormValidator extends \Core\Validators\LaravelValidator
{
protected $rules = array(
'title' => 'required',
'fullname' => 'required',
// and many more
);
public function scenario($scene)
{
switch ($scene) {
case 'update':
$this->rules = array(
'title' => 'required',
'fullname' => 'required',
// and other update validated inputs
break;
}
return $this;
}
}
Which then in my LaravelValidator.php
<?php namespace Core\Validators;
use Validator;
abstract class LaravelValidator {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
/**
* Set data to validate
*
* #return \Services\Validations\AbstractLaravelValidator
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
/**
* Validation passes or fails
*
* #return Boolean
*/
public function passes()
{
$validator = Validator::make(
$this->data,
$this->rules,
$this->messages
);
if ($validator->fails())
{
$this->errors = $validator->messages();
return false;
}
return true;
}
/**
* Return errors, if any
*
* #return array
*/
public function errors()
{
return $this->errors;
}
}
and then finally this is how i call the scenarios inside services like this
public function __construct(somethingFormValidator $v)
{
$this->v = $v;
}
public function updateSomething($array)
{
if($this->v->scenario('update')->with($array)->passes())
{
//do something
else
{
throw new ValidationFailedException(
'Validation Fail',
null,
$this->v->errors()
);
}
}
So the problem is now since i have migrated to L5 and L5 uses FormRequest, how should I use scenario validation in my codes?
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class ResetpasswordRequest 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',
'g-recaptcha-response' => 'required|captcha',
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'g-recaptcha-response.required' => 'Are you a robot?',
'g-recaptcha-response.captcha' => 'Captcha session timeout'
];
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = array(
//scenario rules
);
break;
}
return $this;
}
}
also how should I call it in the controller?
public function postReset(ResetpasswordRequest $request)
{
$profile = ProfileService::getProfileByEmail(Request::input('login_email'));
if($profile == null)
{
$e = array('login_email' => 'This email address is not registered');
return redirect()->route('reset')->withInput()->withErrors($e);
}
else
{
//$hash = ProfileService::createResetHash($profile->profile_id);
$time = strtotime('now');
$ip = Determinator::getClientIP();
MailProcessor::sendResetEmail(array('email' => $profile->email,
'ip' => $ip, 'time' => $time,));
}
}
I believe the real issue at hand is everything is validated through the form request object before it reaches your controller and you were unable to set the appropriate validation rules.
The best solution I can come up with for that is to set the validation rules in the form request object's constructor. Unfortunately, I am not sure how or where you are able to come up with the $scene var as it seems to be hard-coded in your example as 'update'.
I did come up with this though. Hopefully reading my comments in the constructor will help further.
namespace App\Http\Requests;
use App\Http\Requests\Request;
class TestFormRequest extends Request
{
protected $rules = [
'title' => 'required',
'fullname' => 'required',
// and many more
];
public function __construct()
{
call_user_func_array(array($this, 'parent::__construct'), func_get_args());
// Not sure how to come up with the scenario. It would be easiest to add/set a hidden form field
// and set it to 'scene1' etc...
$this->scenario($this->get('scenario'));
// Could also inspect the route to set the correct scenario if that would be helpful?
// $this->route()->getUri();
}
/**
* 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 $this->rules;
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = [
//scenario rules
];
break;
}
}
}
You can use laratalks/validator package for validation with multiple scenarios in laravel. see this repo
I want to validate the route parameters in the "form request" but don't know how to do it.
Below is the code sample, I am trying with:
Route
// controller Server
Route::group(['prefix' => 'server'], function(){
Route::get('checkToken/{token}',['as'=>'checkKey','uses'=> 'ServerController#checkToken']);
});
Controller
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests;
class ServerController extends Controller {
public function checkToken( \App\Http\Requests\CheckTokenServerRequest $request) // OT: - why I have to set full path to work??
{
$token = Token::where('token', '=', $request->token)->first();
$dt = new DateTime;
$token->executed_at = $dt->format('m-d-y H:i:s');
$token->save();
return response()->json(json_decode($token->json),200);
}
}
CheckTokenServerRequest
namespace App\Http\Requests;
use App\Http\Requests\Request;
class CheckTokenServerRequest extends Request {
//autorization
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
}
}
But when I try to validate a simple url http://myurl/server/checkToken/222, I am getting the response: no " token " parameter set.
Is it possible to validate the parameters in a separate "Form request", Or I have to do all in a controller?
ps. Sorry for my bad English.
For Laravel < 5.5:
The way for this is overriding all() method for CheckTokenServerRequest like so:
public function all()
{
$data = parent::all();
$data['token'] = $this->route('token');
return $data;
}
EDIT
For Laravel >= 5.5:
Above solution works in Laravel < 5.5. If you want to use it in Laravel 5.5 or above, you should use:
public function all($keys = null)
{
$data = parent::all($keys);
$data['token'] = $this->route('token');
return $data;
}
instead.
Override the all() function on the Request object to automatically apply validation rules to the URL parameters
class SetEmailRequest
{
public function rules()
{
return [
'email' => 'required|email|max:40',
'id' => 'required|integer', // << url parameter
];
}
public function all()
{
$data = parent::all();
$data['id'] = $this->route('id');
return $data;
}
public function authorize()
{
return true;
}
}
Access the data normally from the controller like this, after injecting the request:
$setEmailRequest->email // request data
$setEmailRequest->id, // url data
If you dont want to specify each route param and just put all route params you can override like this:
Laravel < 5.5:
public function all()
{
return array_merge(parent::all(), $this->route()->parameters());
}
Laravel 5.5 or above:
public function all($keys = null)
{
// Add route parameters to validation data
return array_merge(parent::all(), $this->route()->parameters());
}
The form request validators are used for validating HTML form data that are sent to server via POST method. It is better that you do not use them for validating route parameters. route parameters are mostly used for retrieving data from data base so in order to ensure that your token route parameter is correct change this line of your code, from
$token = Token::where('token', '=', $request->token)->first();
to
$token = Token::where('token', '=', $request->input(token))->firstOrFail();
firstOrFail() is a very good function, it sends 404 to your user, if the user insert any invalid token.
you get no " token " parameter set because Laravel assumes that your "token" parameter is a POST data which in your case it is not.
if you insist on validating your "token" parameter, by form request validators you gonna slow down your application, because you perform two queries to your db,
one in here
$token = Token::where('token', '=', $request->token)->first();
and one in here
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
I suggest to use firsOrFail to do both validating and retrieving at once.
A trait can cause this validation to be relatively automagic.
Trait
<?php
namespace App\Http\Requests;
/**
* Class RouteParameterValidation
* #package App\Http\Requests
*/
trait RouteParameterValidation{
/**
* #var bool
*/
private $captured_route_vars = false;
/**
* #return mixed
*/
public function all(){
return $this->capture_route_vars(parent::all());
}
/**
* #param $inputs
*
* #return mixed
*/
private function capture_route_vars($inputs){
if($this->captured_route_vars){
return $inputs;
}
$inputs += $this->route()->parameters();
$inputs = self::numbers($inputs);
$this->replace($inputs);
$this->captured_route_vars = true;
return $inputs;
}
/**
* #param $inputs
*
* #return mixed
*/
private static function numbers($inputs){
foreach($inputs as $k => $input){
if(is_numeric($input) and !is_infinite($inputs[$k] * 1)){
$inputs[$k] *= 1;
}
}
return $inputs;
}
}
Usage
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyCustomRequest extends FormRequest{
use RouteParameterValidation;
/**
* 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 [
//
'any_route_param' => 'required'//any rule(s) or custom rule(s)
];
}
}
For \App\Http\Requests\CheckTokenServerRequest you can add use App\Http\Requests\CheckTokenServerRequest; at the top.
If you pass the token by url you can use it likes a variable in controller.
public function checkToken($token) //same with the name in url
{
$_token = Token::where('token', '=', $token)->first();
$dt = new DateTime;
$_token->executed_at = $dt->format('m-d-y H:i:s');
$_token->save();
return response()->json(json_decode($token->json),200);
}
$request->merge(['id' => $id]);
...
$this->validate($request, $rules);
or
$request->merge(['param' => $this->route('param')]);
...
$this->validate($request, $rules);
You just missing the underscore before token. Replace with
_token
wherever you check it against the form generated by laravel.
public function rules()
{
return [
'_token' => ['required','exists:Tokens,token,executed_at,null']
];
FormRequest has a method validationData() that defines what data to use for validation. So just override that one with route parameters in your form request class:
/**
* Use route parameters for validation
* #return array
*/
protected function validationData()
{
return $this->route()->parameters();
}
or leave most of the all logic in place and override input method from trait \Illuminate\Http\Concerns\InteractsWithInput
/**
* Retrieve an input item from the request.
*
* #param string|null $key
* #param string|array|null $default
* #return string|array|null
*/
public function input($key = null, $default = null)
{
return data_get(
$this->getInputSource()->all() + $this->query->all() + $this->route()->parameters(), $key, $default
);
}