I have a large form (~150 inputs) and a classic Controller (with create/store methods). Also I'm using validation rules.
Store method:
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|regex:/^[a-zA-Zа-яёА-ЯЁ]+$/u|min:2|max:50',
'phone' => 'required|digits_between:9,10',
'description' => 'required|min:20|max:500',
]);
if ($validator->fails()) {
return redirect()->back()->withErrors($validator);
}
$request['created_by'] = Auth::user()->id;
Profile::create($request->all());
return redirect()->route('admin.profile.index');
}
In this case, the form doesn't store any data...
When I tried to decrease input numbers (from 150 to 30) - data was stored successfully!
After that, I increase "php_memory_limit","max_input_vars" etc. but problem wasn't fixed.
Please help to find best way to store large forms.
UPD:
Form stores successfully without validation...
I tried to set just one simple rule - "required", but problem the problem has not disappeared
You can increase max_input_vars value in the php.ini file
or change your UI and split inputs on multiple pages
It's weird, but problem left when I changed
return redirect()->back()->withErrors($validator); to
return redirect()->back()->withErrors($validator->messages());
Related
I am using form requests in Laravel for validation. I have noticed a pattern that emerges all the time and I couldn't find a solution for it on SE (or at least googling it didn't help me).
Let's say we are creating an API and we are using Laravel's apiResource to create the usual CRUD methods: store, update and delete
Obviously, when we are storing a new record, the field id is not required, but the rest of the fields might be required (and in most cases are). But when we are updating a record, we face the opposite situation. id is required while other fields are no longer required.
Is it possible to handle this situation with one form request in Laravel? Can we use Laravel's required_if in an intelligent way to avoid code duplication?
Edit: it doesn't have to be necessarily a Laravel solution. A solution that uses PHP would be fine too (as long as it is clean and follows SOLID principles).
I faced this problem lots of times and I understand your frustration...
From my point of view and professional experience, the best solution was all the time to have specific FormRequests for each case:
One for store with its own rules
Other for update with similar rules but not same as store
And last one for delete (for sure way less rules than the others and no duplication)
I know you said "no code duplication", but as it is right now, that is not possible (but you should not have code duplication as I stated before).
You said "as long as it is clean and follows SOLID principles", remember SOLID, S = Single Responsability, so if you want to solve this issue with a single FormRequest you are already breaking S. I cannot image a FormRequest with 10 or 15 inputs and those depends on if it is store, update, or delete. That is going to not be clean and for sure will not follow SOLID principles.
What about checking the method and then returning a set of rules based on that method? For instance, I have an InvoiceFormRequest with the following rules. So I'm able to use one form request for two different methods.
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
if ($this->isMethod('post')) {
return [
'template' => 'required',
'due_by_date' => 'required',
'description' => 'required',
'charge_items' => 'required',
];
}
if ($this->isMethod('put')) {
return [
'due_by_date' => 'required',
'description' => 'required',
'charge_items' => 'required',
];
}
}
Here are two possible solutions that I came up with
Use the controller method for returning the proper validation rules:
public function rules()
{
$method = $this->route()->getActionMethod();
switch($method){
case 'store':
return [
\\ validation rules
]
...
}
}
Use $this->getMethod() instead of $this->route()->getActionMethod() and validate by HTTP methods instead.
You could also store your validation rules in an array and manipulate it to reduce code duplication.
This resolves the issue of code duplication to a good extent, I think.
I cannot get the validation to work properly when updating entity data. The validation does not work after changing the initial data. The code below provides an example:
// in controller
$user = $this->Users->newEntity([
'mail' => 'wrong',
'password' => 'password',
'auth_level' => 0,
]);
debug($user->getErrors()); // Will show error: wrong email format
$user->mail = "correct#correct.correct";
debug($user->getErrors()); // Will show no errors
$user->mail = "wrong";
debug($user->getErrors()); //Will show no errors
if (TableRegistry::get('users')->save($user)) {
// This actually executes
}
My validation rule in the model is as follows:
public function validationDefault(Validator $validator): Validator
{
$validator
->email('mail')
->add('mail', 'unique',
[
'on' => ['create', 'update'],
'rule' => 'validateUnique',
'provider' => 'table',
'message' => "Email already in use"
]
);
return $validator
}
I tried creating rules with "on" => "update", but it does not help.
What I am trying to achieve is to get an entity, change the email address and save it back to database. Upon saving, the email field is neither validated for format nor uniqueness.
For the sake of completeness.
There is a difference between Application Rules and Validation Rules.
Validation Rules validate data typically coming from user's input (newEntity(), patchEntity()). Entity is regenerated. Baked ones are in "validationDefault" function within Tables.
Application Rules establish some rules for data modified within application code which is expected to be 'safe' (setters). Entity is not regenerated. Baked ones are in "buildRules" function within Tables.
"save()" function won't go through validation rules, but through application rules.
When saving data that needs to pass through validation rules because it's assigned/set within the application but it's data coming from user's input, make sure you use patchEntity().
More info: https://github.com/cakephp/cakephp/issues/6654
The solution is to always update entity data with
$model->patchEntity($entity, $newdata)
I'm currently working on application that will be storing data from many sources. The thing is that there will be 2-3 new sources each month, so I look for a way to allow users to add new keys within my application. The data will be highly sensitive, so I want to do it securely.
I don't want to enable putenv() function or read the .env file as a text.
Right now my #store action in the controller looks like that:
public function store(Request $request)
{
$this->validate($request, [
'customer_api' => 'required',
'secret_api' => 'required',
'edition_id' => 'required'
]);
$edition = $request->input('edition_id');
$secret = $request->input('secret_api');
$customer = $request->input('customer_api');
putenv("GF_SECRET_ED$edition=$secret");
putenv("GF_CUSTOMER_ED$edition=$customer");
return redirect('/editions/' . $edition . "#dev")->with('success', 'API keys added');
}
And I'm looking for a solution that will allow me to add those keys to .env, then to define those 3rd party keys in config/services.php
Couldn't find anything in docs. Working on Laravel 6.
Thank you in advance!
Modify env in your app dosen't right
you have two choices :
1- use database
2- using file and maybe with json format to easily retrieve data
Yesterday I posted a question CakePHP 3 - Using reusable validators but am still struggling to see how to validate data when it is not tied to a particular database table, or set of fields in a table.
What I'm trying to do is upload a CSV file. The data in the CSV may well end up in the database, but before any of that happens, I want to validate the file to make sure it's valid - extension is .csv, MIME type is text/csv, file size is <1 Mb. This particular validation has absolutely nothing to do with a database.
Where does such validation go, since it's nothing to do with a database table?
The approach I've used is as follows - but this does not work:
I have a UploadsController.php with an upload() method. This method handles the upload (form posts to /uploads/upload)
I have added the following to my src/Model/Table/UploadsTable.php (because I don't know where else to put such code):
public function validationDefault(Validator $validator)
{
$validator
->add('submittedfile', [
'mimeType' => [
'rule' => array('mimeType', array('text/csv')),
'message' => 'Upload csv file only',
'allowEmpty' => TRUE,
],
'fileSize' => [
'rule' => array('fileSize', '<=', '1MB'),
'message' => 'File must be less than 1MB.',
'allowEmpty' => TRUE,
]
]);
return $validator;
}
In UploadsController::upload() I have this:
if ($this->request->is('post')) {
debug($this->request->data['submittedfile']);
$uploads = TableRegistry::get('Uploads');
$entity = $uploads->newEntity($this->request->data['submittedfile']);
debug($entity);
}
No matter what file I upload, no errors are returned. Even if I comment-out the entire validationDefault method, the behaviour doesn't change.
This is becoming very frustrating as all of the documentation on Cake's website talks about data relating to DB tables. Well, what if you're trying to validate something that's nothing to do with a DB table?
I've opened this as a new question, because the last one doesn't really address this problem.
Other questions posted about this do not address this problem, because in this case they are writing the file info to a DB table, and therefore validating the file at the same time. In my case I'm not trying to do that, I just want to validate the file, before considering anything to do with the DB at all. I also want the code to validate a CSV to be re-usable (i.e. not in one specific Controller method) because if I want to check for a valid CSV in 5 different parts of the application, why should I repeat code that does the same thing all over?
Use a model-less form, it has validation built in, to validate your uploaded file. If you want your validation code to be reusable put it in a trait or separate class
In theory you could then do something like this:
$form = new CsvForm();
if ($this->request->is('post')) {
$result = $form->execute($this->request->getData());
if ($result && $this->Model->saveMany($result) {
$this->Flash->success('We will get back to you soon.');
} else {
$this->Flash->error('There was a problem submitting your form.');
}
}
Let your form validate the CSV document and return the pure CSV data, or already turn the rows into a set of entites you save.
So, i have this particular issue.
I have made a form with dropzone included, and the images are uploaded via AJAX, and everything is working from that point of view (selected images are stored, and can be deleted from the box).
So the problem is the following:
When i submit the form, and some validation error happens, the images i have uploaded are already on the server, but dont display on the dropzone form, i have to reupload them again, but then i just fill my storage with unwated data, and the images that were uploaded earlier before the validation cannot be accessed or deleted by any methods later on, so its basically junk data.
Is there any way to prevent this from happening? I would LOVE to show the already uploaded images after validation error (or refresh for example). If there cant be any solution for this, suggest me different approach.
Thanks.
I managed to get a working solution.
My thinking was following: If there are images posted, then using the $validator->fails() method i am deleting the images from the server.
Here is the complete code, if someone needs it:
// Validating the request
public function validate(Request $request){
$rules = []; //define your rules
$messages = []; //define your rules
// We make new validator with request data, rules and messages
$validator = Validator::make($request->all(), $rules, $messages);
// Deleting images if the validator fails
if($validator->fails()){
if(isset($request->images)){
foreach($request->images as $image){
// unlink your image here
}
}
return Redirect::back()->withInput()->withErrors($validator);
}
}
Then in the method store/edit (or custom method) just call:
public function store(Request $request){
// If it fails, return the redirect we defined.
if($this->validate($request)){
return $this->validate($request);
}
}
You can also define the validator directly in the store method, but since i use it few times in my Controller, i made a separate method just for it. We must return its value if we like to redirect back with the errors.
Cheers.