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.
Related
In my project I use Lumen and I want to implement some kind of validation for my GET request.
The example URL would look like this:
[URL]/blubb/bla?tags[]=1&tags[]=2
In my code I validate the URL like this:
public function handleRequest(Request $request)
{
try {
$validatedData = $this->validate($request, [
'tags' => 'nullable|array'
]);
} catch (ValidationException $th) {
// return Error.
}
// go on.
}
My problem is that if a user uses an URL like this one, the validation does not trigger and the "go on." part is called.
[URL]/blubb/bla?invalidParameter=1
Is there a way to only allow a single "kind" of GET Parameter?
EDIT:
The "tags" is nullable because my API endpoint can be called without any GET parameters.
You could get the full array with $request->all() and have a look at the keys.
$paramValidation = $request->all()
unset $paramValidation['tags'];
if (count($paramValidation)) {
// error
}
However, maybe you just want to ignore other params. Have a look at the method $request->validated().
I use AuthComponent with CakePHP 3.8 and now I need to do some logic in Model buildRules method but for this I need to get the current user ID.
Is there any way to pass/retrieve it without using hacks such as accessing directly from the session.
I know that it is possible to pass id via validator from controller as described in CakePHP's documentation
https://book.cakephp.org/3/en/core-libraries/validation.html#using-custom-validation-rules
And it works for validation, however, I am unable to access the validator from the inside of build rules.
When I do as described in here, I get an empty object.
https://book.cakephp.org/3/en/orm/validation.html#using-validation-as-application-rules
It seems that I am able to attach new validation rules but unable to retrieve the "Passed" provider to get the User ID.
It seems a trivial thing but a I spent quite a few hours trying to get the id in a proper way.
OK, After working a bit more, I found how to retrieve user_id inside build rules. Might be helpful to someone.
Do this in the controller
$this->ExampleModel->validator('default')->provider('passed', [
'current_user' => $this->Auth->user('id')
]);
And then put this in you buildRules method
public function buildRules(RulesChecker $rules)
{
$user_id = $this->validator('default')->provider('passed')['current_user'];
$rules->add(
function ($entity, $options) use($user_id) {
//return boolean for pass or fail
},
'ruleName',
[
'errorField' => 'some_id',
'message' => 'Some ID field is inconsistent with App logic.'
]
);
return $rules;
}
The proper way to handle this is usually using either events, or saving options.
For example to make the ID available for the application rules of all tables, you could do something like this:
$this->getEventManager()->on('Model.beforeRules', function (
\Cake\Event\Event $event,
\Cake\Datasource\EntityInterface $entity,
\ArrayObject $options
) {
$options['current_user'] = $this->Auth->user('id');
});
It would be available in the $options argument of the rule accordingly, ie as $options['current_user'].
For a specific save operation you can pass it to the options of the save() call:
$this->ExampleModel->save($entity, [
'current_user' => $this->Auth->user('id')
]);
There's also plugins that can help you with it, for example muffin/footprint.
Some form fields in my laravel 5.5 application have validation rules that run against a remote API and take quite some time. I would thus only want to run these expensive checks when the field value changes (is different from the value currently stored in the model).
Is there something that implements this already, e.g. as a rule similar to sometimes?
I would image it like this: only_changed|expensive_validation|expensive_validation2. The latter rules would only be executed when the field value has changed.
Assuming you are using a custom Request class, the rules() method expects an associative array to be returned before it applies the validation.
You can build your array of rules dynamically using the request contents before applying the validation like so:
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$validation_array = [
'name' => 'required|string|max:255',
];
if ($this->my_field === $some_condition) {
$validation_array = array_merge($validation_array. [
'my_field' => "required|expensive_validation|expensive_validation2"
]);
}
return $validation_array;
}
Note: I haven't run this yet but the principle should be fine.
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()
);
}
Laravel Form Validation is great, except that it doesn't filter out extraneous keys when array notation is used.
I am type hinting the form request in my controller.
public function saveEdit(Post\EditRequest $request)
{
$valid = $request->validated();
}
If my form has address_1 and address_2, and the user spoofs address_3, the spoofed value will not turn up in $valid.
However, if my form uses array notation, such as venue[address_1], and venue[address_2], then the user can spoof venue[address_3], and it will turn up in $valid.
Has anyone else come across this? How did you deal with it?
It seems like the validated() method on the form request class needs to operate recursively.
Unfortunately, Laravel doesn't include built-in support for validating array keys yet, only array values. To do so, we need to add custom validation rules.
However, with alternative protection like $fillable model attributes and the array_only() helper function, creating these rules is a lot of extra work for a very unlikely edge case. We provide validation feedback for expected user error and sanitize to protect our data from unexpected input.
If the user spoofs a key, as long as we don't save it, or we filter it out, they won't be too concerned if they don't see a pretty validation message—it doesn't make sense to validate a field that shouldn't exist in the first place.
To illustrate how sanitization works, here's a Venue model that declares its fillable attributes:
class Venue extends Model
{
protected $fillable = [ 'address_1', 'address_2' ];
...
}
Now, if a malicious user attempts to spoof another attribute in the array:
<input name="venue[address_1]" value="...">
<input name="venue[address_2]" value="...">
<input name="venue[address_3]" value="spoof!">
...and we use the input array directly to update a model:
public function update(Request $request, $venueId)
{
$venue = Venue::find($venueId);
$venue->update($request->venue);
...
}
...the model will strip out the additional address_3 element from the input array because we never declared it as a fillable field. In other words, the model sanitized the input.
In some cases, we may need to simply sanitize array elements without using Eloquent models. We can use the array_only() function (or Arr:only()) to do this:
$venueAddress = array_only($request->venue, [ 'address_1', 'address_2' ]);
you can use dot notation
public function rules()
{
return [
'venue.address_1' => 'required',
'venue.address_2' => 'required',
'venue.address_3'=>'required',
];
}
and if you are using dynamic array you can do same by
public function rules()
{
return [
'venue.address_.*' => 'required'
];
}