TL;DR - How do I massively assign private fields in Yii?
Any Yii experts on StackOverflow? The YiiFramework forums didn't really help me out.
I've got a private field hired in my CActiveRecord model that is dependent on another relation jobCount. Basically, if there is at least one valid job (stored in another table) associated with that member, they are consider hired.
Conventionally, I would set hired in the afterFind method, but that would mean loading the relation every time. For the sake of saving database queries, I would only like to load the relation if hired is needed. So I set hired to private, and can load the relation and set it once getHired() is called.
So far so good...
The problem arises once I incorporate the hired field in my CGridView. I'd like to be able to use the column filters, with a simple dropdown filtering on Yes or No. Upon filling out your filters, CGridView passes back a GET request, which you would set to a cleared model using massive assignment...
$model->attributes = $_GET['ModelName'];
Obviously I would like hired to get set as well, despite it being a private field. (I handle the searching for CGridView, don't worry about that.) I've made it a safe field in my model, but it doesn't get set.
setHired() function doesn't get called
setAttribute() function doesn't get called
setAttributes() function doesn't get called
What's the correct way to do this? Clearly, I could just add an extra line in my controller action...
if (isset($_GET['ModelName']['hired']))
$model->setHired($_GET['ModelName']['hired']);
...but I would really rather learn how to allow private fields to be massively assigned.
I realize that this is rather convoluted. If you see some way that I could streamline this hired bit, I'd appreciate that. Still, I would like to learn if there's a way to do this.
I suppose, you need just to add your attribute to the list of attributes.
public function attributeNames()
{
$names = parent::attributeNames();
$names[] = 'hired';
return $names;
}
Related
This is a long running question that gets me every time I am developing.
I suppose it is not specific to CodeIgniter, but as I am using it, I will consider it in my example.
Firstly, which is better:
function add_entry($data_array)
{
//code to add entry
}
function edit_entry($data_array)
{
//code to update entry
}
OR
function save_changes($what,$data_array)
{
//if what == update update
//otherwise insert
}
Both produce the same action, but does it really matter which one you use?
Getting onto more complicated things.
I have a page where I need to get ONE entry from the database.
I also have a page where I need to get all the entries from the same database ordered by a user specified column.
My resultant method is a function similar to
function($data_array,$order_by='',$limit='')
{
//get where $data_array
//if order_by!='' add order by
//if limit !='' add limit
}
As I develop my application and realise new places where I need 'similar' database functionality I am what feels like hacking previous methods so they work with all my case scenarios. The methods end up containing lots of conditional statements, and getting quite complex with in some cases 4 or 5 input parameters.
Have I missed the point? I don't want duplicate code, and when for the most part the functions are very similar I feel like this 'hacking' methodology works best.
Could someone advise?
Finally my admin functionality is part of the same application in an admin controller. I have an admin model which contains specific methods for admin db interaction. I however use some model functionality from 'user' models.
FOr example if on an admin page I need to get details of a db entry I may load the user model to access this function. There is nothing wrong/insecure about this..? right?
In addition to that within my admin model itself I need to get data about a user database entry so I call my user model directly from my admin model. This is strictly OK, but why? If i need data and there is already a method in my user model which gets it.. it seems a little pointless to rewrite the code in the admin model BUT each time that function is called does it load the whole user model again?
Thanks a lot all.
In order, add edit in the model vs save. Personally I have a save built in MY_Model that chooses whether it is a save or an edit depending on the existence of a primary key in the data being passed, so obviously I prefer that method it means a lot less duplication of code since I can use the save for any table without having functions in the model at all.
As to the second question I think it depends on situation. I also have a number of functions that have a ton of conditionals on them depending on where they're used and for what. In some cases I'm finding this makes the legibility of the code a little rough. If you're running them all through if statements it also could be impacting performance. DRY is a concept, not a rule and like other design concepts there are times when they just don't make sense, it's like database normalization, it's my personal opinion it's VERY easy to over normalize a database and destroy performance and usability.
Finally, using user functions in the admin code. I don't see an issue here at all, the reverse probably isn't true, but rewriting a function just because it's an "admin" function, when it's identical to a user function is utterly pointless. So you're correct there, it's a waste of time and space.
Let's say that I have a Post table and an Update table. What I would like to know is how could I modify the 'modified' time field in the Post table when an update is added to the update table. I have searched - but can't seem to figure it out.
I hope this makes sense...
Thanks!
Yes you can do so, for example you could do this in the model. Implement a afterSave() method in the Update table. There you can update the related Post.
http://book.cakephp.org/2.0/en/models/callback-methods.html
Other way around would be removing modified in the Post model and creating an afterFind() method in the model. In most instances you should not do this because of data load but might be useful when using lots of inserts and a very low amount of reads.
The most nice way is to put those methods into a Behaviour which creates a separation of concerns and also allows re-use:
http://book.cakephp.org/2.0/en/models/behaviors.html
Addition while thinking about it: You could of course update the Post.modified field but it would be more clear and better to create a separate field for it. So Post.latestcomment datetime field. Then also edits to the post will be registered as an update. Also it allows more flexible scheduling. Next to that you know whether there are any posts also.
Already used as many ORMs as you can imagine. At moment I'm in a love / hate crush with RedBean PHP. Here we go... after a few hours studying it I got a doubt about whats the better way to solve this very basic problem (best way means, in this case, the way that better fits to the RedBean's ease of use philosophy):
It's very common to limit access to some properties of our classes so we can prevent certain kinds of wrong data manipulation. This is usually accomplished with good use of getters and setters. But as far as already know about RedBean, there are no formal setters in the native classes, only some public properties that can be changed and persisted in the database.
What I would like to do is to protect some properties from being changed manually, so I can avoid other programmer to make any kind of weirdness like:
$beam->insertion_date = 'yesterday';
R::store($beam);
That field should never be changed after the row insertion, obviously, but we can't just trust no one will do that. Is there a way to achieve something like turning the insertion_date a protected property or making it inaccessible in some way?
I have a feeling that the best way to do that is using $beam->setMetadata() and declare that a given property shouldn't be changed, but I don't know how to achieve this in RedBean and still couldn't find enough information in the official manual. Any help is appreciated.
Thanks for reading.
The only bulletproof way to protect your db from unauthorized or unintended modifications is by using the privileges system of your database (GRANT and REVOKE) by using different user profiles with specific privileges down to the column level, etc.
Maybe not easy to implement, but by keeping privileges in (customized) code there will always be loopholes.
Another way is to set the data type of the column to a type that has the status specified.
For instance if you change varchar(255) to varchar(254) RedBeanPHP will spot the change and leave the column alone because it's now a specified column, for int you can use INT(10) for instance. Special types will never get converted: ENUM/DATE etc.
Found a solution!
On your model, store the state of a bean on a private property every time a bean is loaded from the database.
Before put things back in the database, test if the values that shouldn't have been changed were changed programatically.
In positive case, Throw an exception or add any other event to
warn the programmers that they are trying to change probably by
mistake a field that shouldn't be changed.
This is how the example model should look now:
class Model_book extends RedBean_SimpleModel{
protected $before = false;
public function open(){
if($this->bean->id){
$this->before = clone($this->bean);
}
}
public function update(){
if($this->bean->id){
if($this->before->insertion_date != $this->bean->insertion_date){
throw new exception ("The insertion_date can't be changed.");
}
}
}
}
So now the following code would result in exception:
$beam->insertion_date = 'yesterday';
R::store($beam);
Note: the use of RedBean's meta information API would be a better
solution, but it wouldn't make sense to add (only) this validation
criteria without add a complete validation layer on top of RedBean. On
the other hand, add a custom full validation would become useless in
case a built-in validation get added to the ORM in the near future (I
bet it will happen), so I'll leave it as it is for now.
Hope that helps.
I just started using CodeIgniter, and have really been happy with the results.
The one thing I've noticed is that I seem to be selecting different parts of a row with different parts of a model.
An example would be where on a page I need to get the current user's username, then farther down I need their email address. These are separate functions in the model (and therefore, separate queries). It annoys me knowing I could merge them into one query (saving overhead), but if I did that then I would loose the modularization the MVC model gives me (on plenty of other pages I just need the username or email, not both). Any suggestions on how to get past this?
Yes, but I have a specific function returning part of a row, and another function, returning another section of a row. I need this because most pages only need one section of the row, but some pages need both, this causes my code to run two queries. This is just one example. There are many. I was curious if there is a design pattern for this.
Youre essentially talking about lazy loading. So in your model you use something to track the state of the persisted properties, i.e. whether they have been loaded, modified, or if the model is completely new and not yet in the db. Then your getters for each property first check state, and if something is not yet loaded and the model isnt "new" then they query for that property and set it on the model before returning the value.
So your magic get might look like (pseudo code - youll have to translate to CI):
public function __get($property){
if($this->isPersisted($property) && !$property->isLoaded() && !$this->isNew()){
$this->$property = $this->db
->select($this->getColumn($property))
->from($this->tableName)
->where('id = ?', $this->id)
->fetchColumn(0);
}
return $this->property;
}
I want to save all but some excluded fields. I know that I can do it this way
$this->Blah->save($this->data,false,$fieldList)
Where $fieldList contains all the data fields of the table but these I don't want to get saved. I have some tables that have maaany data fields, and I don't want to write the whole list from scratch in every single controller action (yes, the fields that should not be saved differ from action to action). Additionally, it looky messy and confusing. Is cakePHP providing something ready-to-use for this case? If not, I guess, I'd have to implement it by myself by adding a $fieldList property to every controller and doing something like this (ugly-hacked-together-solution):
$tmp = $fieldList;
unset(array_search('fieldtoexclude', $tmp));
$this->Blah->save($this->data,false,$tmp);
Best Regards
function blacklist($blackList = array()) {
return array_diff(array_keys($this->schema()), $blackList);
}
shoud work
Take a look at:
http://www.dereuromark.de/2010/09/21/saving-model-data-and-security/
for details
If the field list changes from action to action, then you're looking for an automagic function that can read your mind. Cake doesn't provide that!
Somewhere you have to say which fields are to be excluded and doing so longhand in a clear way will make your code much more maintainable.
If it is only one controller, define the list as a class variable, or alternatively subclass the save action on the model.