Skip to main content

Multi Tenancy with Codeigniter

In this post I will show you how I converted my normal Codeigniter application into a multi-tenant system.(first step to a SaaS implementation)
Note - This implementation is a separate DB multi-tenancy implementation.

Lets say we have an up and running CI application name ci_app, with the directory structure like this
./application
./application/config
./application/...so many other important directories
./asset
./asset/js
./asset/images
./asset/css
./system
./index.php
which is accessed through browser like http://localhost/ci_app

So to implement the multi-tenant arch we are most concerned about the following files, and we will be editing them
  1. ./index.php
  2. ./application/config/config.php
  3. ./application/config/database.php
And also we need to create a few new ones
  1. Create a tenant folder in your www root directory, lets say tenant_1
  2. Cut the ./index.php from ci_app and paste it in tenant_1 directory 
  3. Create a blank file config.php in tenant_1 directory
  4. Create a uploads directory for file uploads
  5. Create some tenant specific asset directory lets say tenant_1_asset

tenant_1/config.php 

The config.php file in tenant_1 folder is the main config file containing vital config data like the system, app and asset path, database configurations, and any other config for that matter which are tenant dependent
Here is what we need to put in tenant_1/config.php
/* CORE APP config */
$_SSS['app']['base_dir'] = $_SERVER['DOCUMENT_ROOT'];

$_SSS['app']['tenant_folder'] = 'tenant_1'; //folder name
$_SSS['app']['app_folder'] = 'ci_app'; //folder name
$_SSS['app']['asset_folder'] = 'tenant_1_asset'; //folder name with a trailing slash only if there is an asset folder else leave blank

$_SSS['app']['system_path'] = $_SSS['app']['base_dir'].'/'.$_SSS['app']['app_folder'] .'/system/'; //will be sed by the CI index file
$_SSS['app']['application_path'] = $_SSS['app']['base_dir'].'/'.$_SSS['app']['app_folder'] .'/application/'; //will be used by the CI index file
$_SSS['app']['base_url'] = 'http://'.$_SERVER["SERVER_NAME"].'/'.$_SSS['app']['app_folder'];
$_SSS['app']['tenant_url'] = 'http://'.$_SERVER["SERVER_NAME"].'/'.$_SSS['app']['tenant_folder'];
///The above config variable are defined to create the basic paths and URLs required for the app to work, which are defined just below



/* Main Paths and urls */
if(! defined('APP_TENANTPATH')){
    define('APP_TENANTPATH', $_SSS['app']['base_dir'].'/'.$_SSS['app']['tenant_folder'].'/');//this will be used by ci_app config files to load this config file
    define('APP_TENANTURL', $_SSS['app']['tenant_url']);//this the path that will show on the browser and will be used for accessing the controllers and methods
    define('APP_ASSETURL', $_SSS['app']['base_url'].'/'.$_SSS['app']['asset_folder']);//this will be required to access tenant specific assets}

/* DB config */
$_SSS['app']['db_host'] = 'my_db_host';
$_SSS['app']['db_user'] = 'tenant_1_user';
$_SSS['app']['db_pass'] = 'tenant_1_pass';
$_SSS['app']['db_name'] = 'tenant_1_db';
$_SSS['app']['db_driver'] = 'mysql';


/* CI specific params*/
$_SSS['ci']['db_cache'] = FALSE;

//include any other config files here which have configurations in $_SSS variable
//like email.php etc

tenant_1/index.php

As we have now created the tenant specific configuration file we need a mechanism to load the config variables into our ci_app. For with we need to amend the index.php copied from the ci_app directory.
Here are the changes required in the index.php
<?php
require_once('./config.php');//first thing, include the tenant_1/config.php here


$system_path = $_SSS['app']['system_path']; //changed from $system_path = 'system';
$application_folder = $_SSS['app']['application_path']; //changed from $application_folder = 'application';
//the above 2 variables are coming from the included tenant_1/config.php file

So now instead of opening the app like this
http://localhost/ci_app we will access it like this http://localhost/tenant_1
And because we are calling the tenant_1 directory we require to have the Codeigniter framework to be available for this directory and this is exactly what we are achieving by doing the above mentioned changes in the index.php file copied from the ci_app directory

The browser will open the index.php which will
  • include the tenant config
  • load the variables required by CI
  • and finally load CI
But hold on we have still a little more to fix to get it running, as we need to pass on the configuration variables to the ci_app/application/config/config.php and ci_app/application/config/database.php files

ci_app/application/config/config.php

We need to include the tenant specific config file so that the ci_app config file can access the variable defined there, and this we achieve by changing the following
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if (defined('APP_TENANTPATH'))//lets include the tenant_1/config.php
    require(APP_TENANTPATH.'config.php');

And as we did in the index.php change the variable definition as per your business need

ci_app/application/config/database.php

As we did for the config file just above, we need to pass on the DB configuration variables to database.php as well. so here are the changes for it
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if (defined('APP_TENANTPATH'))//lets include the tenant_1/config.php
    require(APP_TENANTPATH.'config.php');


$db['default']['hostname'] = $_SSS['app']['db_host'];
$db['default']['username'] = $_SSS['app']['db_user'];
$db['default']['password'] = $_SSS['app']['db_pass'];
$db['default']['database'] = $_SSS['app']['db_name'];
$db['default']['dbdriver'] = $_SSS['app']['db_driver'];

//we can have more/different config items as required and set in the tenant config file 

This is one way of achieving the Multi-tenancy with CI.

Next thing to do would be to make the asset_url available to the application as the base_url() and the site_url wont be sufficient enough to give the path for tenant linked assets. This can be achieved by
creating a helper function which gets the APP_ASSETURL if it is set. and use this helper function to load the asset files.

Now you just have to replicate the tenant_* folders and change the configs etc to suit the requirements and access the multi-tenancy enabled app like http://localhost/tenant_*
Or create subdomain and link them to tenant_* folders to access something like this
http://tenant_*.myappworld.com

Thanks for reading,
Sandeep

Check my Analytics app here

Comments

Pankaj said…
Thank you very much... Very nice explanation and easy to understand. It will be great if we can develop a UI to generate folders, config file and index file. Really helpful. Need to implement same on Bonfire.
Sandeep Rajoria said…
Thanks for the support and the food for thought Pankaj.
I will definitely give it a try.
Visningsnavn said…
Followed your instructions on a blank CI3 site but all i get is Bad Request, Your browser sent a request that this server could not understand.
Unknown said…
Nice Article. best article read in the right time. thank you very much for the article. please keep this great work.
Unknown said…
How can you send me a sample code by picking it up?
Email: marcosantonioti10@gmail.com

Unknown said…
How can you send me a sample code by picking it up?
Email: marcosantonioti10@gmail.com
Kang Endang said…
can you send me the code?
Email: endangsantika@gmail.com
UltraCare PRO said…
Thank you very much... Very nice explanation and easy to understand.
get physiotherapy machine here
Faizan Umer said…
This comment has been removed by the author.
The Pope said…
Can sand deep https://www.blogger.com/profile/11156947581557340567 please contact me on mail2shola@gmail.com
Atip Nomsiri said…
I can't run my project.
Can you send me a sample code please?

dbixxy(at)gmail.com
Abdallah mohsen said…
Can you send me a sample code please?
abdullahmohsensayed@gmail.com
VBloggers said…
Nice info, I am very thankful to you that you have shared this special information with us. I got some different kind of knowledge from your web page, and it is really helpful for everyone. Thanks for share it. Read more info about codeigniter ajax login example
aaronnssd said…
I am very thankful to you for sharing this best knowledge with us. This information is helpful for everyone. So please always share this kind of knowledge with everyone. Thanks. Read more info about Upload Image Preview Tutorial in Laravel
Unknown said…
This comment has been removed by the author.
Unknown said…
can you send a sample code at gautaminit@gmail.com
Lascode said…
This was extremely helpful. The knowledge you have shared is invaluable.
Regarding the assets path, in my situation I found the symlink() function in PHP to be the solution for my use case.

Popular posts from this blog

Profiling and checking PHP error_reporting in a Codeigniter App, without editing the config!!

Hi all, You must have definitely used the Profiling in Codeigniter and error_reporting many a times in Development and Testing environment, but I am sure you must have missed it on a real Production environment. As there are scenarios, where you want to quickly debug the Production application and find out what PHP errors is the application throwing, check the page profile, that too without putting the time and effort in replicating the whole production environment on your local machine, or perhaps a testing server. This small piece of code(we could perhaps call it a hack), which I have used in almost all of my CI applications, will make your life very easy, without losing anything on the security of the system. Following points, essentially sum up what exactly it does - Check for the dev(or root or admin, whichever name you use for the su access), if it is logged in, as we don't want others to see all the Profile data and other errors. Check for a specific query str...

How I solved design problems by using various design patterns in my Laravel Project

Hey guys, Lately I have been working on a Virtual marketplace application using Laravel and PostgreSQL. So, when I was asked to build this huge application, the biggest challenge I faced was the design. Having a fair bit of prior experience in Laravel and upon following the current community trend, I decided to go with Laravel. And I hoped and expected that this, somewhat opinionated framework, would take care of my design to a large extent. When I actually started designing it, I realized that for a small/medium application Laravel already has things in place, you, as a developer just need to follow the guidelines set in place by the framework and use the features its providing out-of-box. However, for a larger application, with lot of interdependent modules and complex business flows, you need to make your own design decisions as well along with the existing features. This gave me an opportunity to take a look into the various existing design patterns to solve my design probl...