Yii2: safe validator on condition - php

I am trying to use this validation rule in my model, but it's not working.
I mean it always remains safe even if select other option.
[['dhanwantri_bill_number'], 'safe',
'when' => function($model) {
return $model->bill_type =='d';
},
'whenClient' => "function (attribute, value) {
return $('#opdtestbill-bill_type').val() == 'd';
}"
],
Am I doing anything wrong?
is there any alternative solution to achieve the same.
Thanks.
Rule for bill_type is like
[['bill_type'], 'string', 'max' => 20],
[['bill_type','test_name','date'], 'required'],
Edit
safe attribute public properties as per doc
$when - callable - A PHP callable whose return value determines
whether this validator should be applied. yii\validators\Validator
$whenClient - string - A JavaScript function name whose return value
determines whether this validator should be applied on the client
side. yii\validators\Validator

As the 'safe' validator just tells that an attribute may be set by massive assignment, the approach is not appropiate. It just says that when you use the load() method the attribute can get a value. And if not marked as 'safe' it doesn't prevent setting a value with e.g. $model->dhanwantri_bill_number = 'asdf'. So it is not a proper solution.
More precisely: the 'safe' attribute does not have an effect when $model->validate() (which is usually called with $model->save()) gets called. It is only used when $model->load() is called. If you look into the source code of the SafeValidatior class you see that nothing happens with this validator. The validator doesn't do anything. It is just a marker (you may want to compare it to e.g. RequiredValidator). And with load() the 'when' expression is not used. So you can say 'safe' doesn't work with 'when'. The safe validator may get used when the rule gets evaluated but its validateAttribute() is empty so nothing happens in that point in time.
Besides the whenClient in your code doesn't make sense. What should happen here?
I guess there are several ways of realizing that. One idea would be to let the controller set the attributes not by load(), rather set them explicitely and check there if $model->dhanwantri_bill_number should be set or not. Or you could use load() and then revert the attribute after loading according to what $model->bill_type is set.
Or you could implement a setter method for dhanwantri_bill_number in your model and choose there if the attribute gets set or not. Maybe scenario dependent.

From Yii2 doc:
By default, an active attribute is considered safe and can be massively assigned. If an attribute should NOT be massively assigned (thus considered unsafe), please prefix the attribute with an exclamation character (e.g. '!rank').
Consider to use scenarios to handle your problem.
http://www.yiiframework.com/doc-2.0/guide-structure-models.html#scenarios

Related

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!

Is there a validation rule for "not present"?

I need to check if the key is not set in the array using Laravel validator.
That would be the complete opposite of the "required" validation rule.
Basically the array will be passed to update method if it passes the validation and I want to make sure one column will not be updated.
Is there a way to check if the value "is not present"?
Thank you
EDIT:
I'm currently using Laravel 5
EDIT:
I managed to write my own validation rule by calling Validator::extendImplicit. However I get $value as null to my validation function both when I set it to null or when I don't set it at all. Is there a way to check if the value is set?
I believe I found a solution:
$validator->extendImplicit('not_present', function($attribute, $value, $parameters)
{
return !array_key_exists($attribute, $this->data);
});
I'm not calling extendImplicit statically because the Validator class object is injected to the controller of my class.
I need to access $this->data ($this referring to the Validator object) to make sure the key doesn't exist in the array being validated.
Based on the #MaGnetas answer I came up with this 2 rules that can be applied on any model.
I'm using Laravel 5.4 so putting this lines on your AppServiceProvider.php should work.
The first approach (extendImplicit and array_key_exists)
Validator::extendImplicit('not_present', function($attribute, $value, $parameters, $validator)
{
return !array_key_exists($attribute, $validator->getData());
});
Ussing $validator->getData() we could use the Validator statically.
The second approach (extend and false)
Validator::extend('not_present', function($attribute, $value, $parameters, $validator)
{
return false;
});
You could use extend because we don't need the rule to be executed if the data has not the property (because that's exactly what we want right?)
On the docs:
By default, when an attribute being validated is not present or contains an empty value as defined by the required rule, normal validation rules, including custom extensions, are not run. more info
Important: The only difference is that using extend, empty strings will not run the validation. But if you have setting TrimStrings and ConvertEmptyStringsToNull on your middleware (which AFAIK is the default option) there will be no problem
No there is no build in validtion rule for this, but you can create your own validation rule.
The simplest way to do this:
Validator::extend('foo', function($attribute, $value, $parameters)
{
// Do some stuff
});
And check if key exists.
More information:
http://laravel.com/docs/4.2/validation#custom-validation-rules
For people looking for the not_present logic in 7.x apps (applicable for all versions), remember that you can simply use the validated data array for the same results.
$validatedKeys = $request->validate([
'sort' => 'integer',
'status' => 'in:active,inactive,archived',
]);
// Only update with keys that has been validated.
$model->update(collect($request->all())->only($validatedKeys)->all());
my model has more attributes but only these two should be updatable, therefore I too were looking for an not_present rule but ending up doing this as the results and conceptual logic is the very same. Just from another perspective.
I know this question is really old but you can also use
'email' => 'sometimes|required|not_regex:/^/i',
If the email is present in the request, the regex will match any characters in the request and if the email is an empty string but is present in request the sometimes|required will catch that.

CakePHP 1.3 - Validate input is numeric within view/controller?

I need to validate that a form input is numeric in CakePHP 1.3. However, the input is not a property of the model, so I don't think I should try to set the validation for it in the model. Instead, some calculations are done on that input and the results are used in the resulting model object. How can I validate this in the view/controller? That is, check that what the user input was numeric and show a validation error message if not before passing it through the calculations? Thanks!
There's nothing wrong with defining model validation rules for non-existing / calculated fields, but you can also use the Validation class which might be cleaner. See 1 and 2.
If you use jquery at least you don't have to do a full page reload to check. Especially if it's only for one value. Just another option, see if it helps!
if($('#Field').val() != "")
{
if(!($.isNumeric($('#Field').val())) {
alert('value must be numeric');
}
}

How to Set Form Validation Rules for CodeIgniter Dynamically?

With the new version of CodeIgniter; you can only set rules in a static form_validation.php file. I need to analyze the posted info (i.e. only if they select a checkbox). Only then do I want certain fields to be validated. What's the best way to do this, or must I use the old form validation class that is deprecated now?
You cannot only set rules in the config/form_validation.php file. You can also set them with:
$this->form_validation->set_rules();
More info on: http://codeigniter.com/user_guide/libraries/form_validation.html#validationrules
However, the order of preference that CI has, is to first check if there are rules set with set_rules(), if not, see if there are rules defined in the config file.
So, if you have added rules in the config file, but you make a call to set_rules() in the action, the config rules will never be reached.
Knowing that, for conditional validations, I would have a specific method in a model that initializes the form_validation object depending on the input (for that particular action). The typical situation where I've had the need to do this, is on validating shipping and billing addresses (are they the same or different).
Hope that helps. :)
You could write your own function which checks whether said checkbox is selected, and applies the validation manually.
function checkbox_selected($content) {
if (isset($_REQUEST['checkbox'])) {
return valid_email($content);
}
}
$this->form_validation->set_rules('email', 'Email', 'callback_checkbox_selected');
If you want to avoid writing your own validation function, I came across this site which suggests that, if you're dynamically setting your rules using the Form Validation class, you can simply build up the rule string argument to set_rules() dynamically.
You first test the POST data to determine if your condition is satisfied (eg. checkbox selected) and then, as necessary, add a "|required" to the rule string you pass to set_rules().

Handling input with the Zend Framework (Post,get,etc)

im re-factoring php on zend code and all the code is full of $_GET["this"] and $_POST["that"]. I have always used the more phpish $this->_request->getPost('this') and $this->_request->getQuery('that') (this one being not so much logical with the getquery insteado of getGet).
So i was wondering if my method was safer/better/easier to mantain. I read in the Zend Framework documentation that you must validate your own input since the request object wont do it.
That leaves me with 2 questions:
What is best of this two? (or if theres another better way)
What is the best practice for validating php input with this methods?
Thanks!
I usually use $this->_request->getParams(); to retrieve either the post or the URL parameters. Then I use the Zend_Filter_Input to do validation and filtering. The getParams() does not do validation.
Using the Zend_Filter_Input you can do application level validation, using the Zend Validators (or you can write your own too). For example, you can make sure the 'months' field is a number:
$data = $this->_request->getParams();
$validators = array(
'month' => 'Digits',
);
$input = new Zend_Filter_Input($filters, $validators, $data);
Extending Brian's answer.
As you noted you can also check out $this->_request->getPost() and $this->_request->getQuery(). If you generalize on getParams(), it's sort of like using the $_REQUEST superglobal and I don't think that's acceptable in terms of security.
Additional to Zend_Filter, you may also use simple PHP to cast the required.
E.g.:
$id = (int) $this->_request->getQuery('id');
For other values, it gets more complicated, so make sure to e.g. quote in your DB queries (Zend_Db, see quoting identifiers, $db->quoteIdentifier()) and in views use $this->escape($var); to escape content.
You can't write a one-size-fits-all validation function for get/post data. As in some cases you require a field to be a integer and in others a date for instance. That's why there is no input validation in the zend framework.
You will have to write the validation code at the place where you need it. You can of course write some helper methods, but you can't expect the getPost() to validate something for you all by itself...
And it isn't even getPost/getQuery's place to validate anything, it's job is to get you the data you wan't, what happens to it from there on should not be it's concern.
$dataGet = $this->getRequest()->getParam('id',null);
$valid = new Zend_Validate_Digits();
if( isset($dataGet) && $valid->isValid($dataGet) ){
// do some...
} else{
// not set
}
I have always used the more phpish $this->_request->getPost('this') and $this->_request->getQuery('that') (this one being not so much logical with the getquery insteado of getGet).
What is best of this two? (or if theres another better way)
Just a quick explanation on the choice of getQuery(). The wording choice comes from what kind of data it is, not how it got there. GET and POST are just request methods, carrying all sorts of information, including, in the case of a POST request, a section known as "post data". A GET request has no such block, any variable data it carries is part of the query string of the url (the part after the ?).
So, while getPost() gets the data from the post data section of a POST request, getQuery() retrieves data from the query string of either a GET or POST request (as well as other HTTP Request methods).
(Note that GET Requests should not be used for anything that might produce a side effect, like altering a DB row)
So, in answer to your first question, use the getPost() and getQuery() methods, this way, you can be sure of where the data source (if you don't care, getParams() also works, but may include additional data).
What is the best practice for validating php input with this methods?
The best place to validate input is where you first use it. That is to say, when you pull it from getParams(), getPost(), or getQuery(). This way, your data is always correct for where you need it, and if you pass it off, you know it is safe. Keep in mind, if you pass it to another Controller (or Controller Action), you should probably check it again there, just to be safe. How you do this depends on your application, but it still needs to be checked.
not directly related to the topic, but
to insure that you get an number in your input, one could also use $var+0
(however if $var is a float it stays a float)
you may use in most cases
$id = $this->_request->getQuery('id')+0;

Categories