Quantcast
Channel: Live News for Yii Framework
Viewing all articles
Browse latest Browse all 3375

[Wiki] YII SaaS Multi Tenant application with single database

$
0
0

SaaS application structure in YII using only one database

Lots of people are asking how to solve it with YII,We think its difficult with YII. But its easy to solve . There is no database triggers needed . we can simply sove it by extending a class(say "RActiveRecord") from CActiveRecord .Then extend all our model classes from that class.

create RActiveRecord.php

create a file under protected/components named RActiveRecord.php .

Extend it from CActiveRecord .

WE need override the methods beforeSave() , defaultScope() or beforeFind() and beforeDelete() .

Add a field in all tables named "tenant"

Add a field in all tables named "tenant" . This is the unique identifier .

There is no need to add "tenant" in any model class.

The RActiveRecord.php

Please paste this code to RActiveRecord.php file

<?php
 
//::Rajith:: SaaS
class RActiveRecord extends CActiveRecord
{
 
    //saving model->tenant to all tables automatic ::Rajith::
    public function beforeSave()
    {
        $tenant = $this->getTenant();
        $this->tenant = $tenant;
        return parent::beforeSave();
    }
 
    //Find only tenant match by default ::Rajith::
    //use  defaultScope() or  beforeFind()
    //comment defaultScope(), if you using beforeFind()
    public function defaultScope()
    {
        $tenant = $this->getTenant();
        return array(
        'condition'=> "tenant=:tenant",
        'params' => array(":tenant"=>$tenant));
    }
 
 
    //Find only tenant match by default ::Rajith::
    //uncomment if you using beforeFind()
    /*public function beforeFind()
    {
        $tenant = $this->getTenant();
 
        $criteria = new CDbCriteria;
        $criteria->condition = "tenant=:tenant";
        $criteria->params = array(":tenant"=>$tenant);
 
        $this->dbCriteria->mergeWith($criteria);
        parent::beforeFind();
    }*/
 
 
 
    //before deletion check for the ownership ::Rajith::
    //not working for deleteAllByAttributes
    public function beforeDelete()
    {
 
                $tenant = $this->getTenant();
                if ($this->tenant == $tenant)
                {
                        return true;
 
                }
                else
                {
 
                        return false; // prevent actual DELETE query from being run
 
                }
    }
 
 
    //to get the unique UNIQUE identifier
    public function getTenant()
    {
    //this is the unique identifier . Use your own ideas to get a unique identifier(tenent)
 
    return 'identifier-id-name';
    }
 
}

In my application i used the host name to find the tenant

Example for getTenant()

//to get the unique UNIQUE identifier
    public function getTenant()
    {
 
                $domain = $_SERVER['HTTP_HOST'];
                $connect = mysql_connect("localhost","root","password") or die("not connecting");
                mysql_select_db("databasename") or die("no db");
                $query = mysql_query("SELECT * FROM users WHERE customdomain='$domain'");
                $numrows = mysql_num_rows($query);
                if($numrows)
                {
 
                    $results = mysql_fetch_assoc($query);
                    return $results['username'];
                }
                else
                {
                    $subdomain = implode(array_slice(array_reverse(explode('.', $_SERVER['SERVER_NAME'])),2));
                    $query = mysql_query("SELECT * FROM users WHERE username='$subdomain' AND whitelabel=1");
                    $numrows = mysql_num_rows($query);
                    if($numrows)
                    {
                    return $subdomain;
                    }
                    else
                    { return 'parent';}
                }
 
    }

WE can simply use the subdomain name or domain name as the tenant.

or

use setstate at the time of login in Useridentity

Yii::app()->user->setState('tenant', "something-unique(domain-name or sudomain)");

and use that in getTenant()

//to get the unique UNIQUE identifier
    public function getTenant()
    {
 
     return Yii::app()->user->tenant;
 
    }

Final step

change in the model class

class Model-name extends RActiveRecord
{
 
...........
 
...........

Thats it!!

Please note that the commented parts in the beforeDelete() .

if you want to use the deleteAllByAttributes() and other deletion methods except delete(), then change the CActiveRecord class . because in the CActiveRecord , the beforeDelete() method only invoked for the delete() method .

Or

Use the $this->getTenant() in the conditon, check whether 'tenant' and '$this->getTenant()' matching

i dont think this is the best way to achieve SaaS in YII. Appreciate suggestions and more ideas .

Thank You - Rajith


Viewing all articles
Browse latest Browse all 3375

Trending Articles