Basically I got inspired by this post, an approach to Mutli-Tenancy using defaultScope() of CActiveRecord. Thus I used a Tenant-ID-Field on all my tables, which should be divided by tenant. My User-Class fetches this Tenant-ID on login and publishes it via a getter. I will not post this code. For an exampe code how to implement your user class please refer to the original post.
However I used a slightly different approach on my model-classes. Instead of inheriting from a base class I created a behavior.
As I cannot modify the defaultScope() method using a behavior I needed to rely on Events. Thus as a base class for my behavior I use the CActiveRecordBehavior class. Inherriting from this I can react on the events BeforeFind and BeforeSave. Here is my code:
class MultitenantBehavior extends CActiveRecordBehavior {
public function beforeFind($event) {
//restrict queries to the actual tenant by manipulating the model's DbCriteria
$c=$this->owner->getDbCriteria();
$condition=$c->condition;
if(strlen($condition)>0) {
$condition='('.$condition.') AND '
}
$condition.='tenantId = '.Yii::app()->user->tenantId;
$c->condition=$condition;
}
public function beforeSafe($event) {
//tie this model to the actual tenant by setting the tenantid attribute
$this->owner->tenantId=Yii::app()->user->tenantId;
}
}
In my code you see to filter all results by find methods I overwrite the beforeFind method. In this I modify the condition property of the DbCriteria instance of the owner of this behavior. This is assumed to be an instance of ActiveRecord. This is technically the same as setting the defaultScope().
To make sure all inserts, updates and deletes set the right Tenant-Id I overwrite the beforeSafe method. In this I set the owners attribute tenant_id. This is the same approach as in the original post.
Now if one of model-classes should be filtered by Tenant I just add the following lines of code to it:
public function behaviors(){
return array('Multitenant'=>'path.to.MultitenantBehavior');
}
The behaviors function is an empty function of CActiveRecord. It can be used in model class to return an array of behaviors, which are by default attached to this model. This ensures that my TenantBehavior is automatically attached on the initialization of an instance of my models.

Help














