Bill Garrison
Bill Garrison

Reputation: 2227

Laravel 5 - One server / multiple databases determined by request

So I am developing an application that has two kinds of deployments. One is they host everything and there is one application / one database and no issues. The second (the one this question will center around) will have one application but different database connections per client.

Access to each domain will be white-listed so we don't have to worry about one client popping another clients domain in and their is other authentication on top of that.

What I need is two-fold:

  1. a way to determine the client that is accessing the application (request headers? domain driven?)
  2. grab the configurations for said client so they only access their databases information (use a certain .env file based on client?).

One idea I have thought of is using apache to set env variables based on the domain requested. However, I would prefer to keep this handling inside of laravel. Any ideas?

Upvotes: 1

Views: 1448

Answers (1)

jszobody
jszobody

Reputation: 28911

We do this very thing. As you noted, you need some authority that can map domains to clients, and then a away to fetch the client-specific settings.

Here is how we set it up:

  1. We have different Apache vhost per client, with all the domains/aliases the client has setup. In the vhost file, Apache gives us one single env variable CLIENT_ID. That way we know who the client is, no matter which domain alias is being used at any one time.

  2. We have a Laravel service provider then that looks at env('CLIENT_ID') and sets up a number of config options. One of the things that gets set is a client-specific path, where we store a number of client-specific resources and files. So we're doing something like this:

    Config::set("paths.client", "/var/www/clients/" . env("CLIENT_ID"));

  3. Now that we have the client-specific paths setup, we can use Dotenv to go load a client-specific .env file for this client. Note we still have a root .env file in our app directory (Laravel expects it) which sets up common config. The client-specific .env file sets up client-specific stuff like SMTP settings, and of course database connection settings.

    Dotenv::load(Config::get("paths.client"));

  4. Now that the client-specific .env is loaded, we can easily write to the database.connections config array. And from there on out, eloquent and everything else just work.

    Config::set('database.connections.mysql', [ 'database' => env('DB_DATABASE'), 'username' => env('DB_USERNAME'), 'password' => env('DB_PASSWORD'), ]);

You wouldn't have to do it this way of course. We chose Apache vhosts as our authority for mapping domains to clients, but you could have a master database or just a config file. Then in your service provider, you just need to ask the master db (or the config file) what client needs to be setup.

From there your client settings could even be in the master database or config file, if you didn't want to store separate .env files. Whole lot of ways you can skin this cat.

Ultimately, however you get there, you once you set the database config connection array, you're good to go.

Update:

For anyone reading this in the future, there is another very simple way to load alternate .env files as mentioned in the comments below:

If you set APP_ENV somehow before you get to Laravel (SetEnv in virtualhost etc) it will by default look for .env.APP_ENV (Ex: .env.demo) file. I am using SetEnv to set APP_ENV and then creating .env files for each client.

For our purposes, we didn't want client-specific .env files sitting in the root of our app. But it could work quite well for other situations.

Upvotes: 2

Related Questions