Quantcast
Channel: Slim Framework – Rob Allen's DevNotes
Viewing all articles
Browse latest Browse all 38

Accessing services in Slim 3

$
0
0

One of the changes between Slim Framework 2 and 3 is that the application singleton has gone.

In Slim 2, you could do this:

$app = \Slim\Slim::getInstance();
// do something with $app

In general, you didn't need access to $app itself, but rather you wanted access to something that the app knows about, such as a database adapter, or the router for access to the urlFor method to create a URL to a route.

With Slim 3, there is no getInstance() on App, so you need to inject the instances of whatever you need where ever you need them.

Setup the container

Let's start by setting up the container with a fictional mapper:

// create container and configure it
$settings = require 'settings.php';
$container = new \Slim\Container();

$container['pdo'] = function ($container) {
    $cfg = $container->get('settings')['db'];
    return new \PDO($cfg['dsn'], $cfg['user'], $cfg['password']);
};

$container['books'] = function ($container) {
    return new BookMapper($container->get('pdo'));
};

// create app instance
$app = new \Slim\App($container);

This is standard Slim 3 initialisation code, to which we have added a DI entry called books which depends on a PDO instance, so we have also registered that to allow the container to create the BookMapper when we ask for it.

How would we get at books within our application?

Route callables

Let's start by looking at the two common ways to write a route callable.

A route callable closure:

If you use a closure, then Slim binds the application instance to $this.

$app->get('/', function($request, $response, $args) {
    $books = $this->getContainer()->get('books');
    // do something with $books and then return a response
};

There's also a shortcut you can use as App implements __get which proxies to the container object's get method:

$app->get('/', function($request, $response, $args) {
    $books = $this->books; // retrieve from the container
    // do something with $books and then return a response
};

A route callable class:

If you use a class method for a route callable like this:

$app->get('/', 'HomeAction:dispatch');

Slim will look for a DI key of HomeAction, use the DI container to instantiate the class and then dispatch by calling the dispatch() method.

Hence, you should use the DI container to inject what you need into the class constructor:

class HomeAction {
    public function __construct($books)
    {
        $this->books = $books;
    }

    public function dispatch($request, $response, $args)
    {
        $books = $this->books;
        // do something with $books and then return a response
    }
}

We now need to register the class with the container:

// in index.php:
$container['HomeAction'] = function ($container) {
    return new HomeAction($container->get('books'));
};

The registered route will now work and have access to the BookMapper.

All of Slim's services are in the container, so where I've used books in this example, you can access anything that Slim has registered, such as settings or router and in addition to what you have registered yourself.

Middleware

The same thing pattern works with both app and route middleware.

Middleware closure

If you use a closure in middleware, then the container is bound to $this:

$app->add(function ($request, $response, $next) {
    $settings = $this->get('settings');
    // do something with $settings
    return $next($request, $response);
});

Middleware class

You can also use a class which works exactly as it does for a route:

$app->add('App\Middleware\AppMiddleware:run');

Slim will look for a DI key of App\Middleware\AppMiddleware, use the DI container to instantiate it and then call the run() method.

As before, inject your dependencies via the class constructor:

namespace App\Middleware;

class AppMiddleware
{
    public function __construct($settings)
    {
        $this->settings = $settings;
    }

    public function run($request, $response, $next)
    {
        $settings = $this->settings;
        // do something with $settings
        return $next($request, $response, $next);
    }
}

and register with the container:

$container['App\Middleware\AppMiddleware'] = function ($container) {
    return new App\Middleware\AppMiddleware($container->get('settings'));
};

I've found this pattern to be nicely consistent and easy to remember.

To sum up

Slim 3 encourages you to think a little more carefully about which dependencies you need for any given middleware or action. Due to closure binding and the built-in DI container, it's easy to access the classes you need, when you need them.


Viewing all articles
Browse latest Browse all 38

Trending Articles