cakePHP - modified field not updating - cakephp

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.

Related

How to create a whitelist of updatable fields in a CakePHP's model?

I want to create a whitelist of fields that I want to be updatable in CakePHP. I know that I can pass a fieldList array in the call to Model::save(), but this isn't what I'm looking for. What I want is that every model "publish" a list of the valid fields, so if I call the Model::save() method without a fieldList and with data that mustn't be updatable (like the ownerId) this won't be updated.
What can I do to get this behavior? Maybe override the Model::save method in every Model to call at the "original" Model::save with the whitelist? I think this is a good idea, because I don't pollute all the controllers with lots of duplicated whitelists...
Thanks for your help!
Well, thanks you all for your answers, but I was wrong: I don't need to add this functionality.
The problem I was trying to solve was a security problem, I was trying to avoid form tampering (I've discovered the name just now), and as I am a novice CakePHP user, I didn't know that CakePHP already manages this problem.
The answer to my question is very easy: I must use CakePHP's Security Plugin and use the Form Tampering prevention (http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#form-tampering-prevention).
Can‘t say I’ve ever needed to do this in CakePHP, but CakePHP only saves the fields you pass it.
If you really need to create a white-list and you’re certain you only ever want these fields saving and never any others in your database (although I don’t understand why you have columns for them if you never touch them) then you could emulate this behavior in a model callback method:
<?php
class User extends AppModel {
public function beforeSave($options = array()) {
$whitelist = array('first_name', 'last_name', 'email');
foreach ($this->data[$this->alias] as $field => $value) {
if (!in_array($field, $whitelist)) {
unset($this->data[$this->alias][$field]);
}
}
return true;
}
}
This will just unset any data not in the $whitelist array, but if you really don’t want a column being updated then don’t pass a value for it.

CakePHP actsAs Translate and $Model::find()

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).

Cakephp Model save and update without modifying `created` and `modified` fields

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

CakePHP calling field() within the model

I am writing a function within the php of the model. Is there any issue with using
$this->field('something)
while inside the model? I can use other things like $this->saveField(...) fine but for some reason this is not returning me the value in the table correctly. Just wanted to make sure, thanks!
You have to specify the conditions first, like so:
$this->field('field_name', $conditions);
Or, I think you can also try setting the id first
$this->id = $id;
$this->field('field_name');

CakePHP newbie question: How to add non-database attribute to model?

I want to create a model that has SOME attributes that are not stored in a database. For example, I want to maintain an "age" field in the model, but I only store birthday information in the database (I can calculate "age" once the DOB info has been loaded). I tried adding a simple attribute to a model extension, but as far as I can tell, it's ignored by CakePHP. What's the proper way to go about accomplishing this?
I'm a CakePHP novice, so forgive me if I'm missing something obvious.
I'd do it using the afterFind callback method in the model.
It's been a while since I dealt with Cake, but why not just implement a getAge() method?
If you are using CakePHP 1.3, you could use the 'virtual field':
http://book.cakephp.org/view/1608/Virtual-fields
[Cookbook 3.7.10]
[edit] It also works in CakePHP 2.x:
http://book.cakephp.org/2.0/en/models/virtual-fields.html
The main advantage of this approach is that the virtual field is added in the model. That way, the MVC principles are respected.
example (MySQL):
var $virtualFields = array(
'age' => "YEAR(NOW()) - YourModelHere.dob"
);
There are two ways of doing it with cake.
Use virtual fields http://book.cakephp.org/2.0/en/models/virtual-fields.html
Use call back function afterFind() http://changelog.in/2012/07/alternative-to-cakephps-virtual-fields-which-is-far-less-restrictive/
Use the class Cache:
$parametrs = array('age'=>21, 'name'=>'aziz');
Cache::write('myOptions', $parameters);
Cache::read('myOptions');

Resources