Multi-Tenancy in Laravel 5.5

For the past few months I have been working on a SaaS which, like most SaaS applications has multi-tenancy at it’s core. In this post, I am going to share code from my application and discuss why I have implemented it the way I have. I have implemented it in such a way that each tenant has their own database, I chose this method because it’ll make it easier to split the databases across multiple servers if the need to scale horizontally ever arises.

Model and Database Configuration

I have 2 databases that will be called for each tenant, the dms_core database which holds the instance information and dms_slug table which will be the tenant specific instance. The tenant and domain models are part of the core, and because of this they both have the following property set

in database.php i’ve modified the configuration to look like the following

This means that I have 2 database connections, mysql_core is the core connection and mysql the default connection for models will be the tenant/instance specific connection that is modified later on.

Migrations

The first table I created was the tenants table, this is the table that is going to keep track of all the SaaS instances.

The second table is the table that is going to store all the domains associated with each instance, the is_root column will be used to redirect non-root domains to the root domain.

Service Provider

The next step is switching out the database, I chose to do this in a service provider because this is where I feel this code should live – it’s specific to the entire application not a just HTTP requests for example.

This code checks to see if the domain is registered to a tenant and then binds the record to the IoC container – this means that I don’t need any additional database queries to fetch the current tenant. One thing I did notice, if null is returned from an IoC container binding the closure is ran every time the binding is resolved, this is why I am returning false. I then change the database name to the slug retrieved from the tenant record.

Middleware

So I’ve bound the tenant to the IoC container, and swapped out the database if a tenant was found matching the domain name. But what do we do in the HTTP layer? Well, I decided to write some middleware to handle this.

If the tenant binding in the IoC container returns false, we can assume that no tenant was found. So lets return a 404, otherwise continue to the next request.

With the way I have implemented this, the maximum number of overhead that is added by the multi-tenancy implementation is 2 database queries which I think is pretty impressive.

I think that covers everything, if anyone has any questions, comments or ideas on how to improve this let me know!

Edit – There’s a barebones example of this available on my GitHub profile here (some of the code may differ slightly to what’s in this post) – https://github.com/craigsansam/multi-tenant-base

About the Author

Posted by

Categories:

code, development, dms

10 Comments

Any chance you could cook up a barebones demo of this? I’m new to Laravel & multi-tenancy in general, so seeing some actual code would really help. Whatever your response may be, thanks for sharing this – I’m sure it’ll be of help in the future.

Hi Héctor,

I’ll get a barebones demo of this coded up on GitHub 🙂 I’ll let you know when it’s there!

Craig

Hey Craig,

Unfortunately I just injured my right arm in a motorcycle accident and won’t be able to play around with your demo just yet ( typing this from the phone with my left hand was more challenging than I expected hehe). I’ll do so once I’m able to use my right hand again. If I have any questions / observations / suggestions I’ll make sure to let you know.

Once again, thank you for being do kind and share this with everyone.

Hi Craig! Thanks for this blog post. Just curious, could not see this in your example, do you have any ideas about how to serve user-uploaded assets like images for each tenant if they have separate domains?

Hi Jan,

It depends on the size of the files you wish to serve.

If the files are small, I would make a PHP based “proxy”, for example tenant.com/files -> FilesController@view -> Uses the tenant bound in the IoC container to proxy files from storage/tenants/tenant.com.

If the files are larger, I would use nginx and a structure like uploads.systemdomain.com/tenant.com to serve the assets – This will be a bit more difficult to develop; but will be faster and less resource intensive than the PHP Proxy.

If there’s anything else I can help with, let me know!

Craig

Hi Craig, thanks for your answer! 🙂

I will undoubtedly go for the Nginx and try_files-approach since it’s images, but the trouble is the URLs. Like env(‘APP_URL’) which is used here and there directly (changing app.url in Config seems not do do it.) in various places and overriding .dotenv is against the whole design.

Instead, I have to set APP_URL to blank and manually add the domain where it’s needed. Would be so nice just to tap into APP_URL and change it.

Hello!
i am following your blog.. i have an issue, i cloned your project, composer installed it, and configured DB and DB_CORE variables, generated the key for the application, but when i try to see laravel using multi-tenant-base.dev, it says sorry the page you are looking for could not be found

Add a Response

Your name, email address, and comment are required. We will not publish your email.

The following HTML tags can be used in the comment field: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

%d bloggers like this: