Slim Di Bridge and Interfaces


#1

Hi, im using Slim3 Di Bridge and some Interfaces

Can someone please explain, i have tried to source the answers

  1. I don’t understand why I need to create a flashInterface. I understand an interface is like a contract with rules that’s needed to be followed and implemented in a class.
  2. The flash class and FlashInterface are all dummies, no rules, why do I need to create them, if I later need to add Swiftmailer do i also need to create an class and interface
  3. The DI Bridge is autoloading Classes, in other words I don’t need to use “NEW” to create a class, just use it… ? In the MyApp, im still using “new” to create the link to the twig files.

Any better way of doing this? Not sure if my code is optimized

MyApp.php this is just a short version

namespace App;

use DI\ContainerBuilder;
use Interop\Container\ContainerInterface;
use App\Interfaces\FlashInterface;
use App\Helpers\Flash;

class MyApp extends \DI\Bridge\Slim\App {

    protected function configureContainer(ContainerBuilder $builder) {
        $definitions = [
            'settings.displayErrorDetails' => true,
            \Slim\Views\Twig::class => function (ContainerInterface $container) {
                $view = new \Slim\Views\Twig(__DIR__ . '/../twig/views', [
                    'cache' => false,
                ]);

                $view->addExtension(new \Slim\Views\TwigExtension(
                        $container->get('router'), $container->get('request')->getUri()
                ));

                $view->getEnvironment()->addGlobal('flash', $container->get('flash'));
                return $view;
            },
            FlashInterface::class => function () {
                return new Flash();
            },
            'flash' => \DI\get(FlashInterface::class),
        ];

        $builder->addDefinitions($definitions);
    }
}

flash.php

namespace App\Helpers;

use Slim\Flash\Messages;
use App\Interfaces\FlashInterface;

class Flash extends Messages implements FlashInterface {
    // dummy
}

FlashInterface.php

namespace App\Interfaces;

interface FlashInterface {
    // Dummy
}


#2

I use PHP-DI with Slim (using Slim-Bridge) but I’m far from an expert on this. Based on what I’d see, I’d say:

(1) I don’t think you need to.
(2) Same, I don’t think you need to.
(3) If you don’t put new there, I believe PHP will think Slim\Views\Twig is a function.

Are you trying to add the Slim\Flash extension? Or is this a different package?


#3

Hi Tim

Thanks for responding, I’m using the slim flash package extension, in
theory I also think it should work, maybe my approach is wrong, do you
perhaps have a skeleton code of a small project, that uses eloquent models,
twig, slim flash,mono-log, swift-mailer that’s a email package, will do the
comparison between mine and yours. Thanks


#4

No, I don’t have a skeleton that uses those packages. But I can try to help get you going by helping with one or two. You can also use a crafted Google search to look for other packages that use certain combinations of tools. For example this search should show you all packages that use both slim/flash as well as PHP-DI Slim Bridge.

site:packagist.org/packages "slim/flash" "php-di/slim-bridge"

Are you actually using slim/flash or are you using kanellov/slim-twig-flash? If slim/flash then note that it doesn’t come with a Twig extension so you would likely need to write your own.


#5

I got Slim Di Bridge working with flash and the dummy interfaces as displayed in the sample code posted earlier in this post. I don’t think I’m using the bridge to its full extend. Was looking for a fresh pair of eyes to brows the code.

namespace App;

use App\Auth\CsrfGuard;
use App\Basket\Basket;
use App\Database\CapsuleDatabase;
use App\View;
use Carbon\Carbon;
use DI\ContainerBuilder;
use Interop\Container\ContainerInterface;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Noodlehaus\ConfigInterface;
use Psr\Log\LoggerInterface;
use Slim\Exception\NotFoundException;
use Slim\Interfaces\RouterInterface;
use Slim\Views\TwigExtension;
//Handlers
use App\Handlers\ErrorCustom;
use App\Handlers\NotFoundCustom;
//Helpers
use App\Helpers\Flash;
use App\Helpers\Session;
use App\Helpers\SEmail;
//Interfaces
use App\Interfaces\Auth\CsrfGuardInterface;
use App\Interfaces\Auth\ValidationErrorsMiddleware;
use App\Interfaces\Auth\OldInputMiddleware;
use App\Interfaces\Auth\GuestMiddlewareInterface;
use App\Interfaces\Database\DatabaseInterface;
use App\Interfaces\Helpers\FlashInterface;
use App\Interfaces\Helpers\SessionInterface;
use App\Interfaces\ViewInterface;
//Models
use App\Models\Address;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Payment;
use App\Models\Product;
//Support
use App\Support\Storage\Contracts\StorageInterface;
use App\Support\Storage\SessionStorage;
//Validation
use App\Validation\Contracts\ValidatorInterface;
use App\Validation\Validator;

class App extends \DI\Bridge\Slim\App {

    private $config;
    private $session;

    public function __construct(
    ConfigInterface $config
    ) {
        $this->config = $config;

        $this->session = new Session;

        if (!$this->session->isset1('csrfToken')) {
            $this->session->set('csrfToken', password_hash(time(), PASSWORD_BCRYPT));
        }

        $containerBuilder = new ContainerBuilder;
        $this->configureContainer($containerBuilder);
        $container = $containerBuilder->build();

        parent::__construct($container);
    }

    protected function configureContainer(ContainerBuilder $builder) {
        $definitions = [
            'settings.httpVersion' => $this->config
                    ->get('slimConfig.settings.httpVersion'),
            'settings.responseChunkSize' => $this->config
                    ->get('slimConfig.settings.responseChunkSize'),
            'settings.outputBuffering' => $this->config
                    ->get('slimConfig.settings.outputBuffering'),
            'settings.determineRouteBeforeAppMiddleware' => $this->config
                    ->get('slimConfig.settings.determineRouteBeforeAppMiddleware'),
            'settings.displayErrorDetails' => $this->config
                    ->get('slimConfig.settings.displayErrorDetails'),
            ViewInterface::class => function (ContainerInterface $container) {
                $view = new View($container->get('config')->get('twig.path'), [
                    'cache' => $container->get('config')->get('twig.cache'),
                    'debug' => $container->get('config')->get('twig.debug'),
                ]);

                $view->addExtension(new TwigExtension(
                        $container->get('router'), $container->get('request')->getUri()
                ));

                $view->addExtension(new \Twig_Extension_Debug());
                $view->addExtension(new \Twig_Extensions_Extension_Intl());

                $thisHost = $container->get('request')->getUri()->getScheme() . '://' .
                        $container->get('request')->getUri()->getHost();
                $thisBasePath = $container->get('request')->getUri()->getBasePath();
                $thisPath = $container->get('request')->getUri()->getPath();
                $thisUri = (string) $container->get('request')->getUri();

                $view->getEnvironment()->addGlobal(
                        'globals', [
                    'host' => $thisHost,
                    'basePath' => $thisBasePath,
                    'path' => $thisPath,
                    'thisUri' => $thisUri,
                    'cdn' => $container->get('config')
                            ->get('app.cdn'),
                    'css' => $container->get('config')
                            ->get('app.css'),
                    'js' => $container->get('config')
                            ->get('app.js'),
                    'images' => $container->get('config')
                            ->get('app.images'),
                    'csrfToken' => $this->session->get('csrfToken'),
                        ]
                );

                $view->getEnvironment()->addGlobal('flash', $container->get('flash'));
                $view->getEnvironment()->addGlobal('basket', $container->get(Basket::class));
                return $view;
            },
            'view' => \DI\get(ViewInterface::class),
            'errorHandler' => function (
            ViewInterface $view,
            ContainerInterface $container,
            LoggerInterface $logger
            ) {
                return new ErrorCustom(
                        $container->get('config'), $view, $logger
                );
            },
            'notFoundHandler' => function (
            ViewInterface $view,
            ContainerInterface $container,
            LoggerInterface $logger
            ) {
                return new NotFoundCustom(
                        $container->get('config'), $view, $logger
                );
            },
            LoggerInterface::class => function (ContainerInterface $container) {
                $logger = new Logger(
                        $container->get('config')->get('logger.name')
                );
                $logger->pushProcessor(new UidProcessor());

                $streamHandler = new StreamHandler(
                        $container->get('config')->get('logger.path'), Logger::DEBUG
                );

                $logger->pushHandler($streamHandler);

                return $logger;
            },
            'logger' => \DI\get(LoggerInterface::class),
            RouterInterface::class => function (ContainerInterface $container) {
                return $container->get('router');
            },
            // FlashInterface::class => \DI\object(Flash::class),
            FlashInterface::class => function () {
                return new Flash();
            },
            'flash' => \DI\get(FlashInterface::class),
            DatabaseInterface::class => function () {
                return new CapsuleDatabase();
            },
            'db' => \DI\get(DatabaseInterface::class),
            ConfigInterface::class => function () {
                return $this->config;
            },
            'config' => \DI\get(ConfigInterface::class),
            SessionInterface::class => function () {
                return $this->session;
            },
            'session' => \DI\get(SessionInterface::class),
            CsrfGuardInterface::class => function () {
                $null = null;
                return new \App\Auth\CsrfGuard('csrf', $null, function ($request, $response, $next) {
                            $route = $request->getUri()->getPath();
                            $parsedBody = $request->getParsedBody();
                            if (
                                    isset($parsedBody['csrfToken']) && $parsedBody['csrfToken'] === $this->session->get('csrfToken') && in_array($route, [
                                        'ajax/receive', //  a route that receives ajax
                                            ]
                                    )
                            ) {
                                return $next($request, $response);
                            }
                            throw new \Exception('CSRF Problems on route: ' . $route);
                        }
                );
            },
            'csrf' => \DI\get(CsrfGuardInterface::class),
            ValidatorInterface::class => function (ContainerInterface $container) {
                return new Validator;
            },
            StorageInterface::class => function (ContainerInterface $container) {
                return new SessionStorage('cart');
            },
            OldInputMiddlewareInterface::class => function (ContainerInterface $container) {
                return new OldInputMiddleware();
            },
            ValidationErrorsMiddlewareInterface::class => function (ContainerInterface $container) {
                return new ValidationErrorsMiddleware();
            },
            GuestMiddlewareInterface::class => function (ContainerInterface $container) {
                return new GuestMiddleware();
            },
            Product::class => function (ContainerInterface $container) {
                return new Product;
            },
            Order::class => function (ContainerInterface $container) {
                return new Order;
            },
            Customer::class => function (ContainerInterface $container) {
                return new Customer;
            },
            Address::class => function (ContainerInterface $container) {
                return new Address;
            },
            Payment::class => function (ContainerInterface $container) {
                return new Payment;
            },
            SEmail::class => function (ContainerInterface $container) {
                return new SEmail();
            },
            Basket::class => function (ContainerInterface $container) {
                return new Basket(
                        $container->get(SessionStorage::class), $container->get(Product::class)
                );
            },
        ];

        $builder->addDefinitions($definitions);
    }

}

#6

PHP-DI uses autowiring, so you don’t need to (but you can) define the dependency in cases such as:

        Product::class => function (ContainerInterface $container) {
            return new Product;
        },

and should even be automatically be able to create a Basket instance in your case.


#7

You will need to add a definition for Slim\Flash\Messages, because the constructor has an optional argument that PHP-PI can’t automatically fill:

class Messages {
    // PHP-DI autowiring doesn't know how to deal with $storage
    public function __construct(&$storage = null) 

I have the following:

    Messages::class => function (ContainerInterface $container) {
        return new Messages();
    },

And then in the controller using service injection:

    public function __invoke(Request $request, Response $response, Messages $messages)
    {
        // ...
        $messages->addMessage('success', 'Something succeeded');
        // ...
    }

You don’t need to use an interface (such as FlashInterface), but it will make it easier later on to swap the implementation.


#8

Hi
Thanks for responding.

The one thing that was confusing was the use of the dummy interface,
Is this a typo error… the “&” in the public function __construct(&$storage = null)

regards


#9

No, that is a PHP reference. (But don’t ask me to explain them. ;))


#10

lol, i understand, this is very cryptic