The Laravel Service Container, not as scary as you think

Yea, it sounds scary when someone mentions the IoC or Service Container, but it is a really easy and elegant way to store classes, and other data to retrieve later throughout your app.

I have a one-database multi-tenant app – which is a fancy way for saying multiple clients (tenants) who use the same codebase and can access only their data.  Each client has its own url, a CNAME that is setup, so I can look at that to determine what client I need to display content for and tailor the UI for them.

There are different ways to do this, and here are a few options depending on what you want to achieve. The first is the “typical” way I guess you could call it.

Option 1: No Container, make the call in all your controllers

In any / all of your controllers you could simply get the current client with something like this.

$client = \App\Client::getClientInfo();

In the Client model you’d have this method

public static function getClientInfo()
{
    return \App\Client::where('client_url', '=', $_SERVER['SERVER_NAME'] )->first();
}

This looks at the url, and hits the database and returns the client record that corresponds to that url.  Simple.  So why not do it this way?  First, this does not use the container, therefore your “current client” data it scoped locally within this method in the controller and you’re doing the lookup each time this executes.  While it totally works, and is easy to setup, its not ideal for those reasons.  We always have to do that lookup each time we want to know who’s using our app. (Yes you could store it all in a session if you wanted, but we don’t)

Option 2: Store the entire class for later

An alternate method that is similar functionally to the one above but uses the Service Container works this way.

In the boot method of AppServiceProvider.php you would bind the Client class in the container like this –

$this->app->bind('Client', \App\Client::class);

Then in say the DashBoardController.php (and really all controllers you need this information in) you can pull it out and call a method in it like this –

public function index()
{
   $client = app()->make('Client')->getClientInfo();
}

Now the $client variable references the Client class we bound in the service container and by chaining the getClientInfo method call you can pull out the current client’s record. Just like option 1 above,  the getClientInfo method contains the logic to look at the url to decide which client it is (note this time it’s not a static method)

public function getClientInfo()
{
    return \App\Client::where('client_url', '=', $_SERVER['SERVER_NAME'] )->first();
}

This is a baby step toward using the container, you’re storing the class, but you’re still doing a lookup every time you want the information.

But, there’s a better way!

Option 3: Store the current client in the Service Container instead

This is the method I chose, so whenever I pull it out, my $client is already determined and everything is at my fingertips.

Just like option 2 above, I setup my binding in the boot method of AppServiceProvider.php, but this time we’ll handle the URL logic here instead

app()->bind('Client', function() {
    return \App\Client::where('client_url', '=', $_SERVER['SERVER_NAME'] )->first();
});

At first glance that looks overwhelming, I thought that too.  Here’s the rundown of what its doing, its pretty simple once you understand it… I guess most things are 🙂

We’re going to setup a new binding (think of it as a bucket for now) and I’m naming it ‘Client’ (the green text).  Don’t get confused, I could have called it CurrentClient or anything that makes sense to me.  I just stuck with Client because that’s what I called the model. The callback function (everything in the curly brackets / purple text) is what we’re going to execute and place its results into our Client bucket.  You’ll recognize the following code from Option 1 and 2’s getClientInfo method – its the same simple code

return \App\Client::where('client_url', '=', $_SERVER['SERVER_NAME'] )->first();

it calls our Client model (not the biding bucket) and selects the client record from the database that matches our url.  With the return tacked to the front of this line inside our bind statement, it will return that client record INTO our Client bucket. To explain in simple terms, we shoved our current client into our bucket for later.

What we have then is a new Service Container binding called Client that holds our current client based on the url of the app.

Great, so now what?

We use it!

In our controllers, yes we still have to pull it out in each one, but there’s no database lookup each time – we do this

$client = app()->make('Client');

and now I have the current client object available to me and I can use something like $client->id to go pull content, settings or pull out the name, logos, and colors from that object to alter the UI specifically for them.

Where to go from here

You can use these bindings for anything you want, classes like we’ve seen above, configuration settings, strings, variables, constants. The container is really nice once you get the hang of it.

 

 

This entry was posted in Laravel. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *