Drivers and Managers in Laravel

Posted on Updated on Laravel by Gergő D. Nagy

One of the biggest features in Laravel is the ability to create services easily that can be extended and customized in a painless way. For example, we can quickly set up our own authentication guard and use it where we want. Let’s take a look at how to write a custom manager that we can extend without touching it.

Laravel offers a very simple way to switch between different technologies easily. Let’s say we want Redis based sessions instead of a file-based solution. All we need to do is to switch the driver in our config (or .env) file.

Or also, we can bring our custom driver and after registering it, we can just simply set the config to our driver and it’s ready to go. This is one of the most powerful features of Laravel and this makes things very easy and fluent.

Let’s take a look at the core concept of this solution.

The Manager Class

The Illuminate\Support\Manager class is the base for the driver managers, like Session or Cache managers. Using the functionality of the base class, we have the ability to easily extend a custom service and implement different kinds of drivers.

When we are writing our custom extendable services, probably the best way to start is to extend the manager class and adapt its functionality. Let’s take a look at a concrete example.

Writing a Cart Resolver Service

Let’s say we have a webshop. Like every webshop, we also have a cart system where the users can store their items before checking out.

Let’s say we store the carts in the DB, but we need to make sure we can retrieve the cart model easily from the web or the API interface as well. This means we need multiple cart drivers because maybe we can use a session on the web interface to store the credentials we need to retrieve the cart. But what if we support AJAX cart updates? The API is stateless sessions can’t be used, we need another driver to retrieve the cart.

So first of all, let’s create the cart manager class and then bind it to the container.

<?php

namespace App\Services;

use Illuminate\Support\Manager;

class CartManager extends Manager
{
    /**
     * Get the default driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->config->get('cart.default');
    }
}

This is it. The rest is taken care of by the manager class. All we need to do is to implement the getDefaultDriver class, to provide the default driver value. Usually, we store it in the configuration. Now, let’s bind the service to the container. We can do that, in a service provider’s boot method.

$this->app->singleton('cart', function ($app) {
    return new \App\Services\CartManager($app);
});

From this point, we can easily resolve our cart manager instance from the app container and register the drivers we want to use.

Registering Drivers in the Manager

Let’s say, we have a SessionDriver, a CookieDriver and a NullDriver for testing purposes. We won’t go in the resolution logic, because it can be really anything. The point is, how to extend the manager and register the drivers into it.

Because we bound the manager in the container, we can easily resolve it and using the extend() method to register the drivers.

$cart = app('cart');

$cart->extend('session', function ($app) {
    return new SessionDriver($app);
});

$cart->extend('cookie', function ($app) {
    return new CookieDriver($app);
});

$cart->extend('null', function ($app) {
    return new NullDriver($app);
});

At this point, if we set the default cart driver to session in our config, the SessionDriver‘s resolution logic will be the default one.

Of course, we can also resolve our cart by using explicit parameters, for example in the API cart controller:

$manager = app('cart');

$cart = $manager->driver('cookie')->cart();

Summary

This is just a very brief introduction to the manager type services. Probably if you need to implement different kind of resolutions, this approach can be a good starting point.

Need a web developer? Maybe we can help, get in touch!