Slim framework is my go-to micro framework for all small projects because of it's simplicity and easiness to use. The new version of this cool framework is just around the corner and they have released RC-1 a few days back. Slim 3 have some very good features that makes it more flexible and easy to use. Support for PSR-7 and Dependency Container are the two really interesting features in the new version.
Slim 3 has a default DI Container that extends Pimple. Pimple is quite simple and often people tend to use it as a service locator than a DI Container. You will set the services in the container and will pass the di container around the application to get the services in different classes. This idea is not always good for several reasons.
Though Slim uses Pimple by default, we can replace it with any DI container that implements Container Interoperability interface, which is a really great feature. The only caveat is Slim sets some of it's required services from the default container. So we should do it by ourself when we use a different library.
Aura.DI
Aura.DI is a powerful dependancy injection container that has a lot of features like constructor injection, setter injection, lazy loading etc. Inheritance of constructor parameter from the parent class is one of the most interesting feature of Aura.DI. It lets you to define the constructor and setter parameters of the parent class and avoid repeating them for the child classes. This is especially useful for setting common constructor parameters for Controllers and Model classes.
Slim3 Aura.Di Bridge
Slim3 Aura.Di Bridge is a small library that extends Aura.DI to use with Slim 3. It also sets the services that are required by Slim. Let's see how we can use it in the akrabat/slim3-skeleton project.
First we need to install the package.
$ composer require shameerc/slim3-aura-di
Now we can create a new instance of Slim App.
// Instantiate the app
$settings = require __DIR__ . '/../app/settings.php';
$container = new SlimAura\Container($settings['settings']);
$app = new \Slim\App($container);
Note that we are passing $settings['settings']
array than just $settings as in the slim3-skeleton
example.
Now change the dependencies.php file where we specify all dependency injections.
<?php
$container = $app->getContainer();
$settings = $container->get('settings');
// -----------------------------------------------------------------------------
// Service providers
// -----------------------------------------------------------------------------
// Twig
$container->set('view', function () use ($container) {
$settings = $container->get('settings');
$view = new \Slim\Views\Twig($settings['view']['template_path'], $settings['view']['twig']);
// Add extensions
$view->addExtension(new Slim\Views\TwigExtension($container->get('router'), $container->get('request')->getUri()));
$view->addExtension(new Twig_Extension_Debug());
return $view;
});
// Flash messages
$container->set('flash', $container->lazyNew('\Slim\Flash\Messages'));
// Monolog
$container->params['Monolog\Logger']['name'] = $settings['logger']['name'];
$logger = $container->newInstance('Monolog\Logger');
$logger->pushProcessor(new \Monolog\Processor\UidProcessor());
$logger->pushHandler(new \Monolog\Handler\StreamHandler($settings['logger']['path'], \Monolog\Logger::DEBUG));
$container->set('logger', $logger);
// Controller Injections
$container->params['App\Action\HomeAction']['view'] = $container->get('view');
$container->params['App\Action\HomeAction']['logger'] = $container->get('logger');
$container->set('App\Action\HomeAction', $container->lazyNew('App\Action\HomeAction'));
Here everything is pretty much self explanatory. Rather than creating App\Action\HomeAction
using new
, we let the container to lazyly create it when required. If we have many Actions that require the same costructor parameters, we can create a base class with those constructor parameters and avoid repeating the params.
<?php
// app/src/BaseAction.php
namespace App\Action;
use Slim\Views\Twig;
use Psr\Log\LoggerInterface;
class BaseAction
{
protected $view;
protected $logger;
public function __construct(Twig $view, LoggerInterface $logger)
{
$this->view = $view;
$this->logger = $logger;
}
}
Then all other Action classes will extend this base class
<?php
// app/src/HomeAction.php
namespace App\Action;
final class HomeAction extends BaseAction
{
public function dispatch($request, $response, $args)
{
// ...
}
}
We also need to update the dependency injections to inject parameters into the BaseAction
.
// Controller Injections
$container->params['App\Action\BaseAction']['view'] = $container->get('view');
$container->params['App\Action\BaseAction']['logger'] = $container->get('logger');
$container->set('App\Action\HomeAction', $container->lazyNew('App\Action\HomeAction'));
$container->set('App\Action\ProfileAction', $container->lazyNew('App\Action\ProfileAction'));
Summary
Slim 3 uses a container to create and manage application dependencies. By default it uses Pimple, but we can easily swap it with any DI Containers that implements container-interop
. If you are already comfortable with another library, it makes sense to use it than using the default one. Rob Allen already wrote an article on how to use Zend\ServiceManager
with Slim 3.
Don't forget to check my slim3-skeleton project for more example on using Aura to inject dependencies.
To learn more about Aura.DI checkout the documentation
Let me know your thoughts in comments below.