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'
];
}
}
Related
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"
]
}
I'm using laravel 5.5
I have a Request that I've built but the required rule is not working correctly.
Route
Route::get('v1/learning_centre/user/{userId}/course/list', 'API\LearningCentre#userCourses');
Controller
public function userCourses(GetUserCourses $request)
{
$courses = User::findOrFail($request->userId)
->courses()
->get();
return new CourseResourceCollection($courses);
}
Request
namespace App\Http\Requests\LearningCentre;
use Illuminate\Foundation\Http\FormRequest;
class GetUserCourses 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 [
'userId' => 'required|integer'
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'userId.required' => 'A User is required',
];
} }
If I turn off the required rule I can get to the controller. If I have the required rule in the request I get a 302. I am passing in a valid userId in phpunit. Without the request rules my code works as intended.
Any ideas?
You should be using route model binding to validate a required GET parameter in this situation, not a FormRequest class, which, as the name should indicate, are intended for form requests.
Your route:
Route::get('v1/learning_centre/user/{user}/course/list', 'API\LearningCentre#userCourses');
Your controller:
public function userCourses(User $user) {
If a user ID is missing (or an invalid one used), your controller will automatically throw a ModelNotFoundException, which Laravel by default returns as a 404.
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
I created a validation rule for the image form.
It works fine on store method but I do not want the image field to be required on update because I may only update the title for example.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'image' => 'required|image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* 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;
}
}
For unique validation we can add custom query conditions:
'email' => Rule::unique('users')->ignore($user->id, 'user_id')
or
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
})
Is it a clean way to achieve something similar for required?
Apply required only for new images.
you Can use switch statement inside rule
public function rules()
{
switch ($this->method()) {
case 'GET':
case 'DELETE': {
return [];
}
case 'POST': {
return [
'first_name'=>'required',
'last_name'=>'required',
'email'=>'required|email|unique:users,email,'.$this->id,
'password'=>'',
'dob'=>'required',
'phone_one'=>'required',
'phone_two'=>'required',
//'user_role'=>'required',
// 'profile_image'=>'required'
];
}
case 'PUT':
case 'PATCH': {
return [
];
}
default:break;
}
Also you can use condtion like on update yuo have id so based on that you can check whether its update or insert since on insert you dont have id so
Create another class that extends the Request class, DI that into your update controller action
class UpdateImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60'
];
/**
* 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;
}
}
much better way is to use nullable in Laravel 5.5 validations
Ref Docs
The field under validation may be null. This is particularly useful
when validating primitive such as strings and integers that can
contain null values.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'nullable|string|between:3,60',
'image' => 'nullable|image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* 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;
}
}
However I have used recently with image and it worked like charm for me. Give it a try!
The simplest way in this case in the other way. By default have rules for update and if it's store add required like so:
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'image' => 'image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* 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()
{
$rules = $this->rules;
if ($this->isMethod('POST')) {
$rules['image'] = 'required|' . $rules['image']
}
return $rules;
}
}
I found a solution.
I renamed image into file.
The route is homestead.app/images/1 on update and homestead.app/images on store so the $image property will be $this->image = 1 on update and $this->image = null on store.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title'=> 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'file' => [
'image',
'max:4000',
'dimensions:min_width=200,min_height=200',
],
];
/**
* 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->rules['file'][] = is_null($this->image) ? 'required' : 'sometimes';
return $this->rules;
}
}
I've been struggling with laravel 5.2 login function messages. I've override the default sendFailedLoginResponse function in AuthController which works for failed attempts.
But I need to override the validate function response as well which I could not figure out how to do that. Also I do not want to override the default login functionality in the AuthContrller and want to stick with the same login function.
The reason for overriding the validate function is that am making an angular app and want the response in json format with some custom keys.
Currently default login function in Illuminate\Foundation\Auth\AuthenticateUsers.php
public function login(Request $request)
{
$this->validate($request, [
$this->loginUsername() => 'required', 'password' => 'required',
]);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles) {
$this->incrementLoginAttempts($request);
}
return $this->sendFailedLoginResponse($request);
}
I want the response something like in the below sendFailedResponse function in AuthController
/**
* Get failed request response
*
* #param null
* #return null
*/
public function sendFailedLoginResponse()
{
return response()->json( [ 'status' => false, 'message' => $this->getFailedLoginMessage() ]);
}
Thanks
I don't know anything about Angular and handling json on laravel but I had similar problem while creating custom error message for postLogin function. take a look at this code, perhaps you could do something within form request.
This is my AuthController.php
use App\Http\Requests\LoginFormRequest;
/**
* Handle a login request to the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function postLogin(LoginFormRequest $request)
{
return $this->login($request);
}
Try using form request on the function postLogin
class LoginFormRequest 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 [
'email' => 'required|email',
'password' => 'required|min:6',
];
}
public function messages()
{
return [
'required' => 'Your :attribute is required.',
'min' => ':attribute must be at least :min characters in length.',
'email' => 'Please type valid email address.',
];
}
}
I came up with the solution by implementing JWT for authentication which I could think of is the best solution with Client side.