Right way to pass session and other data to view

Hi,
I’m struggling about passing session data to the view.

I’m using the Slim 4 Skeleton of @odan and use the SessionInterface configured to use Redis.
The user login is working, however what is the best practice to pass user_id, name and other informations from the session to the view? For the view I use the Responder object in the skeleton (don’t know why I use it, it was right here), and I have added on the “withTemplate” method a lot of addAttribute to add all the variables I need.
However it don’t seems the right way.

Example code:

    /**
     * Output rendered template.
     *
     * @param ResponseInterface $response The response
     * @param string            $template Template pathname relative to templates directory
     * @param array             $data     Associative array of template variables
     *
     * @return ResponseInterface The response
     */
    public function withTemplate(ResponseInterface $response, string $template, array $data = []): ResponseInterface
    {
        // Add some global attributes from session to view
        $this->phpRenderer->addAttribute('user_id', $this->session->get('user_id'));
        $this->phpRenderer->addAttribute('username', $this->session->get('username'));
        $this->phpRenderer->addAttribute('tenant_id', $this->session->get('tenant_id'));
        $this->phpRenderer->addAttribute('tenant_name', $this->session->get('tenant_name'));
        $this->phpRenderer->addAttribute('flashes', $this->session->getFlash());

        // Add global attributes from csrf to view
        $nameKey  = $this->csrf->getTokenNameKey();
        $valueKey = $this->csrf->getTokenValueKey();
        $name     = $this->csrf->getTokenName();
        $value    = $this->csrf->getTokenValue();

        $this->phpRenderer->addAttribute('csrf_nameKey', $nameKey);
        $this->phpRenderer->addAttribute('csrf_name', $name);
        $this->phpRenderer->addAttribute('csrf_valueKey', $valueKey);
        $this->phpRenderer->addAttribute('csrf_value', $value);

        return $this->phpRenderer->render($response, $template, $data);
    }

I’ve also tried with a Middleware, but don’t matter if I place it on app, route, group, before, after any other, when I call addAttribute of PhpRenderer taking data from session, in the response are all empty fields.

Moreover, with the Flash messages is a mess, because I already have the messages shows up sometimes in two following request and sometimes never.

I’m really struggling trying to put all together to have a basic app and I hope I don’t need to take username from session on every single request and pass to the view (or should I?). The same for Flash messages… I need to put the code for taking them from session and add as data to the view
on every Action?

The withTemplate and all other Responder methods are generic. This means only the data for the template should be generated and then getting passed by invoking this method. In order to pass “global” data to the template renderer, you could/should do that within a middleware instead. This way the withTemplate method can be left untouched and used for another context where all this data and logic is not needed.

Hi @odan thanks for answering.
Where I can find a working example?

I’ve tried using your PhpViewExtensionMiddleware, and it become like this:

<?php

namespace App\Middleware;

use Odan\Session\SessionInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\App;
use Slim\Csrf\Guard;
use Slim\Views\PhpRenderer;

final class PhpViewExtensionMiddleware implements MiddlewareInterface
{
    /**
     * @var App
     */
    private $app;

    /**
     * @var PhpRenderer
     */
    private $phpRenderer;
    private $session;
    private $csrf;

    public function __construct(App $app, PhpRenderer $phpRenderer, SessionInterface $session, Guard $csrf)
    {
        $this->app         = $app;
        $this->phpRenderer = $phpRenderer;
        $this->session     = $session;
        $this->csrf        = $csrf;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // Add URI and Route parser to view layer
        $this->phpRenderer->addAttribute('uri', $request->getUri());
        $this->phpRenderer->addAttribute('basePath', $this->app->getBasePath());
        $this->phpRenderer->addAttribute('route', $this->app->getRouteCollector()->getRouteParser());

        // Add some global attributes from session to view
        $this->phpRenderer->addAttribute('session', $this->session);
        $this->csrf->appendNewTokenToRequest($request);
        $this->phpRenderer->addAttribute('csrf', $this->csrf);

        $response = $handler->handle($request);

        return $response;
    }
}

It works, but is this the right way to go?

I’ve also read some Action-Domain-Responder articles and from here I understand that I can put some “view” logic inside the Responder for data required for widgets and common view part.

Like in my example, I have a menu item with the name of the logged in user (it can be considered a widget). This information is in every page of the application (except for login), so if I understand correctly, I can put this piece of code in a Responder, right? Or I’m wrong?

This looks much better now. I just would not pass the session object into the template. If possible, better pass only data into the templates to have a better separation and to avoid unwanted side effects.

Yes, Paul M. Jones says that you should create a custom Responder for each Action. Feel free to do it that way. The existing Responder is “just” a generic Responder that can be used to generate a JSON response, to render a template or to create a HTTP redirect etc. Feel free to add your action specific Responder with the view logic (e.g. Transformation) for each API endpoint (Action) if you need it.

For example, add a PageResponder that renders the needed “templates” or widgets etc and use that in your Action to render the response. The PageResponder itself could also use the generic Responder to perform the actual template rendering. But the view logic should then be placed in your PageResponder.

1 Like

Thanks, yes the session and csrf object straight on template was just for testing, now I’m cleaning up all.

The Action-Domain-Responder architecture is relatively new for me and some concepts are somehow “extreme” (no if/else, try/catch and loop in the Action for example); even moving away from the CRUD url-to-controller mapping is something I still fight against.

Anyway thanks for the clarifications.

1 Like