Implementing slim/twig-view with php-di/slim-bridge

Hello,

I’m new to slim and was following the Authentication With Slim 4 tutorials from Zak H on youtube (https://www.youtube.com/watch?v=3Hg2WPwDyG8&list=PLNuh5_K9dfQ3ItwqXlPxmzLqSxCnEHLJp) to get started. In the tutorial they use Blade, but I want to use Twig as I understand it’s better with security. I’ve got everything setup correctly as far as I can tell, but I’m getting an error.

index.php

<?php

//Uses
use DI\Container;
use DI\Bridge\Slim\Bridge as SlimAppFactory;

//Requires
require __DIR__ . '/../vendor/autoload.php';

//Create the app container 
$container = new Container;

//Load settings
$settings = require __DIR__.'/../app/settings.php';
$settings ($container);

//Setup the Slim app factory
$app = SlimAppFactory::create ($container);
// Set the base path to run the app in a subdirectory.
$app->setBasePath("/slimapp");

//Add views
$views = require __DIR__.'/../app/views.php';
$views ($app);

// Add Slim middleware
$middleware = require __DIR__.'/../app/middleware.php';
$middleware ($app);

//Set routes
$routes = require __DIR__.'/../app/routes.php';
$routes ($app);

//Run it
$app->run();

routes.php

<?php

//Uses
use Slim\App;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Controllers\ToolController;

//Returns the call based on the route.  This is where the new tools get listed
return function (App $app) {
	//Testing twig
	$app->get ('/twig[/{name}]', [ToolController::class, 'twigTest']);
	//Undefined route
	$app->get ('/{tool}[/{step}]', [ToolController::class, 'selectTool']);
};

middleware.php

<?php

//Uses
use Slim\App;

//Runs the middleware
return function (App $app) {
	//First we have to pull settigns from the container
	$setting = $app->getContainer ()->get ('settings');
	
	//Middleware settings
	$app->addErrorMiddleware (
		$setting['displayErrorDetails'],
		$setting['logErrors'],
		$setting['logErrorDetails']
	);
};

settings.php

<?php

//Uses
use Psr\Container\ContainerInterface;
use Slim\Views\Twig;

//Returns the settings from the container
return function (ContainerInterface $container) {
	//Settings
	$container->set ('settings', function () {
		return [
			'displayErrorDetails' => true,
			'logErrorDetails' => true,
			'logErrors' => true,
			'views' => [
				'path' => 'resources/views',
				'settings' => ['cache' => false],
			],
			Twig::class => function (ContainerInterface $container) {
				$settings = $container->get ('settings')['twig'];
				$twig = Twig::create($settings['path'], $settings['settings']);
				return $twig;
			},
		];
	});
};

views.php

<?php

//Uses
use Slim\App;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
use Twig\Loader\FilesystemLoader;

//Returns the call based on the route.  This is where the new tools get listed
return function (App $app) {
	$container = $app->getContainer();

    $container->set ('view', function () use ($container) {
        $settings = $container->get ('settings')['views'];
        $loader = new FilesystemLoader ($settings['path']);
        return Twig::create ($loader, $settings['settings']);
    });

    $container->set ('viewMiddleware', function() use ($app, $container) {
        return TwigMiddleware::create ($container->get ('view'), $app->getRouteCollector ()->getRouteParser ());
    });
};

ToolController.php

<?php

//Uses and namespaces
namespace App\Controllers;
use Slim\Views\Twig as Views;

//Define the class
class ToolController {
	protected $views;

    public function __construct (Views $views) {
        return $this->view = $views;
    }

	public function selectTool ($response, $tool, $step = NULL, $id = NULL) {
		$response->getBody()->write("{$tool} initialized<br />");
		if ($step) {
			$response->getBody()->write("On step {$step}");
		}
		return $response;
	}

	public function twigTest ($request, $response, $name){
        return $view->render ($response, 'auth\home.twig', ['name' => $name]);
    }
}

Error message

Entry "App\Controllers\ToolController" cannot be resolved: Entry "Slim\Views\Twig" cannot be resolved: Entry "Twig\Loader\LoaderInterface" cannot be resolved: the class is not instantiable Full definition: Object ( class = #NOT INSTANTIABLE# Twig\Loader\LoaderInterface lazy = false ) Full definition: Object ( class = Slim\Views\Twig lazy = false __construct( $loader = get(Twig\Loader\LoaderInterface) $settings = (default value) array ( ) ) ) Full definition: Object ( class = App\Controllers\ToolController lazy = false __construct( $views = get(Slim\Views\Twig) ) )

From this thread it was suggested I need a contained definition: Slim-4 How to get twig in controllers with PHP-DI
But as far as I can tell I have that done properly in settings.php. Any assistance would be appreciated

I parsed out the two different view outputs to different controllers, jsut in case there was some logic contradiction I didn’t know about.

Updated routes.php

<?php

//Uses
use Slim\App;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Controllers\ToolController;
use App\Controllers\TwigController;

//Returns the call based on the route.  This is where the new tools get listed
return function (App $app) {
	//Testing twig
	$app->get ('/twig[/{name}]', [TwigController::class, 'twigTest']);
	//Undefined route
	$app->get ('/{tool}[/{step}]', [ToolController::class, 'selectTool']);
};

separated TwigController.php

<?php

//Uses and namespaces
namespace App\Controllers;
use Slim\Views\Twig;

//Define the class
class TwigController {
	protected $view;

    public function __construct (Twig $views) {
        return $this->view = $views;
    }

	public function twigTest ($request, $response, $name){
        return $this->view->render ($response, 'auth\home.twig', ['name' => $name]);
    }
}

Entering anything in my URL other than …/twig/foo shows the expected text output
Entering a twig URL gets the same error. I tried loading Twig directly from the request variable as I had seen some tutorials do and so the code then looked like this:

<?php

//Uses and namespaces
namespace App\Controllers;
use Slim\Views\Twig;

//Define the class
class TwigController {
	protected $view;

    public function __construct () {
    }

	public function twigTest ($request, $response, $name){
        return Twig::fromRequest ($request)->render ($response, 'auth\home.twig', ['name' => $name]);
    }
}

and that changed my error message to
Twig could not be found in the server request attributes using the key "view".
Which this post: Adding Twig to Slim4 Skeleton
suggests is an issue of when I’m adding the TwigMiddleware, but given how php-di/slim-bridge changed the middleware implementation I have no idea how I can restructure it.

I got it working. I’m not 100% sure I understand it properly, but I believe the issue was passing the app/container to the different steps and getting values added to them correctly.In case it comes up for others here’s the final layout:

settings.php

<?php

//Uses
use Psr\Container\ContainerInterface;

//Returns the settings from the container
return function (ContainerInterface $container) {
	//Settings
	$container->set ('settings', function () {
		return [
			'displayErrorDetails' => true,
			'logErrorDetails' => true,
			'logErrors' => true,
			'views' => [
				'path' => __DIR__.'/../resources/views',
				'settings' => ['cache' => false,
							   'auto_reload' => true],
			],
		];
	});
};

views.php

<?php

//Uses
use Slim\App;
use Slim\Views\Twig;

//Returns the call based on the route.  This is where the new tools get listed
return function (App $app) {
	$container = $app->getContainer ();
    $container->set ('view', function ($container) {
        $settings = $container->get ('settings')['views'];
        return Twig::create ($settings['path'], $settings['settings']);
    });
};

middleware.php

<?php

//Uses
use Slim\App;
use Slim\Views\TwigMiddleware;

//Runs the middleware
return function (App $app) {
	//Pull values from container
	$setting = $app->getContainer ()->get ('settings');
	//Twig
	$app->add (TwigMiddleware::create ($app, $app->getContainer ()->get ('view')));
	//Error handling
	$app->addErrorMiddleware (
		$setting['displayErrorDetails'],
		$setting['logErrors'],
		$setting['logErrorDetails']
	);
};

routes.php

<?php

//Uses
use Slim\App;
use Slim\Views\Twig;
use App\Controllers\ToolController;
use App\Controllers\TwigController;

//Returns the call based on the route.  This is where the new tools get listed
return function (App $app) {
	//Testing twig
	$app->get ('/twig[/{name}]', [TwigController::class, 'twigTest']);
	//Undefined route
	$app->get ('/{tool}[/{step}]', [ToolController::class, 'selectTool']);
};

TwigController.php

<?php

//Uses and namespaces
namespace App\Controllers;
use Slim\Views\Twig;

//Define the class
class TwigController {
	public function twigTest ($request, $response, $name = NULL) {
		$view = Twig::fromRequest ($request);
		return $view->render ($response, 'twigTest.php', [
			'name' => $name,
		]);
	}
}

twigTest.php

<div style="padding: 10px">
	<p>
		Auth home page for {{ name }}
	</p>
</div>