slimphp / Twig-View

Slim Framework view helper built on top of the Twig templating component
http://slimframework.com
MIT License
359 stars 87 forks source link

Twig could not be found in the server request attributes using the key "view" #174

Closed EdwinHuijsing closed 4 years ago

EdwinHuijsing commented 4 years ago

I installed the slim skeleton app like composer create-project slim/slim-skeleton [my-app-name] then I added composer require zeuxisoo/slim-whoops. and last composer require slim/twig-view:^3.0.

To get twig up and running with php-di (autowire) I added/changed the code blow. While executing the code I get the error: Twig could not be found in the server request attributes using the key "view"

public/index.php

-// Add Error Middleware
-$errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, false, false);
-$errorMiddleware->setDefaultErrorHandler($errorHandler);
+// // Add Error Middleware
+// $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, false, false);
+// $errorMiddleware->setDefaultErrorHandler($errorHandler);
+$app->add(new Zeuxisoo\Whoops\Slim\WhoopsMiddleware());

app/settings.php add

'twig' => [
    'path' => __DIR__ . '/templates/',
    'cache' => false, //__DIR__ . '/../var/cache/twig/',
            ],

app/dependencies.php add

'view' => function (ContainerInterface $c) {
    // Get template engin settings
    $settings = $c->get('settings');
    $twigSettings = $settings['twig'];

    $templatePath = $twigSettings['path'];
    $options = [
            'cache' => $twigSettings['cache']
    ];

    return Twig::create($templatePath, [$options]);
}

app/middleware.php add

$app->add(TwigMiddleware::createFromContainer($app));

app/routes.php

$app->get('/', function (Request $request, Response $response) {
-    $response->getBody()->write('Hello world!');
-    return $response;
+    $view = Twig::fromRequest($request);
+    return $view->render($response, 'index.twig', ['name' => 'test']);
});

app/templates/index.twig

<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Hello, world!</title>
</head>
<body>
    <br><br><br><br><br>
    <center>
        <p>Hi, my name is {{ name }}.</p>
    </center>
</body>
</html>

I am able to fix this, but is more of a hack in the twig-view package in Slim\Views\TwigMiddleware I alter the methode createFromContainer

return new self(
     $twig,
     $app->getRouteCollector()->getRouteParser(),
-    $app->getBasePath()
+    $app->getBasePath(),
+    $containerKey
);

Is this a bug or am I doing something wrong?

l0gicgate commented 4 years ago

So the problem comes from using TwigMiddleware::createFromContainer() in this instance the middleware gets created but the default $attributeName is null meaning that it will not be appending it to the request.

https://github.com/slimphp/Twig-View/blob/3.x/src/TwigMiddleware.php#L68

You are trying to access twig via Twig::fromRequest() within your route callback which is looking for the attribute on the request.

@adriansuter do you remember why we instantiated the middleware that way with a null attributeName in the TwigMiddleware::fromContainer() method?

@EdwinHuijsing In the interim you can simply access it directly via the container from your route callback using: $twig = $this->get('view')

adriansuter commented 4 years ago

I guess the idea was, that if a user explicitly injects the view to the controllers, then there is no need to add the instance to the request object (as attribute). In that case, the user can simply pass null as attribute name.

See also #127

l0gicgate commented 4 years ago

@adriansuter makes sense. Why include the object twice in both route and container. Pick one or the other.

Also worth mentioning that the examples on the README are pretty clear:

Example with container:

// Create App
$app = AppFactory::create();

// Add Twig-View Middleware
$app->add(TwigMiddleware::createFromContainer($app));

// Define named route
$app->get('/hello/{name}', function ($request, $response, $args) {
    return $this->get('view')->render($response, 'profile.html', [
        'name' => $args['name']
    ]);
})->setName('profile');

Example without container:

// Add Twig-View Middleware
$app->add(TwigMiddleware::create($app, $twig));

// Define named route
$app->get('/hello/{name}', function ($request, $response, $args) {
    $view = Twig::fromRequest($request);
    return $view->render($response, 'profile.html', [
        'name' => $args['name']
    ]);
})->setName('profile');
l0gicgate commented 4 years ago

I'm closing this as resolved.

agentcobra commented 4 years ago

maybe need more documentation in wiki with the skeleton template