22

Build a PHP SaaS app from scratch

This is the first of 3 posts which will talk about launching your own SaaS product (or converting your existing single tenant app to a SaaS app). We will cover various aspects from developing the app to building a SaaS site (to enable your clients to signup for it)

Typical SaaS Application

We will be looking at…

1. Creating your app

The app is the product that you are going to sell. If you already have this app ready, you will only have to modify it so that it is compatible for multiple clients. The app is the front-end for your client’s end-users.

2. Creating your SaaS site

The site is the front-end for your clients. They will be using your site to signup for the app.

3. Hosting et al.

We will talk about how to host your app + site, how to ensure scaling, setting up DNS & SSL, database versioning etc.


Part 1: Create your app

Before you begin with selling your product, you need to have a product. Let us first try and understand the SaaS architecture. Every app consists of two parts: code & data. Now, every client has end-users; the data is shared between these end-users and (mostly) not with other clients’ end-users. So every set of end-users (of each client) should access a different set of data. In most cases, the code used by all the clients can be the same.

Now there are various ways in which a SaaS app can be designed:


Option 1 – Single database, single code-base

Use a single database and store all client data in a single database. You will have to add a field like “clientid” in all your tables and modify all your SQL queries to select the correct “clientid”. If you already have a single tenant app, then heavy modification is required in the code.

Advantages: Easy to manage database & updates; no duplication.
Disadvantages: If you miss out a “clientid” in the SQL query, it may result in issues with another client.

Option 2 – Multiple database, single code-base

Create a separate database for every client. No modification is required to your existing single-tenant codebase. You will only have to modify the configuration file to select a database depending on the client.

Advantages: Very little codebase modification, easy to ensure client data does not get mixed.
Disadvantages: Multiple databases to manage so in the event of DB schema updates, you will have to update each and every database.

Option 3 – Multiple database, multiple code-base

Create a separate database & duplicate the complete code for every client. No modification required at all.

Advantages: No codebase modification, easy to ensure client data does not get mixed, easy to perform client specific modifications.
Disadvantages: Multiple databases/code to manage so in the event of DB schema/code updates, you will have to manually update for each client.


There are a number of articles discussing which approach is the best but option 2 helps us develop a scalable SaaS solution in the quickest possible way. So we will stick to option 2 for this tutorial.

If you already have an app then you will only have to modify the configuration file as below. If you do not, then make sure your configuration file looks something like below and the rest of the app can be however you like it.

Let us begin with the PHP part. You should be ideally using a framework like Symfony or Zend to create your app. To simplify, I am only going to be talking about config.php:

This is how a simple standard config.php file looks: (usually DB connection is not part of this, but for a clearer explanation, I have added it)

<?php

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME','root');
define('DB_PASSWORD','password');
define('DB_NAME','dummyapp');

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) {
	echo "Oops! Something went horribly wrong.";
	exit();
}
mysql_selectdb(DB_NAME,$dbh);

In the above code, when the user visits a PHP page, it calls config.php, which stores the database configuration & connects the database. Then the remaining PHP files, calling this configuration file, can connect to the DB.

The problem in the above code is that it is built for a single client (with a single set of end-users). We need to modify this code so that for each client, a different database is used. So depending on how the app is called (i.e. depending on the URL), we will connect to a different database. If you look at SaaS apps online, you will find that they assign a unique sub-domain to every client. This technique will come in handy.

So if your site is mydummyapp.com, then when a client registers, he will be assigned a URL like anantgarg.mydummyapp.com, yourname.mydummyapp.com etc. Now, all end-users of the client will connect to the same sub-domain.

To make our app SaaS friendly we need to check the sub-domain and then accordingly connect to a database. We will perform the following checks:

1. Let the end-user visit the app using *.mydummyapp.com (DNS configuration in next post)
2. Check a central table if the sub-domain is registered; if yes, to whom? Then fetch the DB details
3. Connect the end-user to the client’s DB

Here is how a modified config.php will look:

<?php

$data = explode('.',$_SERVER['SERVER_NAME']); // Get the sub-domain here

// Add some sanitization for $data

if (!empty($data[0])) {
    $subdomain = $data[0]; // The * of *.mydummyapp.com will be now stored in $subdomain
}

// Contact a central database (which stores information about all clients

define('SITE_DB_SERVER','localhost');
define('SITE_DB_PORT','3306');
define('SITE_DB_USERNAME','root');
define('SITE_DB_PASSWORD','password');
define('SITE_DB_NAME','dummysite');

$sitedbh = mysql_connect(SITE_DB_SERVER.':'.SITE_DB_PORT,SITE_DB_USERNAME,SITE_DB_PASSWORD);

// Get client's DB details (make sure $subdomain is already sanitized)

$sitemysql = mysql_query('select id,dbusername,dbpassword from clients where subdomain = '.$subdomain.');
$appdata = mysql_fetch_row($sitemysql);

// You can add caching code here to skip the above DB call

if (empty($appdata['id'])) {
    // If the client does not exist i.e. the end-user types the wrong sub-domain

    echo "Oops! Sorry, we are unable to find you! Please email us at [email protected]";
    exit();
}

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME',$appdata['dbusername']);
define('DB_PASSWORD',$appdata['dbpassword']);
define('DB_NAME','dummyapp_'.$appdata['id']);

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) { exit(); }

mysql_selectdb(DB_NAME,$dbh);

// Success! Now we can continue to use the app as-is!

If you are modifying your existing app, then you only need to modify your configuration file and make it similar to the above. The rest of the app should not need any modification (as long as you are not saving any files). If you have files, then you will have to modify the file upload code and add a folder e.g. /uploads/$subdomain/$filename.

If you are testing on localhost, you can simply add something like the following to your hosts file:

127.0.0.1      test.mydummyapp.com
127.0.0.1      test2.mydummyapp.com

Part 2: Create your SaaS site

I assume you can create a simple SaaS site with user registration. On your registration page, you will need to have something like:

Username: _ _ _ _ _ _ _ _
Password: _ _ _ _ _ _ _ _
Subdomain: _ _ _ _ _ _ _ _ (.mydummyapp.com): 

You will need to create a common DB (e.g. dummysite) with a table called “clients”. In your real site, you will also need other tables like “subscriptions” which will hold payment information.

create table clients (
  id int default 0 auto_increment,
  username varchar(50),
  password varchar(255),
  subdomain varchar(50),
  dbusername varchar(50),
  dbpassword varchar(50)
)

Now for every client who signs up on your site, you will have to add an entry into the above table. You will also have to create random dbusername & dbpassword. And finally, you need to create a new DB for this client.

The PHP code for that will be:

// Connect to database instance (which stores all app DBs)
$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_MASTERUSERNAME,DB_MASTERPASSWORD);

// Create DB ($id will have value of client's inserted ID)
mysql_query("CREATE DATABASE mydummyapp_".$id."",$dbh) or die(mysql_error());

// Create user and grant all privileges for the above DB
mysql_query("GRANT ALL ON mydummyapp_".$id.".* to  ".$dbusername." identified by '".$dbpassword."'",$dbh) or die(mysql_error());

Once this is done, you should be able to put all the pieces together and get your first SaaS app up and running.


Part 3: Putting it all together

The site structure will be as follows-

www.mydummyapp.com: SaaS site (where the client registers)
*.mydummyapp.com: SaaS app (catch-all points to the actual app)

The workflow is as follows:

1. The client visits www.mydummyapp.com
2. The client registers on the site
3. The site creates the a DB (and user) for the client (and adds an entry in the site’s DB)
4. The client then uses the new URL (i.e. clientsubdomain.mydummyapp.com) for himself & his end-users
5. The end-users directly visit clientsubdomain.mydummyapp.com which is basically the app using the client’s DB


Where do we go from here?

We have achieved quite a bit using this tutorial. We have setup our app and site and can now allow clients to register and use the app. In the next parts will we discuss about managing the databases, setting up DNS, handling hosting, scaling and other tips & tricks.


Discuss, Share & Subscribe

Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.

To get a notification when the next part is ready, please subscribe using the form at the bottom of this page.


1557 Words
37215 Views