August 2007


First of all in app controller we need to gather all exists actions.

class AppController extends Controller {

   function __construct()
    {
        if (isset(
$this->othAuthOpen
)) {
            
$othOpen=array_map(‘strtolower’,$this->othAuthOpen
);
        } else {
            
$othOpen
=array();
        }
        
$protected = array( ‘object’low($this->name‘Controller’), ‘controller’‘appcontroller’
,
            
‘tostring’‘requestaction’‘log’‘cakeerror’‘constructclasses’‘redirect’‘set’‘setaction’
,
            
‘validate’‘validateerrors’‘render’‘referer’‘flash’‘flashout’‘generatefieldnames’
,
            
‘postconditions’‘cleanupfields’‘beforefilter’‘beforerender’‘afterfilter’‘disablecache’‘paginate’
);
        
$protected=am($protected,array_map(‘strtolower’,get_class_methods(‘AppController’
)));
        
$controllerName Inflector::camelize($this->name
);
        
$controllerPath Inflector::underscore($controllerName
);
        
$methList
=array();
        
$methods=get_class_methods($controllerName.‘Controller’
);
        if (
is_array($methods
)) {
            foreach(
$methods as $method
) {
                if (
$method{0} != ‘_’ && !in_array(low($method), $othOpen) && !in_array(low($method), am($protected, array(‘delete’
)))) {
                    
$methList[]=$method
;
                }
            }
        }
        
//debug($methList);
        
$this->othAuthRestrictions=$methList
;

         }
}

After it we just need to declare in controller list of opened methods:

       var $othAuthOpen = array(‘login’, ‘logout’, ‘forget’ , ‘register’, ‘confirm’, ‘noaccess’);
 

This behavior allow to limit selected and update rows of model with some scope.

Example of using if you want to scope articles with scope user_id then in beforeFiler of ArticlesController we place next code.
Value ‘User.id’ placed into session during login.


   $this->Article->scopeSetup(array('user_id'=>$this->Session->read('User.id')));
   $this->Article->scopeEnable();

After this all insert and read will scoped with user_id value. 
Now for example possible to call $this->paginate() without any additional conditions.

Also before create and delete record behavior check that record from required scope. 

<?php /* 
 * Scope behavior for cakePHP 
 * comments, bug reports are welcome skie AT mail DOT ru 
 * @author Yevgeny Tomenko aka SkieDr 
 * @version 1.0.0.1 
 
 configuration is
 
 1)  array (‘scope’ => array(‘parent_id’=>$parentIdValue, ‘secondParent’=> $secondParentValue), ‘enabled’ => true);
 
  2) array(‘parent_id’=>$parentIdValue, ‘secondParent’=> $secondParentValue);
  3) array (‘enabled’ => false);
  
 */ 

class ScopeBehavior extends ModelBehavior {     var $settings null

;
    
    function 
setup(&$model$config 
= array()) {
        
$this->scopeSetup(&$model$config
);
    }
    
    function 
scopeSetup(&$model$config 
= array()) {
        
$settings = array(‘enabled’=>true
);
        if (isset(
$config[‘scope’
])) {
            
$settings[‘scope’]=$config[‘scope’
];
        } else{
            
$settings[‘scope’]=$config
;
        }
        if (isset(
$config[‘enabled’
])) {
            
$settings[‘enabled’]=$config[‘enabled’
];
        }        
        
$this->settings[$model->name] = $settings
;
    }
    
    function 
scopeEnable (&$model
){
        
$this->settings[$model->name][‘enabled’] = true
;        
    }
    function scopeDisable (&$model){
        
$this->settings[$model->name][‘enabled’] = false
;        
    }
    
    
/**
     * Before find method. Called before all find
     *
     * Set scope filer settings
     *
     * @param AppModel $model
     * @return boolean True to continue, false to abort the save
     */ 
    
function beforeFind(&$model$cond
) {
        if (!
$this->settings[$model->name][‘enabled’]) return true
;
        if (!
is_array($cond[‘conditions’
])) {
            
$cond[‘conditions’]=array($cond[‘conditions’
]);
        }
        foreach (
$this->settings[$model->name][‘scope’] as $key => $value
) {
            
$cond[‘conditions’][$model->name.‘.’.$key]=$value
;
        }    
        return 
$cond
;
    }      function 
beforeSave(&$model

) {
        if (!
$this->settings[$model->name][‘enabled’]) return true
;
        if (empty(
$model->data[$model->name][$model->primaryKey])) { 
//add
            
foreach ($this->settings[$model->name][‘scope’] as $key => $value
) {
                
$model->data[$model->name][$key]=$value
;
            }
        } else { 
//update
            // now nothing to do
        
}
        return 
true
;
    } 
    function beforeDelete(&$model) {        
        if (!
$this->settings[$model->name][‘enabled’]) return true
;
        
        if (
$model->id!==false
) {
            
$curr=$model->read(null,$model->id
);
        }
        
        foreach (
$this->settings[$model->name][‘scope’] as $key => $value
) {
            if (
$curr[$model->name][$key]!=$value) return false
;
        }
        
        return 
true
;
    } 
    
}
?>

In last build I got inforation next warning

Dispatcher::start – Controller::$beforeFilter property usage is deprecated and will no longer be supported.  Use Controller::beforeFilter().

 So now in app_controller necessry to use beforeFilter function. I used beforeFilter property in app_controller before and now i got next trouble: some my controllers redeclare beforeFilter. So don’t forget to call parent class in controllers.

 function beforeFilter() {
     parent::beforeFilter();
 }

As you know cakephp 1.2 allow to call custom functions during validation.

var $validate = array( 	'alias' => array( 
		'rule' => array('unique', 'alias'), 
		'required' => true, 'allowEmpty' => false, 
		'message' => 'Such alias already exists' 	), 
);

This is sample when you need to have unique field in whole table.

 function unique($data, $name){
  $this->recursive = -1;
  $found = $this->find(array(“{$this->name}.$name” => $data));
  $same = isset($this->id) && $found[$this->name][$this->primaryKey] == $this->id;
  return !$found || $found && $same;
 }

This example from cakebaker blog.