i'm currently having some issues by using the cakePHP data validation.
I've tried to use the rule with the argument 'on' => 'created' but it didn't seems to work.
My objective is to check during the creation of the user, to check if the mail is not already existing in my database.
But when it's a edit, i don't want to check this, i mean, if the user don't change the mail, obviously this mail is already existing in the database, but it's just the same then before...
So i don't want to check if the mail, have not been changed.
'mail' => array(
'valid_mail' => array(
'rule' => array('email', true),
'message' => 'Veuillez insérer un email valide.'
),
'isUnique_mail' => array(
'rule' => 'isUnique',
'on' => 'create',
'message' => 'Ce mail est déjà utilisé.'
)
)
Hope you guys understand my problem.
Part of your problem might be that you're not specifying the ID of the record that needs to be saved using $this->Model->id = $id so when you call the save (or saveAll) function, it's trying to insert a record.
The isUnique validation function actually will filter out the ID of the current record when updating if it's been set using $this->Model->id. You can see this at the bottom of the function (line 2446).
Personally, I'd leave the isUnique validation on all the time to prevent users from changing their emails to one that already exists once they've registered.
Related
There is a table location containing city and name columns.
Query is:
$count=$this->Location->find("all",array("conditions" => array("Location.city" => '$city',"Location.area" => '$location')));
If condition becomes true, it has to show an error message.
In Model
role=>"unique"
option is there, but it is for only one column.
But in above query its depends on two columns. How to write validation for this?
Have you tried to read the documentation?
You can validate that a set of fields are unique by providing multiple fields and set $or to false:
public $validate = array(
'email' => array(
'rule' => array('isUnique', array('email', 'username'), false),
'message' => 'This username & email combination has already been used.'
)
);
Make sure to include the original field in the list of fields when making a unique rule across multiple fields.
If a listed field isn’t included in the model data, then it’s treated as a null value. You may consider marking the listed fields as required.
You can simple define a validation for the city field in the City model.
public $validate = array(
/* Other fields */
...................
'city' => array(
'rule' => array('isUnique', array('city', 'area'), false),
'message' => 'City and area combination already exists.'
)
);
I want to validate with dmy format. Here is what I wrote in my model:
'birthdate' => array(
'date' => array(
'rule' => array('date','dmy'),
'message' => 'Solo data valida',
'allowEmpty' => true,
)
)
However, when I submit the form with the date, I get an error.
One thing you should verify is the value of "birthday" DIRECTLY PRIOR to validation. Try debugging $this->request->data in your controller before calling $this->Model->save, or $this->Model->validate. It's quite possible that you are transforming that date value before you pass it to validation, and thus it could be failing.
For instance, I typically use a beforeSave, and afterFind callback to transform dates going into and coming from MySQL, or you may have something in your appController or beforeFilter callback that's inadvertently transforming the data.
I'm hoping you cakephp experts can answer this re Cake 2.1 and data validation in the model.
Cake gives you an "on" key for use in the validate array. I understand what the docs say about this but my question is, what's the point of these two items.
Let's say I have a validation rule for when a record is created. The validation passes and the record is created.
Then the user goes and edits that record and changes it to something that no longer passes that particular validation. But since I've got my validation set to run on create only, the validation passes and the record is updated with invalid data. It seems to me like this would apply to any on create / on update rules. If a user wanted to bypass validation, just create a valid record, then go and edit it so it's now invalid.
Can someone perhaps help me understand when it might make sense to use on update and on create?
This is most useful in conduction with the required rule. You should set certain fields that are minimally required to be required 'on' => 'create'. This makes the rule fail if those fields don't exist in the data set and the record cannot be created, but allows you to update existing records without having to pass that field each and every time.
For example:
'email' => array(
'required' => array(
'on' => 'create',
'rule' => 'notEmpty',
'message' => 'Enter your email address',
'required' => true,
'last' => true
),
'notempty' => array(
'rule' => 'notEmpty',
'message' => 'Enter your email address',
'allowEmpty' => false,
'last' => true
),
'email' => array(
'rule' => 'email',
'message' => 'Not a valid email address',
'last' => true
)
)
I found it really useful in cooperation with profile pictures. You need one to be uploaded on create (if it is needed), but on update i can remain empty => no update to picture will be done.
'on' => null will do the job
EDIT :
Ok, let's say you have a Profile model.
If you have a Date of Birth field, you would probably use a rule that would be enforced for both addition and edition.
You might also add a delete_picture checkbox in your form that a user should select in order to delete their profile picture. When adding, you have no profile picture so this field is only relevant at edition time and you would then use on => update.
Let's say you also have a group field that is set when you create the object but that is meant to never be changed. You would then use on => create.
I have models Person and Phone/Email with HABTM relationship. After some pain I found out, that my life is easier, when I break HABTM into: Person hasMany PeoplePhone, Phone hasMany PeoplePhone, PeoplePhone belongsTo (Person,Phone). Well, I don't need any help with this :-) now, my problem is different:
Before I can pair Person with his Phone or Email, I need to save this Phone/Email and then get its ID.
Now I would like to save only unique Phones and unique Emails, so I have created this method in app_model.php:
function saveUnique($data = null, $unique_fieldname = null)
{
if (! $data) { return false; }
if (! $unique_fieldname) { return false; }
$id = $this->field('id', array($unique_fieldname => $data[$this->name][$unique_fieldname]));
if ($id)
{
$this->read(null, $id);
}
else
{
$this->create();
$this->set($data);
if (! $this->validates()) { return false; }
if (! $this->save()) { return false; }
}
return true;
}
It seems to work, but I am all new to CakePHP. How would CakePHP guru solve this function/method?
Thank you very much for your time.
-Petr
If I were you, I would stick with default Cake functionality rather than what you are doing. All of this functionality is built into Cake, so why reinvent the wheel?
First, HABTM relationships already work as you have broken them out. You can access the join models by adding with in your model associations. This should give you access to the intersection table data.
$hasAndBelongsToMany = array(
'Phone' => array(
'className' => 'Phone',
'joinTable' => 'persons_phones',
'foreignKey' => 'person_id',
'associationForeignKey' => 'phone_id',
'unique' => false,
'with' => 'PersonsPhones'
)
);
As far as your unique phone and email requirements go, why not use Cake's built in validation to do the checking for you? That way, you just call the save function and then Cake will do all the checking for you.
For instance, if a person only has one email address, then do this in the person model. This will validate that the input is an email address and that it is a unique email address in the database. You can do this validation with any field and build custom validation rules with ease.
public $validate = array(
'email' => array(
'email' => array(
'rule' => 'email',
'message' => 'You must enter an email address.'
),
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'An account with this email address already exists.'
)
)
);
I take it that there is a reason for why you would use HABTM for an email address. Normally, people would not share email addresses, so hasMany would probably be a better relationship. I can see it working for phone as people do share phone numbers frequently. I say this so that you are sure that HABTM is the relationship you really want to use.
I've only been using CakePHP for a couple months but I would implement it a little different.
I'd add a UNIQUE index on person_id & email_id in my PeopleEmail table (do the same thing in PeoplePhone). This way we're not accidentally saving duplicate data, our physical schematic prevents duplicate records before they're even stored.
Now we just have to keep an eye out for the SQL error being thrown. I haven't dug too deep into this aspect of Cake yet but I do know the base AppModel class has an onError() method that may provide useful in this regard.
I have a Projects table and a Users table which are linked by a HABTM relation. In the "add" new Project page I have a multiple checkbox section to select Users for the new project. I want to have at least one User for the Project. What's the best way to approach this in CakePHP ?
Try this:
// app/models/project.php
/**
* An additional validation check to ensure at least one User is
* selected. Spoofs Cake into thinking that there are validation
* errors on the Project model by invalidating a non-existent field
* on the Project model, then also invalidates the habtm field as
* well, so when the form is re-displayed, the error is displayed
* on the User field.
**/
function beforeValidate() {
if (!isset($this->data['User']['User'])
|| empty($this->data['User']['User'])) {
$this->invalidate('non_existent_field'); // fake validation error on Project
$this->User->invalidate('User', 'Please select at least one user');
}
return true;
}
I stumbled on the same issue, but now - 3 years later - with CakePHP 2.3.
To be clear; Group has and belongs to User. I've had a form like this:
// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('User');
With the validation rule like in user448164's answer:
// Model/Group.php
public $validate = array(
'User' => array(
'rule' => array('multiple', array('min' => 1)),
'message' => 'Please select one or more users'
)
);
That didn't work, after Googling for it, I found this question which couldn't still be the best solution. Then I tried several things, and discovered this to work just fine:
// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('Group.User');
Way too easy solution, but had to dig into it to find out it works this way.
Hopefully it helps somebody one day.
Update for CakePHP 2.4.x (possibly 2.3.x as well)
When I wrote this answer, I was using CakePHP 2.3.x. Back then it worked perfectly for both validating and saving the data. Now when applying the same code on a new project, using CakePHP 2.4.x, it didn't work anymore.
I created a test case, using the following code:
$data = array(
'User' => array(
'Client' => array(8)
),
);
$this->User->create();
$this->User->saveAll($data);
My first thought was: Saving all means saving all "root" models, what actually makes sense to me. To save deeper than just the "root" ones, you'll have to add the deep option. So I ended up with the following code:
$data = array(
'User' => array(
'Client' => array(8)
),
);
$this->User->create();
$this->User->saveAll($data, array('deep' => true));
Works like a charm! Happy coding. :)
Update (2014/03/06)
Struggling with the same problem again, in this case with hasMany instead of habtm. Seems like it behaves the same way. But I found myself looking for this answer again, and got confused.
I'd like to make clear that it's key to use Group.User instead of User in your input. Else it won't use the User model validation.
I've just been looking at his problem myself on a project and came across a slightly more elegant solution, as long as you're only dealing with a habtm relationship and you need to ensure that at least one checkbox is selected.
so for example you're editing a Project and you want it to be associated with at least one user
Add this to beforeValidate()
// check habtm model and add to data
foreach($this->hasAndBelongsToMany as $k=>$v) {
if(isset($this->data[$k][$k]))
{
$this->data[$this->alias][$k] = $this->data[$k][$k];
}
}
In the validation rules add the following:
'User' => array(
'rule' => array('multiple', array('min' => 1)),
'message' => 'Please select one or more users'
)
teknoid's blog has a pretty in depth solution to your issue here. The most Cakey way of doing this would be to add custom validation to your model, as you mention in your comment above. Check out http://teknoid.wordpress.com/2008/10/16/how-to-validate-habtm-data/
From the article, where Tag HABTM Post (:: Project HABTM Users):
First, we validate the Tag model, by
using the data from the form to ensure
that at least one Tag was selected. If
so, we save the Post and the relevant
Tags.
2016 update for CakePhp 2.7
Full answer here : HABTM form validation with CakePHP 2.x
TL;DR;
AppModel.php
public function beforeValidate($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
if(isset($this->data[$model][$model]))
$this->data[$this->name][$model] = $this->data[$model][$model];
}
return true;
}
public function afterValidate($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
unset($this->data[$this->name][$model]);
if(isset($this->validationErrors[$model]))
$this->$model->validationErrors[$model] = $this->validationErrors[$model];
}
return true;
}
In the main model of your HABTM :
public $validate = array(
'Tag' => array(
'rule' => array('multiple', array('min' => 1)),
'required' => true,
'message' => 'Please select at least one Tag for this Post.'
)
);
If you are using CakePHP 2.3.x, you may need to add this code to your model in addition to the code that GuidoH provided, otherwise your HABTM model data may not save:
public function beforeSave($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
if(isset($this->data[$this->name][$model])){
$this->data[$model][$model] = $this->data[$this->name][$model];
unset($this->data[$this->name][$model]);
}
}
return true;
}
As per my comment on Guido's answer above, I use Guido's answer exactly, however I modify the data with the beforeSave callback before it saves to the database.
I have this issue on Cake 2.4.5+
public function beforeSave($options = array()) {
$temp = $this->data['Group']['User'];
unset($this->data['Group']['User']);
$this->data['User']['User'] = $temp;
return true;
}