I'm struggeling with my cakePHP validation
Scenario:
In my DB I have one table "alliances" and one "federations". In "federations" connections between alliances are stored. "alliances" has got stupid cols like id, name, etc.. federations is like this:
id, request_alliance, accept_alliance, type, requested_at, accepted_at
where request_alliance and accept_alliance are FK to alliances, type is 1 or 2.
So my model looks like this:
class Federation extends AppModel
{
// Bundarten:
// 1 - NAP
// 2 - Bund
public $displayField;
var $belongsTo = array('Alliance_requesting' => array('className' => 'Alliance', 'foreignKey' => 'request_alliance'),
'Alliance_accepting' => array('className' => 'Alliance', 'foreignKey' => 'accept_alliance'));
public $validate = array(
'request_alliance' => array('required' => true, 'allowEmpty' => false),
'accept_alliance' => array('required' => true, 'allowEmpty' => false),
'type' => array('required' => true, 'allowEmpty' => false, 'rule' => array('between', 1, 2))
);
}
Alliance (created by an former partner, I only added the $hasMany)
class Alliance extends AppModel{
var $hasMany = array(
'Federation_requesting' => array('className' => 'Federation', 'foreignKey' => 'request_alliance', 'dependent' => true),
'Federation_accepting' => array('className' => 'Federation', 'foreignKey' => 'accept_alliance', 'dependent' => true)
);
public $validationDomain = 'alliance';
public $validate = array(
'tag' => array(
'uniqueTag' => array(
'rule' => 'isUnique',
'message' => 'Alliance tag already in use'),
'between' => array(
'rule' => array('between', 2, 15),
'message' => 'Alliance tag must betwenn %d to %d characters')),
'name' => array(
'rule' => array('between', 3, 30),
'message' => 'Alliance name must between %d to %d characters'),
'image_url' => array(
'rule' => 'url',
'message' => 'Alliance picture must be a valid URL',
'allowEmpty' => true),
'homepage' => array(
'rule' => 'url',
'message' => 'Homepage must be a valid URL',
'allowEmpty' => true));
}
So far I've written a view to add a new federation between two alliances. The controller for this
class FederationsController extends AppController
{
var $name = 'Federations';
var $components = array('Message');
var $uses = array('Alliance', 'Federation');
// Requesting new federation
function add()
{
if(empty($this->data['Federation'])) {
$message = __d('federation', "Invalid Request");
$this->notice($message);
return $this->redirect(Path::overall_highscore_alliances_path());
}
$requesting_alliance_id = $this->data['Federation']['req_alliance_id'];
$req_alliance = $this->Alliance->get($requesting_alliance_id);
if(!$req_alliance) {
return $this->redirect(Path::overall_highscore_alliances_path());
}
if(!$this->Alliance->isCurrentUserDiplomat($req_alliance)) {
$message = __d('federation', "Only the diplomat is allowed to modify federations.");
$this->notice($message);
return $this->redirect(Path::alliance_path($requesting_alliance_id));
}
$accepting_alliance_id = $this->data['Federation']['acc_alliance_id'];
$acc_alliance = $this->Alliance->get($accepting_alliance_id);
if(!$acc_alliance) {
$message = __d('federation', "The target alliance for this federation doesn't exists.");
$this->notice($message);
return $this->redirect(Path::alliance_path($requesting_alliance_id));
}
$type = $this->data['Federation']['type'];
$requested_at = time();
$this->Federation->create();
$values = array('request_alliance' => $requesting_alliance_id,
'accept_alliance' => $accepting_alliance_id,
'type' => $type,
'requested_at' => $requested_at);
$saved = $this->Federation->save($values, true, array('request_alliance', 'accept_alliance', 'type', 'requested_at'));
$name = h($acc_alliance['name']);
$message = $saved ? __d('federation', "Federation with '%s' successfully requested.", $name) : '';
$this->notice($message);
$this->errors($this->Federation->validationErrors);
$this->redirect(Path::alliance_path($requesting_alliance_id));
}
}
When I try to add a new federation it the above function is called and a new row is stored inside the DB with the correct values. But the page still shows me the following errors
Could not find validation handler 1 for request_alliance
Could not find validation handler for request_alliance
Could not find validation handler 1 for accept_alliance
Could not find validation handler for accept_alliance
I can't imagine that my validation is not done, because some hours ago I had a mistake which leads to empty fields and I got the correct validation message that this field can't left blank.
Can anyone tell me where I do the mistake which leads to these errors and how to correct it?
Thanks in advance!
There is no validation rule definition
From the question, compare:
'request_alliance' => array(
'required' => true,
'allowEmpty' => false
),
With
'type' => array(
'required' => true,
'allowEmpty' => false,
'rule' => array('between', 1, 2)
)
In the first case there is no rule, rule is a mandatory key if you define the validation rules as an array.
Use the notEmpty validation rule
From the validation rules defined, it looks like there's a misunderstanding. You probably want the notEmpty validation rule:
'request_alliance' => array(
'rule' => 'notEmpty'
)
If you want to ensure that the field is present in all saves, use the required key
'request_alliance' => array(
'rule' => 'notEmpty',
'required' => true
)
There is no need to define the allowEmpty key, as it is the same as the notEmpty validation rule if false, and illogical if defined as true.
Related
I have the below setup of validation rules. For some reason, 'on' => 'create' block doesn't work. The conditions to be implemented are standard create / modify regarding email. Also, in edit section, I'm getting the error from 'on' => 'create' block.
How to validate the email? I'm using CakePHP v 2.6.1.
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('email'),
'message' => 'Kindly provide your email for verification.'
),
'maxLength' => array(
'rule' => array('maxLength', 255),
'message' => 'Email cannot be more than 255 characters.'
),
'editunique' => array(
'rule' => array('editunique'),
'message' => 'Provided Email address already exists.',
'on' => 'update'
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'Provided Email already exists.',
'on' => 'create'
)
)
);
public function editunique($email) {
// email should be one and of the logged in user only.
if ($this->find('count', array(
'conditions' => array(
$this->alias . '.id <>' => $this->data[$this->alias]['id'],
$this->alias . '.email' => $email
)
)) > 1) {
return false;
}
}
Also, I'm not getting the $this->data[$this->alias]['id'] value.
My Controller has the following section:
if ($this->Client->hasAny(array('Client.id' => base64_decode(trim($this->request->query['client_id']))))){
if ( $this->request->is('ajax') && $this->request->is('post') ){
$this->Client->create();
$this->Client->id = base64_decode(trim($this->request->query['client_id']));
$this->Client->set($this->request->data);
// validate
if($this->Client->validates()) {
// save the data after validation
if($this->Client->save($this->request->data)){
}
}
}
}
I think you are misunderstanding what Cake's isUnique rule checks for and as a result over complicating things. Cake defines isUnique as:-
The data for the field must be unique, it cannot be used by any other rows
When it checks if a value is unique it is smart enough to exclude existing data of the current row (which appears to be what you are attempting to do with your editunique rule).
So you just need your validation rules to look like:-
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('email'),
'message' => 'Kindly provide your email for verification.'
),
'maxLength' => array(
'rule' => array('maxLength', 255),
'message' => 'Email cannot be more than 255 characters.'
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'Provided Email already exists.'
)
)
);
This removes the editunique rule and drops the on condition of your unique rule.
As of cakephp 3.0 in the entities table it should look something like this
namespace App\Model\Table;
public function validationDefault($validator)
{
$validator
->email('email')
->add('email', 'email', [
'rule' => [$this, 'isUnique'],
'message' => __('Email already registered')
])
->requirePresence('email', 'create')
->notEmpty('email', 'Email is Required', function( $context ){
if(isset($context['data']['role_id']) && $context['data']['role_id'] != 4){
return true;
}
return false;
});
return $validator;
}
}
function isUnique($email){
$user = $this->find('all')
->where([
'Users.email' => $email,
])
->first();
if($user){
return false;
}
return true;
}
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 have built a simple CakePHP app with a users login system and have hooked up the Cake Form plugin by Milesj.me (not sure if that's causing the problems).
However my validation seems to have applied itself to the login form as well as the signup form. So when I try and login, I am getting errors like 'Username already in use'.
Any ideas what would cause this? Has something changed in CakePHP that adds the validation to authentication forms as well?
Also why am I having to hash the password in the model? I was under the impression that CakePHP hashed passwords automatically? And I've not needed to do it before. However If I don't do it, then it was saving the password in the DB as in and not hashed...
Here is my view, controller and model:
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('User.username',
array('tabindex'=>1, 'autofocus',
'label'=>array('class'=>'placeholder','text'=>'Username'))); ?>
<?php echo $this->Form->input('User.password',
array('tabindex'=>2, 'type'=>'password',
'label'=>array('class'=>'placeholder','text' =>'Password' ))); ?>
<div class="input button">
<button class="orangeButton" tabindex="3" type="submit"><span class="icon login">Log in</span></button>
</div>
<?php echo $this->Form->end(); ?>
controller:
public function login() {
if ($this->request->data) {
$this->User->set($this->request->data);
if ($this->User->validates() && $this->Auth->login()) {
if ($user = $this->Auth->user()) {
$this->User->Profile->login($user['id']);
$this->Session->delete('Forum');
$this->redirect($this->referer());
}
}
}
}
Note: the calls to Profile model for login just saves some data for when last logged in and other stuff and doesn't actually do anything regarding authentication!
and the model:
class User extends AppModel {
public $name = 'User';
public $hasOne = array(
'Profile' => array('className' => 'Forum.Profile')
);
public $hasMany = array(
'Access' => array('className' => 'Forum.Access'),
'Moderator' => array('className' => 'Forum.Moderator')
);
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A valid email address is required'
),
'email' => array(
'rule' => array('email'),
'message' => 'This is not a valid email address'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This email is already in use'
)
),
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This username is already in use'
),
'alphaNumeric' => array(
'rule' => array('alphaNumeric'),
'message' => 'Usernames must only contain letters and numbers'
),
'between' => array(
'rule' => array('between', 4, 20),
'message' => 'Usernames must be between 4 and 20 characters long'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
)
);
public function beforeSave()
{
if (isset($this->data[$this->alias]['password']))
{
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
Just add to your validation rules 'on' => 'create'
http://book.cakephp.org/2.0/en/models/data-validation.html#on
As per cakephp
In case of multiple rules per field by default if a particular rule
fails error message for that rule is returned and the following rules
for that field are not processed.
But if first rule doesn't fail, then it continue to evaluate second
rule.
You can remove particular validation by following way
$this->validator()->remove('username', 'unique');
I'm not sure why I keep receiving all the errors instead of just the invalid fields even when I fill out some of the required fields properly.
Submissions Controller:
public function submit() {
$this->set('title_for_layout', 'Submit - ');
if ($this->request->is('ajax')) {
if (!empty($this->request->data)) {
$this->Submission->set($this->request->data);
if ($this->Submission->invalidFields($this->request->data)) {
$formErrors = $this->Submission->validationErrors;
} else {
$formErrors = null;
}
} else {
$formErrors = null;
}
$this->set(compact('formErrors'));
}
/Submissions/json/submit.ctp:
<?php
$toReturn = array(
'formErrors' => $formErrors
);
echo json_encode($toReturn);
Submission model:
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 (e.g. IJL John F. Kennedy donated his presidential salary to charity)'
),
'maxLength' => array(
'rule' => array('maxLength', 300),
'message' => 'Your title needs to be shorter'
),
),
'description' => array(
'shortDescription' => array(
'rule' => array('shortDescription'),
'message' => 'Your description needs to be longer'
),
'longDescription' => array(
'rule' => array('longDescription'),
'message' => 'Your description needs to be shorter'
),
),
'source' => array(
'source' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Enter a valid source URL (e.g. http://en.wikipedia.org/wiki/Penguins)'
),
'website' => array(
'rule' => 'url',
'message' => 'Enter a valid source URL (e.g. http://en.wikipedia.org/wiki/Penguins)'
),
),
'category' => array(
'category' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Please choose a category'
)
)
);
Form values that are getting serialized and sent:
Errors I'm getting in a json response:
Pulling hair out over here :|
You seem to have got a little muddle up with validates() and invalidFields()
invalidFields() returns the invalid fields after a validates(), see: http://book.cakephp.org/2.0/en/models/data-validation/validating-data-from-the-controller.html
So your code should look something like this:
$this->Submission->set($this->request->data);
if (!$this->Submission->validates()) {
$formErrors = $this->Submission->invalidFields();
} else {
$formErrors = null;
}
First, set the data to the model:
$this->ModelName->set($this->request->data);
Then, to check if the data validates, use the validates method of the model, which will return true if it validates and false if it doesn’t:
if ($this->ModelName->validates()) {
// it validated logic
} else {
// didn't validate logic
$errors = $this->ModelName->validationErrors;
}
Validating Data from the Controller
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;
}