The question is already asked here for a previous version of laravel and not yet answered.
I have a html form which is validated using three different Form Request Validations. I am able to do this. But, the problem is, the form validations take place one by one. Not at the same time.
If the first form request throws a validation error the form is returned to the view so rest of the two forms doesn't evaluated, hence a proper validation error can't be displayed to the user.
What I want is : validate the form with the three form validation requests rules at the same time.
Controller:
public function store(TransportationRequest $request1, PurchaseRequest $request2, SaleRequest $request3)
{
//do actions here
}
I have tried with inheriting the form requests one by one but could not be succeeded.
Edit :
To be more specific to my question:
I do have three seperate forms for purchase, transporataion and sale which are individually valuated using PurchaseRequest, TransportationRequest and SaleRequest for individual operations.
But there is a special case where a single form handles a purchase, transporataion and a sale. I want to validate the form using combining the three form request rules because I didn't want to write the same validation rules again.
This
Note : The fields in the seperate forms and combined form are same.
Thanks..
A FormRequest throws an Illuminate\Validation\ValidationException Exception when validation fails which has a redirectTo method, and from there the Exception Handler performs the redirect.
You can achieve your desired behaviour by running your Form Requests manually in your controller within a try/catch block which captures the errors and combines the error bags before redirecting, or if it's essential that you run them by Laravel injecting them into your controller then you would need to add your own exception handler which captures all of the errors, combines them and then redirects after the final Form Request has ran.
However, it's worth noting, both of those approaches aren't great: they're cumbersome and are going to cause you more problems than they solve. You should try to adhere to the Laravel way of doing things as best possible if you'd like to write a maintainable application.
A Form Request exists to validate a form, therefore, each Form should have one Form Request, if you wish to compose a Form Request from different sets of rules then that should be done within the Form Request, e.g:
Define your Form Request for your form php artisan make:request StoreMultipleForm
From the rules method on StoreMultipleForm fetch the rules for each of the other Form Requests and then return them together, e.g:
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$formRequests = [
TransportationRequest::class,
PurchaseRequest::class,
SaleRequest::class
];
$rules = [];
foreach ($formRequests as $source) {
$rules = array_merge(
$rules,
(new $source)->rules()
);
}
return $rules;
}
Use the new composed Form Request in your controller, e.g:
public function store(StoreMultipleForm $request)
{
// Do actions here.
}
The advantages of this method are that it's self-contained, it adheres to the one form one Form Request expectation, it doesn't require changes to the Form Requests you're combining and if you need to add additional rules unique to this form you can do so without creating another Form Request.
I would create traits containing the rules for each FormRequest - purchase, transporataion and sale. Use the trait in it's specific FormRequest and then when you need all the rules you can use all three traits in the combined FormRequest and merge the rules arrays then.
If I understand correctly, you have 3 forms, each with their own form requests to deal with their respective validation. You also have another form which combines those 3 forms somewhere else and you don't want to repeat yourself by rewriting those validation rules.
In which case, I would still suggest going with a single form request, but try to combine the rules of each of those individual requests. For example, you use static methods to define your rules on the 3 individual form requests and have each individual request call its own static method to grab them:
class TransportationRequest extends FormRequest
{
public static function getRules()
{
return []; // Return rules for this request
}
public function rules()
{
return static::getRules();
}
}
class PurchaseRequest extends FormRequest
{
public static function getRules()
{
return []; // Return rules for this request
}
public function rules()
{
return static::getRules();
}
}
class SaleRequest extends FormRequest
{
public static function getRules()
{
return []; // Return rules for this request
}
public function rules()
{
return static::getRules();
}
}
And then have your combined request merge all three sets:
class CombinedRequest extends FormRequest
{
public function rules()
{
return array_merge(
TransportationRequest::getRules(),
SaleRequest::getRules(),
PurchaseRequest::getRules()
);
}
}
Then you can use the single CombinedRequest in your controller method. Of course, if you don't like the static method approach, in your combined request rules method you could just new up each individual request and call the rules method on each of them and merge the results.
class CombinedRequest extends FormRequest
{
public function rules()
{
$transportation = (new TransportationRequest())->rules();
$sale = (new SaleRequest())->rules();
$purchase = (new PurchaseRequest())->rules();
return array_merge(
$transportation,
$sales,
$purchase
);
}
}
Turns out if you resolve the request, validation will kick in.
One caveat of this is that all the validation objects will not kick in at once but depending on your use case, this might be simpler.
public function store()
{
// request()->replace([ some fields to be validated ]);
resolve(TransportationRequest::class);
resolve(PurchaseRequest::class);
resolve(SaleRequest::class);
// request is valid
}
You can manually execute Request classes one by one:
public function store()
{
$isValid = true;
try{
app(TransportationRequest::class);
} catch (Illuminate\Validation\ValidationException $ex){
$isValid = false ;
}
try{
app(PurchaseRequest::class);
} catch (Illuminate\Validation\ValidationException $ex){
$isValid = false ;
}
try{
app(SaleRequest::class);
} catch (Illuminate\Validation\ValidationException $ex){
$isValid = false ;
}
if (!$isValid){
throw $ex;
}
}
If validation in one of them will fail, a user will be redirected back to previous page with an error.
You could concatinate all rules and validate manually:
$allRules = (new TransportationRequest)->rules() + (new PurchaseRequest)->rules() + (new SaleRequest)->rules();
Validator::make($request->all(), $allRules)->validate();
I know this is a pretty old question, but I got annoyed by not being able to chain form requests together so I made a composer package for this, so you don't have to.
https://github.com/sharpie89/laravel-multiform-request
I recently came up against this problem, and solved it like this:
public function rules()
{
$request1 = RequestOne::createFrom($this);
$request2 = RequestTwo::createFrom($this);
return array_merge(
$request1->rules(),
$request2->rules()
);
}
Related
I find something lost...
The problem
I need to construct 2 custom FormRequest from 1 normal Request
Let's suppose this fake scenario
First FormRequest
StoreClientRequest
Second FormRequest
UpdateClientRequest
On the Controller:
public function store(Request $request){
//Do something...
$firstRequest = new StoreClientRequest($request);
$secondRequest = new UpdateClientRequest($request);
}
Are there way to make something similar to this fake scenario.
If you really want to get an instance of your Form Requests, without resolving them from the IoC Container, you could use the createFrom method:
$firstRequest = StoreClientRequest::createFrom($request);
This will make sure it is filled with the same data as $request.
When running tests in Dusk, submitting a form generates a validation error that reads "The query field is required." The error does not occur when testing the page with manual input.
I added dd( $request ) to the first line of the controller method that handles the POST request. When I test the page manually, the system dumps the request to the page. When I test the page with Dusk, I get a screenshot that shows the line of code was never executed: The page reloads with the validation error.
I have searched the form for a hidden input with a name of 'query.' It does not exist.
I have searched the controller class and base classes for any validations that test the 'query' input. I have found none.
Can anyone point me in the right direction to figure out why the page does not work in the automated testing environment, when it does work using the serve command?
Has anyone seen a similar error in the past?
Short Answer: Check the page to verify that the selector is grabbing the correct form. In this case, the tester forgot that a form existed in the menu bar. The test was clicking the button in the menu bar instead of in the main page content.
Original Text:
I guess sometimes you just need to walk away and come back to the problem. I was so focused on the form at the center of the page that I overlooked the form in the menu bar that has an input with the name 'query'.
I was clicking the wrong button with my Dusk commands, because my selector applied to multiple buttons on separate forms.
For example, we can take as Post Model With PostController.
Your store function may look like
public function store(Request $request)
{
Post::create($request->all());
return redirect()->route('post.index')->with('success','PostCreated Successfully');
}
If you add dd function at the begining of the function it will work ie) dd($request->all());
But if you use custom requests, for example PostStoreRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostStoreRequest 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 [
'post_name' => 'required',
];
}
/**
* Custom message for validation
*
* #return array
*/
public function messages()
{
return [
'post_name.required' => 'Enter Post Name',
];
}
}
and the PostController#store
public function store(PostStoreRequest $request)
{
Post::create($request->all());
return redirect()->route('post.index')->with('success','PostCreated Successfully');
}
Even though if you add dd at the top of function because it validated the request first and it will enter into the function.
Currently, my controller delegates the CRUD operations to a CRUDService.
When for example, I need to update a resource, this request pass through the validator first, and then to the CRUDService. If this one finds that the resource does not exists, it will respond with 404.
Here's the snippet that performs this operation:
public function update($id, Request $input)
{
if ($this->notFound($id)) {
return $this->response->notFound();
}
$input = $this->converter->convert($input);
$this->model->where("id", $id)->update($input);
return $this->response->updated();
}
private function notFound($id)
{
return !count($this->model->find($id));
}
I'm wondering if there's a way to perform this check inside the FormRequest class... Any suggestions?
Yes there is. Check out the exists Laravel validation rule here:
https://laravel.com/docs/5.3/validation#rule-exists
exists:table,column
The field under validation must exist on a given database table.
Basic Usage Of Exists Rule
'state' => 'exists:states'
I am trying to test the store method in the simplest way possible. I don't necessarily need Mockery, I'm fine with actually adding to the database. I couldn't get Mockery to work anyway.
Now I have this:
public function testStore()
{
$data = ['name' => 'TestClub', 'code' => 'TCL'];
Input::replace($data);
$this->route('POST', 'clubs.store', $Input::all());
$this->assertResponseOk();
}
This is my controller method:
public function store() {
$validator = Validator::make($data = Input::all(), Club::$rules);
if ($validator->fails()) {
return Redirect::back()->withErrors($validator)->withInput();
}
Club::create($data);
return redirect(Session::get('backUrl'));
}
I get a return code 500, I have no idea why. Can I somehow see the request it's sending?
I am using Laravel 5.0
First, never use Input::all(). You may not know what's in the form when it's submit, instead use only.
However, you also shouldn't be putting validation logic in the Controller if you can help it. Instead, you should make a new Request and put your validation rules in the rules array. Let's look at that
php artisan make:request StoreUserRequest
Then inside of this Request, you'll add return true to your authorize function (or whatever logic you need to identify if the user should be able to make the request), and then your rules to the rules() function (to the array within the rules function, to be more specific).
Now add your new StoreUserRequest as a dependency to the first argument of the store() function.
public function store(App\Http\Requests\StoreUserRequest $request)
Now use the $request->only() method to get your fields back.
Club::create($request->only(['field1', 'field2', 'blob1', 'text1']);
Furthermore, make sure that any fields you wish to add data to are listed within the protected $fillable array in your Club model.
protected $fillable = ['field1', 'field2', 'blob1', 'text1'];
This should do it.
I have contoroller action, where I want to render form and handle it submitted, so here what I am do:
public function add(PostFormRequest $request)
{
if (Input::get('title')) {
$post = new Post(Input::all());
if ($post->save()) {
return redirect('posts');
}
}
return view('add_post');
}
But I getting redirect loop, when I use just public function add() all fine, what I am doing wrong?
I'm guessing that your form request probably contains some validation rules. Those will obviously fail on the GET request and it will redirect, fail, redirect and so on. Form requests are always injected and validated. Not just on POST requests. The best and in general more clean solution is to just split it up in two different methods.