Bootstrappers

Table of Contents

  1. Introduction
  2. Registering Bindings
  3. Lazy Bootstrappers
    1. Targeted Bindings
    2. Caching
  4. Environment Variables
  5. Config Values

Introduction

Most applications need to do some configuration before starting. For example, they might need to setup a database connection, configure which view engine to use, or assign the authentication scheme to use. Because Opulence uses dependency injection heavily, it's important that you set your bindings in the IoC container. Bootstrappers are the place to do this.

Bootstrappers are loaded before the request is handled. Typically, they register bindings to the IoC container, but they can also perform any necessary logic once all of the bindings are registered or before the application is shut down. Bootstrappers extend Opulence\Ioc\Bootstrappers\Bootstrapper. Their constructors are empty.

Registering Bindings

Before you can start using your application, your IoC container needs some bindings to be registered. This is where Bootstrapper::registerBindings() comes in handy. Anything that needs to be bound to the IoC container should be done here. Once the application is started, all bootstrappers' bindings are registered.

use Opulence\Ioc\Bootstrappers\Bootstrapper;
use Opulence\Ioc\IContainer;
use Project\Domain\Users\UserRepo;

class UserBootstrapper extends Bootstrapper
{
    public function registerBindings(IContainer $container)
    {
        $container->bindInstance(UserRepo::class, new UserRepo());
    }
}

Lazy Bootstrappers

It's not very efficient to create, register bindings, run, and shut down every bootstrapper in your application when they're not all needed. Sometimes, you may only like a bootstrapper to be registered/run/shut down if its bindings are required. This is the purpose of lazy bootstrappers. In Opulence, you can designate a bootstrapper to be lazy-loaded by making it implement Opulence\Ioc\Bootstrappers\ILazyBootstrapper, which requires a getBindings() method to be defined. This method should return a list of all classes/interfaces bound to the IoC container by that bootstrapper. Let's take a look at an example:

namespace Project\Application\Bootstrappers;

use Opulence\Ioc\Bootstrappers\Bootstrapper;
use Opulence\Ioc\Bootstrappers\ILazyBootstrapper;
use Opulence\Ioc\IContainer;
use Project\Domain\Posts\IPostRepo;
use Project\Domain\Posts\PostRepo;

class PostBootstrapper extends Bootstrapper implements ILazyBootstrapper
{
    public function getBindings() : array
    {
        return [IPostRepo::class];
    }

    public function registerBindings(IContainer $container)
    {
        $container->bindInstance(IPostRepo::class, new PostRepo());
    }
}

Targeted Bindings

If you take advantage of targeted bindings in your lazy bootstrapper, you must indicate so in getBindings() by denoting targeted bindings in the format [BoundClass => TargetClass]. Let's say your repository class looks like this:

namespace Project\Domain\Posts;

use Opulence\Orm\DataMappers\IDataMapper;

class PostRepo implements IPostRepo
{
    private $dataMapper;

    public function __construct(IDataMapper $dataMapper)
    {
        $this->dataMapper = $dataMapper;
    }
}

Let's suppose you always want your dependency injection container to inject an instance of Project\Infrastructure\Posts\PostDataMapper into PostRepo. Here's a bootstrapper that accomplishes this:

namespace Project\Application\Bootstrappers;

use Opulence\Ioc\Bootstrappers\Bootstrapper;
use Opulence\Ioc\Bootstrappers\ILazyBootstrapper;
use Opulence\Ioc\IContainer;
use Opulence\Orm\DataMappers\IDataMapper;
use Project\Domain\IPostRepo;
use Project\Domain\Posts\PostRepo;
use Project\Infrastructure\Posts\PostDataMapper;

class PostBootstrapper extends Bootstrapper implements ILazyBootstrapper
{
    public function getBindings() : array
    {
        return [
            // This is a universal binding
            IPostRepo::class,            
            // This is a targeted binding
            [IDataMapper::class => PostRepo::class]
        ];
    }

    public function registerBindings(IContainer $container)
    {
        $container->bindInstance(IPostRepo::class, new PostRepo());
        $container->for(PostRepo::class, function (IContainer $container) {
            $container->bindSingleton(IDataMapper::class, PostDataMapper::class);
        });
    }
}

[IDataMapper::class => PostRepo::class] in getBindings() lets the bootstrapper know that IDataMapper is bound for PostRepo. When the bootstrapper's bindings are registered, IDataMapper will be bound to PostDataMapper whenever PostRepo is instantiated by the dependency injection container.

Caching

Opulence automatically caches data about its lazy and eager (ie not lazy) bootstrappers. This way, it doesn't have to instantiate each bootstrapper to determine which kind it is. It also remembers which classes are bound by which bootstrappers. If you add/remove/modify any bootstrappers, you must run php apex framework:flushcache command in the console to flush this cache.

Environment Variables

Sometimes, your bootstrappers need access to environment variables. To access them, simply use PHP's built-in getenv() function.

Config Values

If you're using the skeleton project and you need to access config values such as the path to a particular directory, use Opulence\Framework\Configuration\Config::get($category, $name). $category refers to the category of the config value, eg "paths" or "routing". The $name refers to the actual name of the config value.

Note: Config is only meant to be used within bootstrappers as a means to grab config values for your application. It's good practice to never use them outside of bootstrappers.