After studying Miles Jones cupcake forum plugin, I have a couple of questions here:
1)Is it compulsory for each field (that appears in a model's validation rules) to be a field in a database table? I found the following validation rules in the User model of cupcake forum plugin. oldPassword and newPassword are not fields in the users table. I'm confused coz' I thought I should only make validation rules for fields of table.
public $validate = array(
'username' => array(
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'That username has already been taken',
'on' => 'create'
),
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'Please enter a username'
)
),
'password' => array(
'rule' => 'notEmpty',
'message' => 'Please enter a password'
),
'oldPassword' => array(
'rule' => array('isPassword'),
'message' => 'The old password did not match'
),
'newPassword' => array(
'isMatch' => array(
'rule' => array('isMatch', 'confirmPassword'),
'message' => 'The passwords did not match'
),
'custom' => array(
'rule' => array('custom', '/^[-_a-zA-Z0-9]+$/'),
'message' => 'Your password may only be alphanumeric'
),
'between' => array(
'rule' => array('between', 6, 20),
'message' => 'Your password must be 6-20 characters in length'
),
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'Please enter a password'
)
),
'email' => array(
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'That email has already been taken',
'on' => 'create'
),
'email' => array(
//'rule' => array('email', true),//boolean true as second parameter verifies that the host for the address is valid -- to be uncommented once website is uploaded
'rule' => array('email'),
'message' => 'Your email is invalid'
),
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'Your email is required'
)
)
);
2)Does each form field need to be a field in a database table?
For example when I ask a user to signup there will be: username, email addr, password and confirm password. But confirm password field doesn't need to be a field in a table right?
Is that a good practice?
I found the following isMatch function in form_app_model.php:
/**
* Validates two inputs against each other
* #access public
* #param array $data
* #param string $confirmField
* #return boolean
*/
public function isMatch($data, $confirmField) {
$data = array_values($data);
$var1 = $data[0];
$var2 = (isset($this->data[$this->name][$confirmField])) ? $this->data[$this->name][$confirmField] : '';
return ($var1 === $var2);
}
Can someone tell me on what is === in the last line of the above code?
Thank you.
That mean exactly equal (without type conversion) . For example: if y = 25, then y === 25 is true and y == '25' is true, but y === '25' is not true.
== means equal
=== mean identical
http://www.techsww.com/tutorials/web_development/php/tips_and_tricks/difference_between_equal_and_identical_comparison_operators_php.php
Related
I have a form which have add more functionality. user can enter their multiple education. now I want to add a validation rule for the below condition
if user started their first education in 2006 and completed in 2008
then he can not enter second education starting date 2008 or before
that
here are my validation rules
/**
* Validation
*
* #var array
* #access public
*/
public $validate = array(
'degree_type_id' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank.',
'last' => true,
),
'is_unique_degree' => array(
'rule' => 'is_unique_degree',
'message' => 'You have added same course multiple time.',
'last' => true,
),
),
'college_hospital' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank.',
'last' => true,
),
'size' => array(
'rule' => array('maxLength', 255),
'message' => 'This field must be no larger than 255 characters long.'
),
),
'year_passing' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank.',
'last' => true,
),
),
'start_date' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank.',
'last' => true,
),
'dateRule' => array(
'rule' => array('date', 'ymd'),
'message' => 'Enter a valid date in MM/YYYY format.',
'allowEmpty' => true
),
'validateDuration' => array(
'rule' => 'validateDuration',
'message' => 'This field cannot be left blank.',
'last' => true,
),
),
'end_date' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank.',
'last' => true,
),
'dateRule' => array(
'rule' => array('date', 'ymd'),
'message' => 'Enter a valid date in MM/YYYY format.',
'allowEmpty' => true
),
'validateEndDate' => array(
'rule' => 'validateEndDate',
'message' => 'End date should be greater than start date.',
'allowEmpty' => true
)
),
);
This is what I try and got success only for validating future date condition
public function validateDuration() {
$startDate = $this->data['DoctorEducation']['start_date'];
$endDate = $this->data['DoctorEducation']['end_date'];
if (strtotime(date('Y-m-d')) < strtotime($startDate)) {
return __('Education duration will not be future date');
}
if (strtotime(date('Y-m-d')) < strtotime($endDate)) {
return __('Education duration will not be future date');
}
//pr($this->data);
return true;
}
let me know if you want any other information.
Assuming I understood your question correctly, you should have a User model with a hasMany association with DoctorEducation. One approach could be to define in the User model:
var $usedDates = array;
public function beforeValidate($options = array()) {
$this->usedDates = array();
}
then the validation rules for the DoctorEducation model could use it:
public function validateStart( $dataToValidate ) {
foreach($this->ParentModel->usedDates as $usedDate) {
// Perform whatever check you need to ensure periods
// don't overlap and return errors
}
$key = count($this->ParentModel->usedDates);
$this->ParentModel->usedDates[$key]['start_date'] = $dataToValidate['start_date'];
return true;
}
public function validateEnd( $dataToValidate ) {
foreach($this->ParentModel->usedDates as $usedDate) {
// Perform whatever check you need to ensure periods
// don't overlap and return errors
}
$key = count($this->ParentModel->usedDates) - 1;
$this->ParentModel->usedDates[$key]['end_date'] = $dataToValidate['end_date'];
return true;
}
The above assumes you will be saving the User model together with the DoctorEducation model.
I want to use model validation.
lets i explain here clearly. in my view there is a file called "register.ctp". Also i have two table one is users and another is profiles, according to cakephp concept also i have two model that user model(User.php) and profile model(Profile.php).
My register.ctp contains the follow fields
name, email, address and phone all are mandatory - when i submit, name and email will store in users table and address and phone will store in profiles table
when i use model validation i have tried by using the below code that is working only for name and email but not working on for other fields.
here is my code for for model validation in user model(User.php)
public $validate = array(
'email' => array(
'blank' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Email.'
)
),
'name' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Name'
),
'address' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Address'
),
'phone' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Phone'
),
);
Thanks
You need to set the validation per model.
So in User.php:
public $validate = array(
'email' => array(
'blank' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Email.'
)
),
'name' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Name'
)
);
And in Profile.php:
public $validate = array(
'address' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Address'
),
'phone' => array(
'rule' => 'notEmpty',
'message' => 'Please enter Phone'
)
);
And you need to validate both models in the Controller:
$this->User->set($this->data());
$valid = $this->User->validates();
$this->Profile->set($this->data);
$valid = $valid && $this->Profile->validates();
See these two model methods.
Model::validateAssociated() Validates a single record, as well as all its directly associated records.
Model::validateMany() validates multiple individual records for a single model
See Model::saveAll() as well. It can be used to validate the whole set of records and associated records. This will only validate but not save the reocrds as well:
$this->saveAll($data, array('validate' => 'only'));
I recommend you to read the whole documentation about saveAll() to understand how it works and what it will return when you only validate the data.
There is no need to manually validate each model one by one like noslone suggested. The single line above will do it as well.
I wrote a custom validation method inside my Submission model that basically allows a blank input field, but once someone enters something in it, it'll validate the data entered.
The validation inside my Submission Model looks like this (All other validation rules are working except for 'description'):
var $validate = array(
'title' => array(
'title' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Please enter a title'
),
'minLength' => array(
'rule' => array('minLength', 5),
'message' => 'Please make your title longer'
),
'maxLength' => array(
'rule' => array('maxLength', 300),
'message' => 'Your title needs to be shorter'
),
),
'description' => array(
'checkDescription' => array(
'rule' => array('validateDescription'),
'message' => 'Description must be greater than 5 characters'
),
),
'source' => array(
'source' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Enter a valid source URL'
),
'website' => array(
'rule' => 'url',
'message' => 'Please enter a valid source URL'
),
)
);
My method which is also in my Submission model (below the above code) is:
public function validateDescription($data) {
if(empty($data['Submission']['description']))
return true;
if((strlen($data['Submission']['description'])) <= 5)
return false;
}
I'm not sure why this isn't working at all. In my view, I've got this to display the error:
if ($form->isFieldError('Submission.description'))
echo ($form->error('Submission.description', null, array('class' => 'error')));
The only reason I'm trying to do this, is because using the normal validation with required => false and allowEmpty => true along with a minLength and maxLength validation rule weren't behaving how I intended.
Any help would be greatly appreciated! :)
The $data variable passed into the validation method only contains array($fieldname => $value). You're also not returning true for strings over length of 5. Your method should look like this:
public function validateDescription(array $data) {
$value = current($data);
return !$value || strlen($value) > 5;
}
For some reason, I can't get this validation to work as I'd like it to, specifically with the password minLength field.
Everything else is fine (even the minLength for Username works). For some reason, when I add the same minLength rule into the password field, it just ignores it and when I actually do enter in a password, it tells me that I need to enter a password:
var $validate = array(
'email' => array(
'email' => array(
'rule' => array('email', true),
'required' => true,
'allowEmpty' => false,
'message' => 'Please enter a valid email address'
),
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'This email is already in use'
)
),
'username' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'Please enter a valid username'
),
'allowedCharacters' => array(
'rule' => '/^[a-zA-Z]+[0-9]*$/',
'message' => 'Please enter a valid username'
),
'minLength' => array(
'rule' => array('minLength', 3),
'message' => 'Please enter a longer username'
),
'maxLength' => array(
'rule' => array('maxLength', 23),
'message' => 'Please enter a shorter username'
),
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'That username is already taken'
)
),
'password' => array(
'notEmpty' => array(
'required' => true,
'allowEmpty' => false,
'message' => 'Please enter a password'
),
'minLength' => array(
'rule' => array('minLength', 4),
'message' => 'Please enter a longer password'
),
'passwordConfirm' => array(
'rule' => array('checkPasswords'),
'message' => 'Passwords must match'
)
),
);
Am I overlooking something minor? It's driving me nuts.
This happens because in Cake, the password field is automatically hashed as soon as you submit it; which will break your validation rules (a 5 character password suddenly becomes a 40+ digit hash). There are various proposed fixes for this problem.
One that sounds the most promising:
Create two fields e.g pw and pw_confirm as opposed to password and confirm_password. Use these values for your password validation (so, max length etc)
Then something like:
$this->User->set($this->data);
if ($this->User->validates()) {
// all your data validates, so hash the password submitted,
// ready for storage as normal.
$password_hash = $this->Auth->password($this->data['User']['pw'];
$this->data['User']['password'] = $password_hash;
}
This way, Cake won't automatically hash the passed that's entered - allowing your validation to function as you intended.
To visualise this, add this to your register/add user method:
function admin_add() {
if (!empty($this->data)) {
debug($this->data);
exit;
You'll get:
Array
(
[User] => Array
(
[username] => somename
[password] => 25ae3c1689d26b20e03abc049982349482faa64e
)
)
before validation takes place.
It looks like you have a small mistake in your validation array.
Every validation for a field must have a 'rule' key, and you don't have that in your 'notEmpty' validation.
Try updating the password validation like this:
<?php
array(
'password' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Please enter a password'
),
'minLength' => array(
'rule' => array('minLength', 4),
'message' => 'Please enter a longer password'
),
'passwordConfirm' => array(
'rule' => array('checkPasswords'),
'message' => 'Passwords must match'
)
))
?>
Also, note that if you're using the Auth component your password will be hashed BEFORE it is validated. This means that even if you enter a 3-character password you'll end up with a 40-character hash, which obviously will validate as being longer than the minLength.
use my change password behavior. it takes care of all those things at a single and clean place:
http://www.dereuromark.de/2011/08/25/working-with-passwords-in-cakephp/
you will most certainly have more problems later on otherwise
because you need a lost password and change password functionality as well.
and maybe a backend for the admin to simply change passwords as well
and to your problem i already commented:
"you should also use last=>true here! otherwise it doesnt make much sense"
i believe this is also part of your problem. all your rules need this param to make it work properly. the error messages will be off otherwise.
Here is the error message:
Warning (2): preg_match() [http://php.net/function.preg-match]: Delimiter must not be alphanumeric or backslash [CORE/cake/libs/model/model.php, line 2611]
This is happening when I call the following code from my controller:
$this->Account->save($this->data)
The model looks like this:
class Account extends AppModel
{
var $validate = array(
'first_name' => array(
'rule' => array('minLength', 1),
'required' => true
),
'last_name' => array(
'rule' => array('minLength', 1),
'required' => true
),
'password' => array(
'rule' => array('minLength', 8),
'required' => true
),
'email' => array(
'emailRule1' => array(
'rule' => 'email',
'required' => true,
'message' => 'You must specify a valid email address'
),
'emailRule2' => array(
'rule' => 'unique',
'message' => 'That email address is already in our system'
)
)
);
}
I found a similar problem explained here
He solved it by changing required' => true to required' => array(true) I tried that for every occurange in my model but it did not fix the problem.
The problem was I named the rule unique it should be isUnique instead.
I would have figured this out much faster with a better error message.