I'm using CodeIgniter's form validation, and I've spent a lot of time trying to fix this, with no luck.
I have this field:
<input type="text" name="user" id="user" length="20" placeholder="Username">
And I'm using this to validate:
$this->form_validation->set_rules('user', 'Username', 'trim|required|min_length[3]|max_length[20]|alpha_dash|is_unique[users.user]');
My db has a table users and user is a field in it, so I don't know what I'm doing wrong, or what the problem is. The table is empty (but I've also tried with it having records) and in phpmyadmin the "unique" icon is selected.
I know the db connection is working fine, because if I remove that rule and enter otherwise valid data and submit the form, then the user is added to the database.
Unless is_unique uses another db configuration file that I haven't configured? I don't really know. It's kind of frustrating and I'm thinking that I may as well just drop the use of a framework...
Your help would be great! Thanks.
This might/might not help: You seem to be loading the DB after running the form validation. There's also a typo uses.user.
In Transact-SQL the word "USER" is a special word. Try surrounding uses.user with a back-ticks like so: `users.user`...see if that helps.
try this :-
$this->form_validation->set_rules('user', 'Username', 'trim|required|min_length[3]|max_length[20]|alpha_dash|unique[users.user]');
it needs to load database.
$this->load->database();
I know your question is very old but I just recently encountered a similar problem with a custom form validation and after some serious debugging I found a cause and workaround.
Apparently, the CI core has a small bug: the database library isn't instanced (at least in the latest versions of CI) when calling the is_unique form validation method, thus preventing the check from actually being performed and always returning false as the validation result.
Here is the workaround for the form validation library (system/libraries/Form_validation.php)
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
// add the following line
$this->CI->load->database();
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
By adding the line after the comment, you'll make sure the database library is correctly instanced for the is_unique method and you'll get it to work. Without that line, the isset($this->CI->db) check will always return false
You could also put that line in the library's constructor, but then you'd be instancing the database library in all form validation rules which is not necessary (only is_unique needs it).
Using your code as an example, the is_unique validation rule works by looking for a field called user_name in your users database table. If the field with the same value exists it validates as false.
To make sure it runs only when the user submits a new value, you could check the posted value $this->input->post('user_name') against the value you pulled from the database to populate your form with. If they are the same, don't validate is_unique:
if($this->input->post('user_name') != $original_value) {
$is_unique = '|is_unique[users.user_name]'
} else {
$is_unique = ''
}
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean'.$is_unique);
Related
I have several Codeigniter-based projects on which I've done this exact same thing, but for some reason in the most recent project I can't get it to work.
I have some customizations made directly on the CI core (yes, I know I should extend the core rather than modifying it, but it has worked so far and it makes it easier to start a new project with all modifications already in place). One of those modifications is an additional method on the Form_validation library (system/libraries/Form_validation) called is_unique_sc which allows to check if the submitted field is not already present on a table (just as the regular is_unique method) with the sole difference of allowing a schema.table.field argument rather than just table.field (this is needed since many projects I work on require using multiple separate schemas)
This is the custom method
public function is_unique_sc($str, $field)
{
sscanf($field, '%[^.].%[^.].%[^.]', $schema, $table, $field);
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($schema.'.'.$table, array($field => $str))->num_rows() === 0)
: FALSE;
}
For comparison, this is the original is_unique method:
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
This goes along the required modification on the form_validation_lang language file in case validation returns false:
$lang['form_validation_is_unique_sc'] = 'The {field} field must contain a unique value.';
I use this, like I said, on many projects with no issues until now.
However, on my most recent project, the new user creation is failing because of this rule:
$this->form_validation->set_rules('new_email', 'Mail', 'required|trim|htmlspecialchars|strtolower|valid_email|is_unique_sc[accv_users.controllers.controller_email]');
The is_unique_sc rule is always returning false (and thus, displaying its validation error message) even if the email entered by the user is in fact unique. Removing the is_unique_sc rule allows the rest of the statement to validate properly, which discards the possibility of the whole set_rules statement to have an incorrect field name.
I have double and triple checked that the schema name is accv_users, the table name is controllers and the field name is controller_email. Query Builder is enabled as required by the method and the database connection is correctly configured and available.
Checking the output of the sscanf() function correctly assigns the schema name to $schema, the table name to $table and the field name to $field
For the sake of completeness, this is the field in the form view:
<div class="col-12 col-lg-3">
<input class="form-control" type="email" id="new_email" name="new_email" placeholder="Mail" required>
</div>
The expected result is of course that when submitting the form with an email that doesn't already exist in the table, validation passes. validation should fail only when the email is already on the table.
I've searched a lot of similar cases in SO, but no solution has helped so far.
If anybody could help me pinpoint the issue, I'd be extremely grateful.
After months of leaving this in oblivion (I worked around this failing validation by other means) I took another stab at it and cracked it!
It seems that in the latest versions of CI3, there's a bug that prevents the is_unique form validation rule from ever running correctly.
I did some deep digging and eventually found that this function:
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
would ALWAYS return false (and thus, make the form validation fail) because isset($this->CI->db) was always returning a boolean FALSE, regardless of all database-related stuff being autoloaded and Query Builder being active.
The fix was simple. Just a single line of code in the function itself:
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
// add the following line
$this->CI->load->database();
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
After adding that single line, the form validation started working correctly again. It'll still fail if the $this->CI->load->database() fails, which is to be expected considering that the is_unique rule (and my own custom rule too) depend on being able to run queries. But as long as the database connection is available, the rule works!
I'm using ZF2 and mysql, but the question is platform-independent. I have a data transfer object Organization that gets hydrated from an html form. OrganizationMapper has a save method that (1) gets Organization as an argument and (2) fills a couple of database tables one after another.
Suppose the 1st table gets filled ok, but the 2nd doesn't because one of the properties of Organization isn't set (not null constraint on a column). The user gets an error, but the 1st table is already filled. If he attempts to submit the form again, but this time with all html fields filled, all the tables get filled ok, but the 1st has a previous unused row.
How could I avoid this situation?
I thought of checking for empty values with if's in the mapper's save method, but it doesn't seem elegant. I know about the InputFilter validations in ZF2, but these check the user input in the form, they don't check things when the php code communicates with the database.
Any help?
The best way is to validate all the data before you start writing it to the database.
I didn't use ZF2 and this solution is actually framework-dependent, so you need to check the ZF2 docs. For example, in Yii, you just define validation rules for every field of the model, so you can ensure that your Organization contains all the data before you start saving it to the database, probably something similar is possible in Zend.
Note, that validation doesn't mean just to check for empty values, you may need to verify different things, like: "email is correct email like xxx#yyy.com", "name is not empty", "name length is more than 3 chars", "name length is less than 1000 chars" and so on.
For Yii it roughly looks like this:
class Organization extends ActiveRecord {
...
// here we define the validation rules
public function rules() {
return [
// name is required
['name', 'required'],
// check min / max length
['name', 'string', 'min' => 3, 'max' => 12],
// check if email is valid
['email', 'email']
];
}
}
Now you can do $organization->validate() to make sure everything is correct (also when you do $organization->save() the rules will be checked before saving to the database).
And one more solution to protect from the inconsistent data is to use transactions. In the case you write to multiple tables, you anyway need them, even if you validated everything. Unexpected things happen, so it is better to protect your saving code like this (pseudo-code):
$transaction->start();
try {
$table1->writeSomeData();
$table2->writeMoreData();
$transaction->commit();
} (catch Exception $e) {
$transaction->rollback();
}
Again, check your framework documentation, it probably supports this in some way.
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!
I have a question about enabling the is_unique() rule for form validation in CodeIgniter.
In another explanation (link), they don't include the model query builder for standard usage of is_unique()
I need to use the rule is_unique(table.field) for my id field.
What should I do for making this function work on my model file to initiate table.field from my database? Because at documentation, I didn't see an explanation for enabling the is_unique rule.
My current code is still use matching data manually, but I need to know how to use this rules
$this->form_validation->set_rules('siteid', 'Site ID', 'trim|required|max_length[100]|is_unique[site_tower.site_id_tlp]');
I have just gone through the link you posted, There are 2 ways to use such validation. If you have set in your configuration files.
With that you can use the code as is is_unique[TABLE_NAME.FIELD] and it will work automatically. But at times this logic might not necessarily meet your need and you will need something more complex.
For example lets say you have a members registration that requires you to check if the email already exists, you can run is_unique and it will work perfectly. Now let's say you want to edit the same member, running is_unique on an edit function will render the user unable to save the data if no data is edited. WHY? because is_unique would determine that the email is already registered although it belongs to the current user that is being edited.
How do we fix this? We run our own callback in which we specify the logic.
You do it by specifying a method within the controller (or a model -- slightly different) but you prefix the method name with callback_ so that it is detected.
$this->form_validation->set_rules('username', 'Username', 'callback_username_check');
This will then look for a method in your controller called 'username_check'
public function username_check($str)
{
if ($str == 'test')
{
$this->form_validation->set_message('username_check', 'The {field} field can not be the word "test"');
return FALSE;
}
else
{
return TRUE;
}
}
Of course you can use a query within the callback to check against the db rather than check for just a string as it shows in the example.
more information can be found on Ci3 documentation.
LINK
Use CTRL + F and search for callback or is_unique
You might have missed this?
$this->load->library('database');
works instantly after adding database lib.
HI guys,
I building an app using CodeIgniter and I came to a problem. I have a form with a textarea in which the user puts his text using a simple editor powered by jwysiwyg.jquery. The problem is that is need to clean this input of garbage code (link the one that comes with pasting directly from Word).
The form is validated with the form_validation library from CodeIgniter, with this rule:
array(
'field' => 'job[description]',
'label' => 'Description',
'rules' => 'trim|required|callback_clean_html'
),
Then I have a clean_html method that simply does a:
return strip_tags($text,'<a><p><br><strong><em><h3><h4><h5><ul><ol><li>');
The problem is that this is simply ignored and the original text gets inserted in the database. The method runs (I've tested). I asume it's because a callback should return TRUE or FALSE, but then xss_clean doesn't return a BOOL. The documentation isn't much help.
Any thoughs?
Thanks in advance.
I think form_validation callbacks do need to return a bool. I find that form_validation is most useful when you need to display an error message to a user usually to resubmit the form. Although the prepping functions can be convenient, they don't need to be there to validate. Why not pass the submitted string through the strip_tags function after the form is submitted but before you send it to your db?
Have you tried removing callback_ in the rule? You can do regular PHP functions like trim so this should work.
Something aI always do just to be double safe, after setting the rules for the input I also run them through this
`$string = filter_var($string, FILTER_SANITIZE_STRING);`
That will strip out the html
I too have run into situations lately where the input totally ignores the rules that have been set.
xss_clean and other CI validation functions return non-bool values. I just tested the following callback function in CI version 1.7.2:
function test_string_change($str)
{
return "$str **";
}
The string was changed successfully using callback_test_string_change. I know there were some issues with the callback functions in 1.7.0, are you using the latest version?
From what you posted, it should work. Both the "callback_" prefix and the return are correct. Validation methods can return non-bools, which will replace the value. Check the form_validation documentation, it explicitly says that.
So your problem must be in some place of the code that you didn't post.