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');
Related
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.
I continue building my CakePHP application. Now I've created all the database schema in MySQL, run some "cake bake all" and I have lots of models, views and controllers, and I'm gonna personalize them.
In many of my models I have this fields:
company_id
created_by
modified_by
As you can understand, the first field is the id of the owner of the "row", created_by is the id of who created the row and modified_by the latest person who updated it.
I know I can create a beforeSave filter in the model and update all the data (I suppose that I can know if I'm creating or updating a row, isn't it?), but just now I have 15 different models and I hope the app will grow more, so it's a lot of repetitive code to write. And, it breaks the DRY principle.
And my question is: Is there any "elegant" way to solve this problem? Maybe creating a class extending AppModel with a beforeSave filter for updating the fields, and that all my models inherit from the new Model class instead of AppModel?
Thanks for your help!
Make it a behaviour and load it for models that need that functionality.
See http://book.cakephp.org/2.0/en/models/behaviors.html#creating-behaviors
I think the most appropriate way is to create Behaviors.
You can set up the beforeSave callback in the behavior like what you have in your model.
Here is the link to the documentation http://book.cakephp.org/2.0/en/models/behaviors.html#behavior-callbacks
You can also check as example dereuromark's WhoDidItBehavior.
I'm having trouble wording my problem, so it's been tough to search for an answer. Hopefully you'll know how to help.
I am creating a CakePHP 2.1 Plugin that will interact with a series of its own Models:
- Friend
- Group
- User
Friend and Group are models that are created specifically for the Plugin, and they function within the plugin normally. However, the User model is really just an alias for some other table in the parent app.
So, if "My Awesome Application" decides to use "My Awesome Plugin", it will have to have its own "users" table (though it may called something else). Let's say "My Awesome Application" has a Model called MyUser. "My Awesome Plugin" wants to dynamically tell its internal User model to $useTable = "my_users".
My question is, how do I pass that data to the Plugin? How do I configure "My Awesome Plugin" to understand that User should $useTable "my_users";
As I understand you would like a Model in a PlugIn to use a table that would typically belong to a Model in your Application - by the conventions. Have you tried statically setting:
public $useTable = "my_users";
in the plugin? All plugins usually get initialized when Cake starts up, so all configurations should be loaded then. Why do you need this - it does really restrict you a lot? Will the table being used by the Plugin model change runtime?
The Model class also has some goodies you may find useful:
$this->User->table;
holds the table name for the model - the table that is currently being used that is**.
Also you can set the source table for the Model (inside a Controller) with:
$this->User->setSource('table_name);
** I am not sure if this applies when you use Model::setSource(). It would be interesting to check out what $this->User->table; holds after a Model::setSource() call.
I've figured out a way to accomplish this, but it might not work in all scenarios for all people.
I created a Component in my Plugin, and then I call the Component in my Controller. I pass the name of the users Model through the Component. This way, I can get information about the users Model, and I can set it as the useTable to my Plugin for use in the Plugin.
Of course, this method restricts me to using the Component to utilize the Plugin, but that's probably for the best.
Here's an example of how I did it:
// in the AppController
public $components = array(
'MyPlugin.MyPluginComponent' => array('userModel'=>'UserModelName')
);
// in the Plugin's Component
class MyPluginComponent extends Component {
function initialize($controller) {
//get the base user model
$this->UserModel = ClassRegistry::init($this->settings['userModel']);
//set the useTable for our plugin's user model
$this->PluginUser = ClassRegistry::init('MyPlugin.PluginUser');
//set the useTable value for this model
$this->PleaseUser->setSource($this->UserModel->useTable);
}
That seems to work for me. Hope this helps someone else.
I have two models (Posts and Categories), those two model have i18n properties. I need to do something like this:
$posts->find('all')
and return the Post with Category both translated.
How can I achieve this on cakephp 1.3?
How are you storing the properties? That would help answer.
If you do not have a lot, you could create a translator component that stores the key value pairs and then pass your data to a function in the component after you have retrieved your data.
$posts = $this->Post->find('all');
$posts = $this->Translator->translatePost($post, 'language');
Unless you are storing the translations within the Model data, then this is probably a better solution so that you don't break the MVC paradigm.
There are tons of ways to do it, but you need to be more specific with your question in order to hone in on the issue.
How can I filter Results by County,District,City with dropdown select.
Is there a way to do it in Cake way, because currently I'm doing it classic way, by passing Id to url, like:
/sort/county_id/1/district_id/5
But this is very bad hack, because I have to explode $this->here and not good thing. I don't know if there is uri class like in codeigniter, so I can play with $this->uri->segment().
In fact my problem is that I need to see if isset then pass it.
Have you considered named parameters?
Add/remove Cake named URL parameter for a link