Laravel Custom Form Validation with Conditional Messages - php

I am building a Laravel-based eCommerce solution with a shopping cart. I have a page for each product where you can choose a product option, whether you want to subscribe, how often you want to receive shipments, the quantity you want, and then add it to your shopping cart. I want to validate this combination of choices before adding to the customer's shopping cart. To validate, I need to check against the database for this product option to make sure that it can be subscribed to, that the quantity chosen is below the maximum quantity allowed for that product, etc.
With CodeIgniter, I would write a custom callback, pull in the inputs from the POST request, and check everything in one call to the database. If there was a validation failure, I would then output a relevant conditional message for what caused the validation to fail. (i.e. "You can't subscribe to that product" or "The quantity you selected is more than the maximum quantity allowed for the product.")
This seems a little more difficult to accomplish with Laravel. I was looking through the documentation and articles online, and I found an approach that seems to work.
I have my validation method in my model, CartLine. Then, in a new Form Request, I overwrite the getValidatorInstance method with the following:
public function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->after(function() use ($validator) {
$cartLine = new CartLine;
$cartLine->validate($this->all());
if (!$cartLine->valid()) {
$validator->errors()->add('product', $cartLine->errors());
}
});
return $validator;
}
I have a couple of questions:
When I first experimented with this solution, I tried to use method injection for the instance of CarLine inside the closure, like so:
$validator->after(function(Validator $validator, CartLine $cartLine) use ($validator) {
$cartLine->validate($this->all());
if (!$cartLine->valid()) {
$validator->errors()->add('product', $cartLine->errors());
}
});
But I kept getting an error saying that the closure was expecting an instance of CartLine and none was given. I fixed this by newing up CartLine inside the closure, but it's obviously not as elegant. Why doesn't method injection work here?
Does anyone have a better or more elegant solution for how to accomplish this type of custom validation with conditional error messages in Laravel?

you may pass the custom messages as the third argument to the Validator::make method:
$messages = [
'required' => 'The :attribute field is required.',];
$validator = Validator::make($input, $rules, $messages);
Where :attribute will be replaced by actual name of field.
Also you can provide validations only for particular attribute as below
$messages = [
'email.required' => 'We need to know your e-mail address!',];

Related

In Yii2 how to validate multiple attributes together. Using AJAX

I have tried following this https://github.com/samdark/yii2-cookbook/blob/master/book/forms-validator-multiple-attributes.md
Unfortunately, it already has an issue raised that it no longer works with updates to Yii2 and I can't find any way to make it work.
I have two attributes which must be unique from each other. (Both are an array of strings.)
My validation logic works, but I can't find a way to display errors (or remove errors) on both attributes at the same time.
I have tried making a custom validator class, and also an inline validator, with the same problem:
When entering data in the form, only the attribute being edited will have its error message updated.
Below is the simplest version of my code that doesn't work, I want to display an error on both attributes after editing either one of them.
public function rules()
{
return [
[['attribute1', 'attribute2'], 'customValidator'],
];
}
// declare validator
public function customValidator($attribute, $params)
{
$this->addError('attribute1', 'error');
$this->addError('attribute2', 'error');
}

field considered empty in FormRequest and validations

I have a template that allows adding new lines. Example: I want to add multiple products at once to a store, so I can add them all at once, through those lines that replicate themselves. The problem is that I need to validate these fields, they are all mandatory. I'm implementing so that I can walk through each one and leave it compulsory. However, I came across another problem, Laravel is telling me that my field is empty, but it is not. I would like to know how I can solve it, thank you in advance.
Here is my code to FormRequest
public function rules()
{
$rules = [];
foreach($_POST['esp'] as $key => $esp){
$name_field = "esp[" . $key . "]" . "[esprqe]"; //name of my field is complicated even as it is time based
$rules[$name_field] = 'required';
}
return $rules;
}
My template is big, no need to show.
message I receive: esp[1553533952015][esprqe]: ["The esp[1553533952015][esprqe] field is required."]
My input is not empty, I do not know what the problem is
You state the name of your field 'esprqe' is time based and complicated. This is likely the cause of your problem.
The esp['.$key.'] field is probably fine, as this field is transferred from your form. However, the value that goes into esprqe, if that is being generated in your server side code above (I don't know, you haven't provided how this field is generated), and if the rules are looking for that field to match a time-based generated field on the form... those values won't match and you will have a non-value coming into your method above.
This would very likely generate a message
["The esp[1553533952015][esprqe] field is required."]
If this is your issue, you can test by first creating a simple numbered index for the esprqe field that will be matched on both form and method. If this succeeds, you can then increase the complexity using a common generator so that the rules section knows exactly what the name of the field is. Time can't be common between form creation and rules creation - thus perhaps the reason for the failure.
You need use "dot notation" to validate attributes within an array.
$name_field = "esp.$key.esprqe";
Laravel Docs: validating arrays

Testing for POST in Yii 2.0

In my controllers that Gii creates it is common to see the following:
if($model->load(Yii::$app->request->post()) && $model->save()){
//.....do something such as redirect after save....//
}else
{
//.....render the form in initial state.....//
}
This works to test whether a POST is sent from my form && the model that I am specifying has saved the posted information (as I understand it).
I've done this similarly in controllers that I have created myself but in some situations this conditional gets bypassed because one or both of these conditions is failing and the form simply gets rendered in the initial state after I have submitted the form and I can see the POST going over the network.
Can someone explain why this conditional would fail? I believe the problem is with the 'Yii::$app->request->post()' because I have removed the '$model->save()' piece to test and it still bypasses the conditional.
Example code where it fails in my controller:
public function actionFreqopts()
{
$join = new FreqSubtypeJoin();
$options = new Frequency();
$model = new CreateCrystal();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
$model->insertFreqopts();
return $this->redirect(['fieldmap', 'id' => $join->id]);
} else {
return $this->render('freqopts', ['join' => $join, 'options' => $options]);
}
}
My initial thought was that I'm not specifying the correct "$model" in that I'm trying to save the posted data to FreqSubtypeJoin() in this case and the $model is CreateCrystal(); however, even when I change the model in this conditional it still fails. It would be helpful if someone could briefly explain what the method 'load' is actually doing in layman's terms if possible.
The load() method of Model class is basically populating the model with data from the user, e.g. a post query.
To do this it firstly loads your array of data in a form that matches how Yii stores your record. It assumes that the data you are trying to load is in the form
_POST['Model name']['attribute name']
This is the first thing to check, and, as long as your _POST data is actually getting to the controller, is often where load() fails, especially if you've set your own field names in the form. This is why if you change the model, the model will not load.
It then check to see what attributes can be massively assigned. This just means whether the attributes can be assigned en-mass, like in the $model->load() way, or whether they have to be set one at a time, like in
$model->title = "Some title";
To decide whether or not an attribute can be massively assigned, Yii looks at your validation rules and your scenarios. It doesn't validate them yet, but if there is a validation rule present for that attribute, in that scenario, then it assumes it can be massively assigned.
So, the next things to check is scenarios. If you've not set any, or haven't used them, then there should be no problem here. Yii will use the default scenario which contains all the attributes that you have validation rules for. If you have used scenarios, then Yii will only allow you to load the attributes that you have declared in your scenario.
The next thing to check is your validation rules. Yii will only allow you to massively assign attributes that have associated rules.
These last two will not usually cause load() to fail, you will just get an incomplete model, so if your model is not loading then I'd suggest looking at the way the data is being submitted from the form and check the array of _POST data being sent. Make sure it has the form I suggested above.
I hope this helps!

Conditional validation with Phalcon

There's any way to do something like this using Phalcon's validation feature:
$validation->add('telephone', new PresenceOf( [
'message' => 'The telephone is required',
'onFail' => //Validate this,
'onSuccess' => //Validate that
]));
EDIT:
Well, I asking for this because I have some validations that should run only if 2 or 3 conditions are matched in any order, so the cancelOnFail property wouldn't help me that much here.
Maybe if I create a custom validation group like $validation->add('stuff', new ComplexValidation()) but the order still a problem. The way that this validation system works is quite linear. Taking theses considerations, how can I implement more complex validations with Phalcon?
Yes. Phalcon has a validation system which works similarly. The following code would be used to ensure the 'telephone' field is supplied:
$this->validate(new PresenceOf(
array(
"field" => "telephone",
"message" => "The telephone is required"
)
));
This validator is called on the model in the validation() function. According to the documentation, the PresenceOf validator is automatically added for fields marked as ‘not null’ in the model’s table. If I understand this correctly, you actually do not need to insert this code if your field is ‘not null’, though you can do so if you want to override the default ‘validation failed’ message for this specific form. You can modify the default validation message (and supply translations) by overriding the getMessages() function in your model. There’s additional information on Phalcon's validation message system here.
The failure message is aggregated with other messages from other field validations (such as Uniqueness, for example). For each failed validation, you can access the message, field, type (and originating model). The following code would output all the information contained in the message, for each failed validation:
if ($form->save() == false) {
foreach ($form->getMessages() as $message) {
echo "Message: ", $message->getMessage();
echo "Field: ", $message->getField();
echo "Type: ", $message->getType();
echo "Model: ", $message->getModel();
}
}
I’ve noticed two different approaches to informing users of validation messages: placed together before the form (as shown here) or placed near the form input itself. If you want to output the message close to the input, you may need to iterate over each message (from getMessages()) and check for the field name which matches your input, then echo the message using getMessage().
I’m very new to Phalcon myself, so I haven’t explored all the functionality of validators, yet. There is an event system that you can use to insert code beforeValidation and afterValidation. That may be one place to look for outputting a successful validation message.

Laravel custom validation issue

I am trying to register a custom validation rule but it does not seem to work. I need either of 2 fields to be filled in. One is a URL(link) field and other is a File input(file_upload).
Here is my custom validation:
Validator::register('file_check', function($attribute, $value, $parameters) {
if (!trim($value) == "" || array_get(Input::file($parameters[0]), 'tmp_name')) {
return true;
}
return false;
});
$messages = array(
'file_check' => 'Please upload a file or provide a link to files.',
);
$rules = array(
'link' => 'url|file_check:file_upload',
'file_upload' => 'mimes:jpg,jpeg,gif,png,psd,ai,bmp,xls,xlsx,doc,docx,zip,rar,7z,txt,pdf'
);
$validation = Validator::make(Input::all(), $rules, $messages);
if ($validation - > fails()) {
return Redirect::to('page') - > with_errors($validation - > errors) - > with_input();
}
Need help :)
EDITED
Also, I just noticed that the validation rule should accept "PSD" files but when I try to upload a PSD file it redirects with the error "Invalid file type".
I am maybe late in party but may be somebody will find it useful, in case you need to create implicit rule which will be called even if field is not present in Input (like required,required_if....) use
Validator::extendImplicit( 'validator_name', function($attribute, $value, $parameters)
{
});
Check this out
I was just struggling with this myself! It turns out that except when a few specific rules are applied to them, Laravel doesn't pass empty fields through the Validator at all. So a custom either-this-or-that rule can't work, since at least one of the two fields is likely to not be visible to it.
You can get around this by moving from the registering-a-new-rule approach to the alternate extend-the-Validator-class approach. Your new class will inherit all the methods of the standard Validator, including a method called "implicit" (you can find the original on line 215 of the standard Validator class), which specifies a whitelist of rules that Laravel should pass fields along to even if they are empty. Add your new rule to that list and you should be good to go.
Jason is right, but there is one thing that can be confusing.
Laravel's 'registering a new rule' approach uses the syntax 'Validator::extend(...'. As described elsewhere, this is convenient when you want to customize in a special situation. However, if you want to add a number of reusable rules, then you probably want to use the extend-the-Validator-class approach. In that case, IF you have a rule conditionally requires something, you need to override the existing implicitRules array with a new one adding your rule.
If the first rules you add don't conditionally require, you will think you have it nailed, then you will spend hours trying to figure out why your new 'RequireWhenBlaBla...' rule is invisible.

Categories