CakePHP association one Model to exclusively one out of three others? - database

i got a problem with designing my Database and the CakePHP code around it.
I have 4 Models,
ServerName
VirtualMachine
ServerHousing
ManagedServer
In ServerName, i want to save all ServerNames which then can be used in either one of the three other models. How can i achieve that i am only able to link one ServerName to either one of the other models?
Thank you guys in advance.
EDIT:
I now did it a little bit different. First of all it need to be done in the Model itself.
I used the validate option in cakePHP's models.
The code is like this:
public $validate = array(
'server_name_id' => array(
'rule' => 'serverNameTaken',
'message' => 'This Servername has already been taken.'
)
);
public function serverNameTaken()
{
$this->ManagedServer = ClassRegistry::init("ManagedServer");
// Assuming the server_name_id was passed from the form...
$server_name_id = $this->data['VirtualMachine']['server_name_id'];
// Check if this servername_id is already saved in virtual_machines
if ($this->ManagedServer->find('count', array(
'conditions' => array(
'ManagedServer.server_name_id' => $server_name_id
)
)) > 0
) {
// Found the server_name in the VirtualMachine model!
return false; // Prohibit saving the data
}
if ($this->find('count', array(
'conditions' => array(
'VirtualMachine.server_name_id' => $server_name_id
)
)) > 0
) {
// Found the server_name in the VirtualMachine model!
return false; // Prohibit saving the data
}
// Do this for the other models too. If a return false is not hit by now,
// everything should be fine and you can...
return true;
}
Same code I used in the other models, only the code had to be altered.
Thanks again for you answer!!!

You can't do this on the Model layer. Models are either associated or they are not. What you are probably looking for is some logic in your Controller that checks if a row for ServerName X has already been saved in any of the other models, prior to saving it from another model.
The beforeSave function is useful for this. For example, in your Controller, put this:
public $uses = array('VirtualMachine', 'ServerHousing', 'ManagedServer');
public function beforeSave() {
// Assuming the servername_id was passed from the form...
$servername_id = $this->request->data['servername_id'];
// Check if this servername_id is already saved in virtual_machines
if($this->VirtualMachine->find('count', array(
'conditions' => array(
'VirtualMachine.servername_id' => $servername_id
)
)) > 0) {
// Found the servername in the VirtualMachine model!
return false; // Prohibit saving the data
}
// Do this for the other models too. If a return false is not hit by now,
// everything should be fine and you can...
return true;
}
Hope this will get you going in the right direction.

Related

how to give CAKEPHP validation?

If condition is true it should show an error message "already exits" or else a message "successful" should be displayed.
Is it possible to add a validation like this to the model part:
$name = $_POST["name"];
$validation_sql = "SELECT COUNT(*) > 0 FROM college WHERE status='2' AND name='$name'";
You can use hasAny() as the solution:
$conditions = array(
'status'=>'2',
'name'=>$name
);
if ($this->XXXXXXX->hasAny($conditions)){
//do something
}
hasAny will return true if found else false.
NOTE: hasAny is not available in version 3.x
You can add server validation in model like:
public $validate = array(
'name' => array(
'rule' => array('isUnique', array('name'), false),
'message' => 'This name has already been used.'
)
);
It is not recommended to use $_POST in CakePHP at all, rather use the Request Object in the controller to access the data given by a POST request:
$this->request->data['College']['name'];
This information can then be passed to the model where it is validated.
If the post request has been created by the CakePHP form helper you don't need to access it - you can directly pass the data to the save method of the model instance (see CakePHP Handbook - Saving your data).
if ($this->College->save($this->request->data)) {
// handle the success (Normally success flash)
}
debug($this->College->validationErrors); //Normally error flash - if FormHelper is used the error messages are automatically shown beside the input elements
The validations can be added with the Bake Console or manually by adding validation rules to the College Model code:
public $validate = array(
'name' => array(
'rule' => 'isUnique',
'message' => 'This username has already been taken.'
)
);

Inserting new row instead of Updating

I am trying to update a profile in the database without passing the profile id as a paramater and instead of updating I am adding new row. I tried to use the getLastInsertId() but it did not work.
public function editProfile(){
if (isset($this->data)){
$Client = $this->Client->find('first', array(
'fields' => array('email','username','first_name','surname','country','phone_prefix','phone'),
'conditions' => array(
'Client.email' => $this->request->query['email'],
'Client.client_type' => $this->request->query['client_type']
),
)
);
if($this->request->is('get')) {
if($data = $this->Client->save($this->request->query,array('first_name','surname','country','phone_prefix','phone')))
{
$this->Client->id = $this->Client->getLastInsertId();
It looks like you are trying to update a record in your Client model based on the email and client type because (I assume) you don't know the id.
Try changing your code to get the id based on the information you have-
public function editProfile(){
if($this->request->is('get')) {
if (isset($this->data)){
$conditions = array('conditions' => array(
'email' => $this->request->query['email'],
'client_type' => $this->request->query['client_type']
));
$this->Client->id = $this->Client->field('id', $conditions);
if($this->Client->save($this->request->query,array('id', 'first_name','surname','country','phone_prefix','phone')))
{
$this->setFlash('success');
}else{
$this->setFlash('fail');
}
}
}
Looking at the code it seems you have two possible keys:
The client email
The last saved id
First, using the last saved id seems very fragile - how can you be sure this will always be the record you want to update? Best to avoid this approach.
However, if you have the user's email you can use the following method:
$client = $this->Client->findByEmail($this->request->data['Client']['email']);
$id = $client['Client']['id'];
You now have the client record id available to use in your save. Something like:
$data = array();
$data['Client']['id'] = $id;
$data['Client']['fieldFromForm'] = $this->data['Client']['fieldFromForm'];
$this->Client->save($data);
...
Without the id, cake will assume that you are adding a new Client. To update via save you'll need to tell it which Client.id to update in some way. Is there some reason that you can't provide the Client.id? The usual approach is to have Client.id present in a hidden input on your edit view which you can then pass to the edit action...

CakePHP one form for multiple models does not validate properly

So I have two models, that are Company and User. Here are the relations:
Company:
public $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'company_id',
)
User:
public $belongsTo = array(
'Company' => array(
'className' => 'Company',
'foreignKey' => 'company_id',
)
And there is a form were both instances are being created. The problem is that only the Company fields are being validated and User is not. Even though the validation rules for just creating or editting a User work perfect. Here is the form in question:
echo $this->Form->create('Company', array('action'=>'register'));?>
echo $this->Form->input('Company.name', array('label'=>__('Company name*')));
echo $this->Form->input('Company.address', array('label'=>__('Address*')));
echo $this->Form->input('Company.city', array('label'=>__('City*')));
echo $this->Form->input('User.0.name', array('label'=>__('Name*')));
echo $this->Form->input('User.0.email', array('label'=>__('Email*')));
echo $this->Form->input('User.0.email_confirmation', array('label'=>__('Email confirmation')));
echo $this->Form->end(array('label'=>'Save', 'onclick'=>'disableSubmit(this)'));
I am fully aware of the validate only option, but I wonder what is the proper way to make validation work.
Any help or guidance is much appreciated.
As requested, I provide the Model logic as well:
public function registerCompany($data){
unset($this->User->validate['company_id']);
if($this->saveAll($data, array('validate'=>'only'))){
if($this->saveAssociated($data)){
return true;
}
}else{
return false;
}
}
Here are some of the validation rules of User model, there are two different validation types for REST service and for web client, so this is for the web client only. NOTE: it works all good on single user create/edit:
$this->validate = array(
'name'=>array(
'Insert user name'=>array(
'rule'=>'notEmpty',
'message'=>'Insert user name',
'last'=>false
)
)
Use this to validate:
Suppose you are saving the data from "Company Controller".
if( $this->Company->saveAll(array('validate'=>only)){
//your save logic here
} else {
//here form has some validation errors.
debug($this->Company->validationErrors);
}
Try this any reply....
Same you can do with "User"
if( $this->User->saveAll(array('validate'=>only)){
//your save logic here
} else {
//here form has some validation errors.
debug($this->User->validationErrors);
}
And
In form use this for user data:
echo $this->Form->input('User.name', array('label'=>__('Name*')));
saveAll is a wrapper of saveMany and saveAssociated. So you don't have to call both methods.
Then, according to the Cake's doc, to unset a validation you should use the syntax : $this->Model->AssociatedModel->validate...
Should look like this :
public function registerCompany($data){
unset($this->Company->User->validate['company_id']);
if($this->Company->saveAssociated($this->request->data)){
return true;
}else{
return false;
}
}

Cakephp SaveAssociated and Save - Using same Model Validation code

Question: How can I use the same code in the model validation (in particular for child models) for both SaveAssociated and Save function calls in CakePHP,... given that SaveAssociated implementations expect the form data array to contain a numeric index [0] for data fields belonging to a child model?
Scenario:
Assuming I have a parent model with a hasMany relationship to several child models.
Typically if you use SaveAssociated to save data to all models at once, you would need to specify an index number (typically 0) on the view form input. Example:
echo $this->Form->input('MerchantControl.0.startdate', array('type' => 'text', 'class' => 'datepicker_start'));
As a result, any custom child model validation code will need to be written with [0] as well. See function urlParamNotUsedByOtherMerchants in the code sample below.
public $validate = array(
'urlparam' => array(
'In Use by other Merchants' => array(
'rule' => 'urlParamNotUsedByOtherMerchants',
'message' => 'URLPARAM belongs to another Merchant'
)
)
);
public function urlParamNotUsedByOtherMerchants($data) {
$searchfilter = array(
//Because of SaveAssociated, need to refer to index [0]
'MerchantControl.id !=' => $this->data['MerchantControl'][0]['merchant_id'],
'MerchantControl.urlparam ' => $data,
);
$merchantcontrol = $this->find('all', array('conditions' => $searchfilter));
if (sizeof($merchantcontrol) > 0) {
return false;
} else {
return true;
}
}
The problem is there are many other instances where I will also be using a "Save" and not a "SaveAssociated" in maintainence views where i directly update or create the child model only. In this case, this model validation code is going to fail with an error saying index "[0]" not defined or something similar.
How can I use the same code in the model validation (in particular for child models) for both SaveAssociated and Save function calls in CakePHP?
If I understand you correctly you want to check whether the urlparam is already used by another merchant or in other words whether it is unique.
Why don't you use the built-in validation rule isUnique?
Example:
public $validate = array(
'urlparam' => array(
'In Use by other Merchants' => array(
'rule' => 'isUnique',
'message' => 'URLPARAM belongs to another Merchant'
)
)
);

CakePHP - access to associated model in beforeSave

In my app Quotes belongTo a product, which in turn belongs to a material. As I can't get the product model afterFind array to include the material when it is accessed from the Quote model I have associated the Quote directly with a material.
The problem I'm having now is that the material_id for the quote needs to be automatically saved based on the product which is selected for the quote
i.e. pulling the value of Product.material_id from the selected product and saving it to the Quote.material_id field automatically before the Quote has been saved to the database.
I'm quite new to cakePHP. Does anyone know how this can be done?
EDIT:
Here is an example to help explain. In my Quote model i can have:
public function beforeSave($options) {
$this->data['Quote']['material_id'] = 4;
return true;
}
but i need to do something more like this which doesn't work:
public function beforeSave($options) {
$this->data['Quote']['material_id'] = $this->Product['material_id'];
return true;
}
I'm shocked this hasn't been properly answered yet....
Oldskool's response is semi-correct, but not entirely right. The use of "$this->Quote" is incorrect, as the beforeSave function itself resides in the Quote class. I'll explain using an example.
-> We have a model Subscription which belongsTo a SubscriptionsPlan
-> Model SubscriptionsPlan hasMany Suscriptions
To access the SubscriptionsPlan data in a beforeSave function in the Subscription model, you would do the following:
public function beforeSave($options = array()){
$options = array(
'conditions' => array(
'SubscriptionsPlan.subscriptions_plan_id' => $this->data[$this->alias]['subscriptions_plan_id']
)
);
$plan = $this->SubscriptionsPlan->find('first', $options);
//REST OF BEFORE SAVE CODE GOES HERE
return true;
}
It should probably work by using a find instead.
public function beforeSave($options) {
// Assuming your Product model is associated with your Quote model
$product = $this->Quote->Product->find('first', array(
'conditions' => array(
'Product.material_id' => $this->data['Quote']['material_id']
)
));
$this->data['Quote']['material_id'] = $product['material_id'];
return true;
}

Resources