I have two tables, profiles and regions.
in regions table I have list of all countries name. and in profiles table I have profile for my users and one of the field in that table in 'country'
Previously I was giving users an input box to enter country name but recently I noticed that few people are entering wrong information there. One guy entered 'jupiter' as his country name.
now what I wanna do is in my Profile model is something like this:
'country must exist' => array(
'rule' => 'countryValidation',
'message' => 'There is no such country'
)
public function countryValidation($check) {
$country = strtolower($this->data['Profile']['country']);
$countries = array();
$regions = $this-Region->find('all');
foreach($regions as $region){
array_push($countries, strtolower($region['Region']['country']));
}
return in_array($country,$countries);
}
and I am sure that the problem here is with
$regions = $this-Region->find('all');
What's the correct way to validate country?
BTW, I can't give a drop down list of country because it mess up with my layout/website design.
What you're looking to do is a bad idea, as you can't guarantee that end users will spell the names of the countries correctly etc.......
At any rate, create a custom function in your model
public function myFunction($countryName){
return in_array($countryName,$countriesList);
}
where $countriesList is an array of country names.
In the model validation you can do the following
public $validate = array(
'country' => array(
'rule' => 'myFunction',
'message' => 'Insert custom message here'
)
);
EDIT:
Then I would create a custom find function in the Region model, and call it from the current model to retrieve an array of results for validation. Something like:
$countriesList = $this->Region->customFunction();
or $countriesList = $this->Region->find('list);
Check this to use a model in another model
App::uses('Model', 'Region');
$Region = new Region();
$regions = $Region->find('all');
Related
Here is Yet another cakePHP question! I have table called blood_groups which has blood_group_id and group fields.. Then I have another table called donors, which has several fields such as name, surname etc. Another field included inside this table is the foreign key 'blood_group_id' which will need to map to the blood_group table on retrieval. in the donor registration view, i want to be able to retrieve the values from the blood_groups table, and display them using the formHelper (with their respective id's).
I have gone through CAKE doc, and I understand that I would need to create the association between my models, but I am struggling to figure this one out. Should I create $hasOne association inside the Donor Model (considering that the Donor table has the fk of the other table). And how would I go about retrieving the options of blood_groups from the blood_groups Model?
Should It work like this?(and are any other prerequisites involved?) :
In my DonorController -
$this->set('blood_groups', $this->Donor->Blood_Group->find('all'));
in Views/Donor/add.ctp
echo $this->Form->input('blood_group_id');
Accessing data through associations is fine. But for radios or checkboxes you want to do a find('list). Your model and variable name does not match the CakePHP convention, there should be no underscore.
Properly named this should be already enough to populate the input.
// controller
$this->set('bloodGroups', $this->Donor->BloodGroup->find('list'));
// view
echo $this->Form->input('blood_group_id');
If you don't follow the conventions for some reason:
echo $this->Form->input('blood_group_id', array(
'options' => $bloodGroups
));
See:
Linking Models Together
The Form Helper
Create one function in BloodGroup Model
function getDonors(){
$options = array(
// 'conditions' => array('Donor.blood_group_id'=>$id),
'joins' => array(
array(
'alias' => 'Donor',
'table' => 'donors',
'type' => 'LEFT',
'conditions' => array(
'Donor.blood_group_id = BloodGroup.blood_group_id',
),
)
),
'fields' => array('Donor.name','Donor.surname','Donor.blood_group_id',
'BloodGroup.blood_group_id')
);
$returnData = $this->find('all',$options);
return $returnData;
}
Now from controller call this function
App::import('model','BloodGroup');
$BloodGroup = new BloodGroup;
$donorList = $BloodGroup->getDonors();
$this->set('donorList',$donorList);
In view file you will get list of donors in $donorList.
how can I display values from an over a JOIN table associated table in cakePHP?
I tried the following:
echo $post['Post']['user_id'] // displays e.g. '4'
but then I only get the id not the defined $displayfield of the user model.
It works when I use the association in an input field like:
echo $this->Form->input('user_id', array( 'label' => 'User'); // displays 'Mr. Oizo'
The virtualField is defined in the user model as follows:
public $virtualFields = array(
'VirtualName' => 'CONCAT(User.Name, " ", User.Vorname)'
);
public $displayField = 'VirtualName';
Can anyone help me ?
Best regards
dan
If you have the relationships defined between models correctly, you would display the User virtual field like this in your view:
echo $post['User']['VirtualName']
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 am using CakePHP and i have something like:
PRODUCT -------> PRODUCT_CATEGORY <---------- CATEGORY
so one product can have 'n' categories and viceversa.
The problem is that i would like to validate the products so that the have at least one category. Since I am using the Form assistant and the validate functions of CakePHP y have arrived to this:
class Product extends AppModel {
var $name = 'Product';
var $validate = array(
'category_id' => array(
'rule' => array('multiple', array('min' => 1)),
'message' => 'You have to choose at least one category'
)
);
}
But it doesn't work, any ideas?
I think you should not validate against category_id, instead use Category (the name of your model).
If this still doesn't work, you should be ablo to find a solution in this question on SO: HABTM form validation in CakePHP
or have a look on this article:
http://nuts-and-bolts-of-cakephp.com/2008/10/16/how-to-validate-habtm-data/
have you tried the NOTEMPTY rule? im assuming the list of categories is in a checkbox format, rite.. by default the category_id if empty. the only logic i can think is, if nothing checked, then it throw the error message.
correct me if im wrong.. :)
Let's say I've got two tables: cities and countries. The City model belongsTo a Country. Now, when I display lists of cities, I want them to be ordered by country name and then city name.
I tried this in my City model class:
var $order = array('Country.name' => 'asc', 'City.name' => 'asc');
The sort order works correctly for my index page, but I get errors in several places where the model has been asked not to load associated models. Do I have to change the order parameters when I change which tables are loaded, or is there a smarter way to do this?
For now, I think I'll make the default order definition be City.name and then just change it to Country.name and City.name on the index page. That way it's safe by default, and I shouldn't get unexpected errors.
Update: As Rob suggested you can specify order parameters in the model associations. The query builder applies them like this:
If there is an order field on the main model, apply it first.
Walk through all the associated models in the order they appear. If the association includes an order parameter, add it to the end of the list.
To implement my example, I would do it like this:
class City extends AppModel {
var $belongsTo = array(
'Country' => array(
'order' => array('Country.name' => 'asc', 'City.name' => 'asc')));
}
One caution: if you turn off City.recursive, then the cities will be unsorted. However, I'm usually retrieving only one record when I've turned off recursion.
Have you tried defining the order directly in your association? I've never needed to do this, so I can't swear it will do what you're after, but:
class City extends AppModel {
$belongsTo = array(
'Country' => array(
'Order' => 'Country.name'
)
);
}
You could do something similar for your Country model, of course.
this is untested, but the idea is
in the country model you can do
hasMany = array('City');
function beforeFind()
{
foreach($this->hasMany as $model)
{
$this->order[] = $model['className'].'.'.'ASC';
}
}
not worth doing IMO. ur way is fine.