Can any body tell how to save and update database table using Model save function without modifying the created and modified fields in cakephp 2.*
If you don’t want the modified field to be updated when saving some data add
'modified' => false
to your $data array
Ref: Saving Data
Hope it helps
Example:
$this->request->data['YourModel']['modified'] = false;
The documentation states that setting 'modified' to false will stop the modified date from being saved. However, I have found that this causes the Save call to fail without any indication of what is wrong. Using unset on the modified value seems to work, however.
unset( $this->request->data['YourModel']['modified'] );
Documentation: http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-save-array-data-null-boolean-validate-true-array-fieldlist-array
Related
I am using the Security component in my AppController. I need to build a check form input that will allow custom formatting of each item in the list. To accomplish this, I am processing each item in the $options set using a foreach and creating the new element like so:
foreach ($fileTypes as $fileType_key => $fileType_value) {
echo $this->Form->input(
'FilesIncluded.' . $fileType_key,
array(
'type' => 'checkbox',
'value' => $fileType_key,
'label' => false,
'div' => false,
'before' => '<span class="checkbox clearfix"><span class="check">',
'after' => '</span><label for="add-product-check-sub-cat">' . $fileType_value . '</label></span>',
'hiddenField' => false,
)
);
}
There is a few things to note:
I am setting each check box to data[FilesIncluded][{UUID}] (where UUID actually represents the UUID pf the FilesIncluded) instead of data[FilesIncluded][]
FilesIncluded is not part of the form model, so it will appear in $this->request->data as $this->request->data['FilesIncluded'] instead of $this->request->data['Model']['column']
What I am trying to figure out is why this throws an auth security risk? When I change the field name from 'FilesIncluded.' . $fileType_key to something with a counter in it like 'FilesIncluded.' . $count . '.id', it seems to work without throwing any security auth errors. Any ideas how to make this work the way I am expecting it to?
UPDATE:
The other issue is being able to maintain a fixed set of FileTypes. For example, I want to be able to control the HABTM records that can be selected from the checkbox. For example, I will display this list:
http://cl.ly/image/0b1Q3C0d0w1Y
And only when the user selects the records will they be stored as hasMany. Then when it comes time to edit, I want to not only be able to show the same set of records, but then have them associated to the records the user saved.
(Probable) Cause
You're probably getting the Security error, because you're disabling the hiddenField for the checkboxes. The Security components checks if a Submitted Form is valid (i.e. not tampered with) by calculating a checksum, based on the names of the Form fields, when creating the form and comparing this with the data received when submitting the form.
By suppressing the hiddenField, the checkbox will not be present in the posted data if it is not checked (Non-checked checkboxes are never sent in HTML forms). As explained above, CakePHP calculates a checksum based on the fields/inputs it expects and the actual fields (data) it receives. By disabling the hiddenField, the checksum calculated from the posted data will depend on wether a checkbox was checked or not, which will invalidate the posted data
Workarounds
There may be some Workarounds;
Do not suppress the 'hiddenField' This will make sure that the checkboxes will always be present in the posted data. If a checkbox is not checked, the value of the checkbox will be 0 (zero). If the checkbox is checked, its posted value will be the specified value of the checkbox (or 1 if no value is specified)
Exclude your custom inputs from the checksum. You can exclude fields from the checksum via $this->Form->unlockField('fieldname');. CakePHP will ignore those inputs when calculating the Security checksum.
Documentation: FormHelper::unlockField()
Notes
Although these Workarounds may help, I'd suggest to not re-invent the wheel. By changing the names of your inputs, you're no longer following the CakePHP conventions. Sticking to the conventions often saves you a lot of time.
For example, saving related data can be performed with a single Model::saveAssociated() call. If your Model-relations are properly set, for example:
Document->hasMany->UserFiles, then CakePHP will insert/update both the Document and UserFiles data automatically.
Read the documentation here Saving Related Model Data (hasOne, hasMany, belongsTo)
I have a hasAndBelongsToMany association between Node and NodeTag.
My save works great when at least one is selected, but - if there is a previous association, and they try to save with none selected, my habtm table doesn't get updated, since it's not getting anything passed for NodeTag.
(I'm not sure if this is because I'm using javascript and custom buttons, or if the default CakePHP checkboxes do the same thing).
In my Controller, before the save, I added this:
if(!isset($this->request->data['NodeTag'])) {
$this->request->data['NodeTag'][0] = array();
}
This makes it so if I wasn't sending any NodeTag data, I now pass an empty array, and it updates the habtm table so that this Node no longer has any rows for NodeTags.
Note: Notice the array structure: ['NodeTag'][0] = array();
I was actually using your solution until it dawned on me that it is possible to achieve this with the Formhelper. Setting the hiddenField option to true ('hiddenField' => true), will force the submission of an empty array. Doing that saves you the trouble of checking once more in the controller.
I do not know how you could achieve it with javascript, but the default checkboxes have the functionality.
I have attached the Translate behavior to one of my models and I have some shortcomings regarding this:
1) If I don't save data in all fields passed as params when attaching the behavior to the model, $Model::find() method doesn't get the inserted rows.
public $actsAs = array(
'Translate' => array(
'title' => 'title_Translation',
'description' => 'description_Translation',
'description_long' => 'description_long_Translation'
)
);
Ex: if i pass to $Model::save() method only a value for 'title', the data is saved, even in the i18n table, but the $Model::find() doesn't get anything. I must pass data for all the fields.
Can I force it to retrieve those records ?
2) How can I get all the records in the admin side of the application (regardless of the language in which a record is saved) in order to list them so the user can alter it (edit data, save data in multiple languages)? Right now, I can only get the records that correspond to the current language (read from Configure or set explicitly)..
Thank you!
I kind of solved it, I copied the TranslateBehavior to app/Model/Behavior (just to avoid problems on future upgrades and keep the original one just in case) then I changed the _addJoin(...) method of the behavior, just changed the join type from INNER to LEFT on line 255 (I use cake 2.2.3).
Now if a record exist it is always retrieved, even if translated fields are missing.
Don't see any drawbacks besides the need to check if the translation field is empty.
OK, I might be a bit late, but anyway...
1) Cake uses an INNER JOIN when fetching a row and it's associated translations, so basically there's no easy way around this. You have to make sure you save every translatable field, every time - even if you just save it as blank. The only alternative would be to go hacking round the core to make it use a left join rather than an inner join - but don't do that.
2) The cookbook explains how to fetch all records here: http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#retrieve-all-translation-records-for-a-field
Now, probably most of the time you want to get just one translation, so you don't want to modify the definition of your $actsAs['Translate'] array in your model. So what I did, was set up a method in AppModel.php which modifies the $actsAs['Translate'] array on the fly:
/*
* See http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#using-the-bindtranslation-method
* This is for making it so we fetch all translations, as opposed to just that of the current locale.
* Used for eg. editing (multiple translations) via the admin interface.
*/
public function bindAllTranslations(){
$translatableFields = $this->actsAs['Translate'];
$keyValueFields = array();
foreach($translatableFields as $field){
$keyValueFields[$field] = $field.'Translation';
}
$this->bindTranslation($keyValueFields,false); // false means it will be changed for all future DB transactions in this page request - and won't be reset after the next transaction.
}
So, if it's an admin method (or any other situation you want all translations) you call that code before doing a find:
$this->MyModel->bindAllTranslations();
$this->MyModel->find('all');
Hope that helps!
Not exactly sure if it will help in your case, but you can also use
array to set locale before you call find()
$this->YourModel->locale = array("ENG", "GER", "JAP");
This way you will always get all records even if they don't have all possible translations.
Thanks a lot eleonzx, I'm having this problem since a decade, and with your simple answer I can now move forward ! So thanks again.
And maybe this code can help a lot of people :
in my AppController beforeFilter method I call _setLanguage
private function _setLanguage() {
if($this->Session->read('Config.language')){
$locale = $this->Session->read('Config.language');
$this->{$this->modelClass}->setLocale($locale);
}else{
$this->{$this->modelClass}->Behaviors->disable('Translate');
}
}
With the else condition I disable the Translate Behavior on the fly to get the original contents if there is no locale set in the session (I use basic links to switch between languages).
I am having trouble updating the aros table in my CakePHP app. Here is the code I've got:
$aro = new Aro();
$targetAro = $aro->find('first', array('conditions' => array('foreign_key' => intval($user['User']['id']))));
$newParent = $aro->find('first', array('conditions' => array('alias' => $userUpdate->level)));
$targetAro['Aro']['parent_id'] = $newParent['Aro']['id'];
$aro->updateAll($targetAro);
I've put in debug log statements to make sure that I'm getting the correct results from the find calls. But when I check the database after the updateAll call, the data is unchanged. Any ideas what might be going on?
I also tried calling $aro->setParent but I couldn't get that to work either.
I found the problem by tracing through the code that does a similar thing in the cake console. Instead of using $aro->updateAll($targetAro) I should be using $aro->saveAll($targetAro).
updateAll() is used to save changes to a number of entities. The first argument should be an array of values. saveAll() is used when you just want to save the changes to a single entity, as is the case in the code above.
Althought it seems like I should, I couldn't find error output anywhere in the logs or the script output because of the incorrect arguments I was passing to updateAll().
Any ideas on how to debug a modified field that is not being updated? The created field works fine.
I'm not sure what code you would need to look at so let me know!
Thanks
Like Anh Pham said, the modified and created field need to be DATETIME and they also need to default to NULL, as explained in the CakePHP book about the subject.
Most likely you are overwriting the field, unset the field from the object before saving it like this:
unset($user['User']['modified']);
Now you can save it:
$this->User->save($user);
You should try checking if you didn't use
$this->ModelName->read(NULL, $id);
before saving your data, if you think you didn't - double check it
That field should be named 'modified' with 'datetime' type. Cake should update the field automatically for you. If you want to check, just query it, or look into the database.
I got the same problem today and found out the modified field is only updated if any changes have been made, otherwise the field will not be touched.
The problem might also arise if you use manual update queries. The field is updated only when you use Cake's own functions.
In AppModel.php, just write the following code.
public function beforeSave($options = array()) {
parent::beforeSave();
if (isset($this->data[$this->alias]['modified'])) {
unset($this->data[$this->alias]['modified']);
}
if (isset($this->data[$this->alias]['created'])) {
unset($this->data[$this->alias]['created']);
}
}
This will work for all models. But we can do that for individual MODELS too.
Since we have used parent::beforeSave(); as our first line therefore this can be overwritten in each individual MODEL.
Thanks to inheritance.