How many times you found yourself in situation when you were trying to prevent certain fields from being edited by users? I’ve done it myself and I’ve seen others doing it. The most hardcore method is to unset() long list of fields in $this->data variable in controller’s action. It can get difficult to maintain if you change fields’ values from many places – if you forget to unset a sensitive field somewhere this could happen in serious security flaw in your application.
Imagine that your user model has such fields: id, username, password, group_id. Of course you let users change their username and password, but it’s critical to keep them away from group_id field that assigns them to certain user group to make sure they don’t make themselves Administrators!
My proposal is to control these unwanted changes from model level. Let’s call these read-only fields ‘locked fields’. We are going to define which field to lock in our model’s definition and the beforeSave() callback method will take care of the rest. Here is the AppModel:
class AppModel extends Model { var $lockedFields = array(); function beforeSave() { if (!empty($this->id)) { foreach ($this->lockedFields as $field) { unset($this->data[$this->name][$field]); } } return true; } function lockFields($fields = array()) { if (!is_array($fields)) { $fields = array($fields); } foreach ($fields as $field) { if (!in_array($field, $this->lockedFields)) { $this->lockedFields[] = $field; } } } function unlockFields($fields = array()) { if (!is_array($fields)) { $fields = array($fields); } foreach ($this->lockedFields as $k => $field) { if (in_array($field, $fields)) { unset($this->lockedFields[$k]); } } } }
As you see beforeSave() does it’s job only when a record is being edited ($this->id is set) as it should. UserModel looks like this:
// user.php class UserModel extends AppModel { var $lockedFields = array('group_id'); }
We defined group_id as locked field in model’s definition but thanks to lockFields() and unlockFields() methods defined in AppModel we are able to alter this dinamically. There may be occasion when administrator actually wants to move an user to another group. You can achieve it through a simple method in UserModel.
// user.php function moveToAdmins($userId) { $this->unlockFields('group_id'); $this->id = $userId; $this->saveField('group_id', 1); $this->lockFields('group_id'); }
Simple. You also can lock/unlock many fields at once passing them in array.
As always I hope this helps someone