I have the following database model.
Model1 hasMany Users
Model2 hasMany Users
Model3 hasMany Users
And one user can only belong to one Model. So the table user fields are the following:
id username password model1_id model2_id model3_id
I need to check in CakePHP User model that the three foreign keys cannot be NULL at the same time.
Which is the best way to solve that? Because I can't create any custom validation rules, validation rules was thought for validating one field. So How can I validate multiple fields and print the pertinent validation error.
you should add this in your Model.
public $validate = array(
'fieldName1' => array(
'rule' => 'ruleName',
'required' => true,
'allowEmpty' => false,
'message' => 'Your Error Message'
)
);
Cakephp Validation links
Use Custom validations
public $validate = array(
'table1_id' => array(
'rule' => array('custom_validation'),
'required' => true,
'allowEmpty' => false,
'message' => 'Your Error Message'
),
'table2_id' => array(
'rule' => array('custom_validation'),
'required' => true,
'allowEmpty' => false,
'message' => 'Your Error Message'
),
'table2_id' => array(
'rule' => array('custom_validation'),
'required' => true,
'allowEmpty' => false,
'message' => 'Your Error Message'
)
);
function custom_validation(){
if( empty($this->data['table1_id']) && empty($this->data['table2_id']) && empty($this->data['table3_id']) ) {
return false;
}
return 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 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'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.
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
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;
}