Using complex conditional validation rule in FormRequest in Laravel - laravel

I am developing a Web application using Laravel. What I am doing now is creating a FirmRequest for the validation.
This is my FormRequest.
use Illuminate\Foundation\Http\FormRequest;
class StoreVacancy extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required',
'type' => 'required',
'complex_field' => ...need complex conditional validation based on the type field
];
}
}
If I did not use the FormRequest, I can create validator in the controller and set complex conditional validation rule like this.
$v = Validator::make($data, [
//fields and rules
]);
$v->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
But the issue is that I am not creating the Validator in the controller. But I am using the FormRequest. How can I achieve the same thing in the FormRequest?

You can manually adjust the rules depending on the input data:
class StoreVacancy extends FormRequest
{
public function rules()
{
$reason = $this->request->get('reason'); // Get the input value
$rules = [
'title' => 'required',
'type' => 'required',
];
// Check condition to apply proper rules
if ($reason >= 100) {
$rules['complex_field'] = 'required|max:500';
}
return $rules;
}
}
Its not the same as sometimes, but it does the same job.

Since 5.4 you can use the method "withValidator" inside your FormRequest:
https://laravel.com/docs/5.4/validation
This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated
use Illuminate\Foundation\Http\FormRequest;
class StoreVacancy extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required',
'type' => 'required',
];
}
public function withValidator($validator)
{
$validator->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
}
}

You can check the createDefaultValidator function in https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Http/FormRequest.php. We will override that function and add our condition
/**
* Add Complex Conditional Validation to the validator instance.
*
* #param \Illuminate\Validation\Factory $factory
* #return \Illuminate\Validation\Validator
*/
public function validator($factory)
{
$validator = $factory->make(
$this->validationData(),
$this->container->call([$this, 'rules']),
$this->messages(),
$this->attributes()
);
$validator->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
return $validator;
}

Related

Merge Form Request Validation for store and update

I am using Request validation to validate the user's input.
This is UpdateUser:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate;
class UpdateUser extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return Gate::allows('update-user');
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$user_id = Arr::get($this->request->get('user'), 'id');
return [
'user.firstname' => 'required|string|max:255',
'user.lastname' => 'required|string|max:255',
'user.email' => "required|string|email|max:255|unique:users,email,{$user_id}",
'user.password' => 'sometimes|nullable|string|min:4|confirmed',
];
}
}
As you can see, there is some update-specific stuff happening:
The authorize() method checks whether the user is allowed to update-user and inside the rules I am excluding the row of the current user from being unique:
'user.email' => "required|string|email|max:255|unique:users,email,{$user_id}",
As I would like to merge UpdateUser and StoreUser, what would be the most efficient and readable way to determine, whether I am updating or saving?
This is my current approach:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate;
class UpdateUser extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
if($this->isUpdating())
{
return Gate::allows('update-user');
}
return Gate::allows('create-user');
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
if($this->isUpdating()){
$user_id = Arr::get($this->request->get('user'), 'id');
return [
...
];
}
return [];
}
/**
* #return bool
*/
protected function isUpdating(){
return $this->isMethod('put') || $this->isMethod('patch');
}
}
I am wondering if I may extend the FormRequest class and provide isUpdating() by default.
Your update and store method are not the same request type, you have PUT and PATCH method on your request instance, so you can check the request type as like :
switch ($request->method()) {
case 'PATCH':
// do anything in 'patch request';
break;
case 'PUT':
// do anything in 'put request';
break;
default:
// invalid request
break;
}
I learnt about a new approach to validation some time ago using separate validator class and I kinda like it a lot. Let me show you
Create a directory Validators and a class inside UserValidator
class UserValidator
{
public function rules(User $user)
{
return [
'user.firstname' => [
'required',
'string',
'max:255',
],
'user.lastname' => [
'required',
'string',
'max:255',
],
'user.email' => [
$user->exists ? 'sometimes' : null,
'required',
'string',
'email',
'max:255',
Rule::unique('users', 'email')->ignore($user->exists ? $user->id : null)
],
'user.password' => [
$user->exists ? 'sometimes' : null,
'required',
'string',
'min:8'
],
];
}
public function validate(array $data, User $user)
{
return validator($data, $this->rules($user))
//->after(function ($validator) use ($data, $user) {
// Custom validation here if need be
//})
->validate();
}
}
Then authorization can be done in Controller
class UserController
{
use AuthorizesRequests;
/**
* #param Request $request
*/
public function store(Request $request)
{
$this->authorize('create_user', User::class);
$data = (new UserValidator())->validate(
$request->all(),
$user = new User()
);
$user->fill($data)->save();
}
/**
* #param Request $request
* #param \App\user $user
*/
public function update(Request $request, User $user)
{
$this->authorize('update_user', $user);
$data = (new UserValidator())->validate(
$request->all(),
$user
);
$user->fill($data)->save();
}
}
This was proposed and explained by (twitter handle) #themsaid

How to use the ignore rule in Form Request Validation

this is PostsRequest.php in http/request:
<?php
namespace App\Http\Requests;
use App\Post;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PostsRequest 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','max:255', Rule::unique('posts')->ignore($this->id)],
'slug' => ['required', Rule::unique('posts')->ignore($this->id),],
'content' => 'required',
'type' => 'required|in:blog,download,page',
'status' => 'required',
];
}
}
and this is edit() method in PostController.php
public function update(PostsRequest $request, $id)
{
$validated = $request->validated();
$validated['user_id'] = auth()->user()->id;
$post = Post::find($id)->fill($validated);
$post->save();
return redirect()->action('PostController#index');
}
Problem: show error in update page that this value is already exists.
who to resolve problem unique fields in edit form?
Problem Solved
change:
Rule::unique('posts')->ignore($this->route('id'))
with:
Rule::unique('posts')->ignore($this->route('post'))
If you're wanting to resolve the $id from the route then you can use the route() method in your request class e.g.
Rule::unique('posts')->ignore($this->route('id'))

How to add errors after FormRequests validation?

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',
]);

laravel api validation custom message

I am trying to write a custom message for a validation in laravel.
I have checked online and I saw some post where others solve that same issue by adding a protected function. I have also added the function to my code but it is not working. This is my code
This is myFormController.php:
public function req(RegistrationRequest $request){ $validated =
$request->validated();
return $validated; )}
This is the RegistrationRequest.php:
use Illuminate\Contracts\Validation\Validator; use
Illuminate\Http\Exceptions\HttpResponseException;
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => 'required|email',
'firstname' => 'required|string|max:20',
'lastname' => 'required|string|max:50',
'password' => 'required|min:8',
];
} protected function failedValidation(Validator $validator) {
throw new HttpResponseException(response()->json($validator->errors(), 422)); }
When that did not work, I used this:
protected function
failedValidation(\Illuminate\Contracts\Validation\Validator
$validator) { throw new
\Illuminate\Validation\ValidationException(response()->json($validator->errors(),
422)); }
Please what am I doing wrongly?
If you want custom messages just override messages method you just need to return an array containing key value pairs of field_name => 'message'
/**
* Get custom messages for validator errors.
*
* #return array
*/
public function messages()
{
return [
'field_name.required' => 'field_name is requeired',
'field_name.max' => 'max length should be something.'
];
}

Validation fileds from ajax request [Laravel 5]

Here is my validation request :rules
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Support\Facades\Auth;
class UpdateCommentRequest 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() {
$user = Auth::user()->id;
return [
'comment' => 'required|between:15,600',
'projectID' => "required|exists:project_group,project_id,user_id,$user|numeric",
'order' => "required|numeric",
'level' => "required|numeric"
];
}
}
And in my model I have like this:
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find(Input::get("order"));
$comment->text = Input::get('comment');
if($comment->save()){
return 'success';
}
}
This fileds I need to validate agins rules array:
array(
'comment' => Input::get('comment'),
'projectID' => Input::get('projectID'),
'order' => Input::get("order"),
'level' => Input::get("level"),
);
I need to check if all rules are ok and then update comment... Anyone can help?
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find($request->get("order"));
$comment->text = $request->get('comment');
if($comment->save()){
return 'success';
}
}
The logic behind the code:
A post request is send the the server and the route file sends it the the apiUpdateComment with all variables inside the $request. But before the code of the function is executed the validator checks the rules in your UpdateCommentRequest. If the test fails it will return errors. If it pass a comment with the id will be updated.

Resources