Tuesday, April 10, 2012

CakePHP Multi-Tenant Single Database en 3 Pasos

Inspirados en One core, one app, multiple domains un sencillo pero bien pensado artículo de CakePHP Bakery que ilustra como es posible tener una sola aplicación o core de CakePHP y usar varios dominios y bases de datos completamente separadas (Multi-Tenant, multiple database), hicimos una adaptación para permitir, basados en el mismo principio de detección de dominios, tener una sola base de datos e inyectar automáticamente el tenant id correspondiente. Esto también permite administrar fácilmente nuevos dominios usando la base de datos.

1. Detectar el Tenant o Cliente por el dominio que accede: bootstrap.php

$serverName='localhost';
// Thanks to eimermusic
if ( isset($_SERVER['SERVER_NAME']) ) { // web
 $serverName = $_SERVER['SERVER_NAME'];

} elseif ( count($_SERVER['argv']) ) { // cli
 $serverName = $_SERVER['argv'][count($_SERVER['argv'])-1];

}
$_SERVER['SERVER_NAME'] = $serverName;
Configure::write('Config.serverName', $serverName);

2. Hacer Match de a que TenantId pertence el dominio que esta accediendo: AppController.php

 public function beforeFilter(){

  $tenantModel = ClassRegistry::init('Tenant');
  $amountTenants = $tenantModel->find('count');
  if($amountTenants == 0 ){
   //This is for load the default configuration in database the first time the app loads
   $tenantModel->saveDefaultTenants();
  }
  //Custom find to get the tenant by ServerName
  $currentTenant = $tenantModel->getTenantByServerName(Configure::read('Config.serverName'));
  $tenantId = isset($currentTenant['Tenant']['id'])?$currentTenant['Tenant']['id']:0;
  if(!empty($currentTenant)){
   Configure::write('Config.tenantId',$tenantId);
  }

 }

3. Insertar el tentantId en las búsquedas: AppModel.php


public function beforeFind($queryData){
  if($this->name!='Tenant'){
   $tenantId = Configure::read('Config.tenantId');
   if(isset($tenantId) && $tenantId!=''){
    $queryData['conditions'][$this->name.'.tenant_id'] = $tenantId;
   }
  }
 }

 public function beforeSave($queryData){
  $tenantId = Configure::read('Config.tenantId');
  if(isset($tenantId) && $tenantId!=''){
   $this->data[$this->name]['tenant_id'] = $tenantId;
  }
  return true;
 }

No comments:

Post a Comment