ZF2 - Rendering variables from Module.php to twig

2019-09-08 06:17发布

问题:

I'm in the midst of converting a ZF2 site from using default templates into twig versions, and so far, for the main part, everything is going well, except I have some variables in Module.php defined from a bootstrap method that I'm now unable to get into the layout.twig template.

The original code that worked fine for the default templates is as:

public function bootstrapLayout(MvcEvent $e)
{
    $config = $e->getApplication()->getServiceManager()->get('applicationconfig');
    $authService = $e->getApplication()->getServiceManager()->get('AuthService');

    $version = new \stdClass();
    $version->major = $config['version']['major'];
    $version->minor = $config['version']['minor'];

    $dt = new \DateTime();

    $thisYear = $dt->format('Y');

    $dt->setTimestamp($config['version']['build_date']);
    $version->buildDate = $dt->format('d/m/Y H:i');

    $user = new \stdClass();
    $user->hasIdentity = $authService->hasIdentity();
    $user->username = $authService->getStorage()->read('username');

    $e->getViewModel()->setVariables(array(
        'version' => $version,
        'thisYear' => $thisYear,
        'user' => $user,
    ));
}

However, neither 'version', 'thisYear' nor 'user' make itself known to the twig layout template. I've searched on here and the closest resolution I came to finding was to obtain the ZfcTwigRenderer by:

$zfcTwigRenderer = $e->getApplication()->getServiceManager()->get('ZfcTwigRenderer');

but now I'm at a loss how I can utilise this and pass my defined vars through it to the layout.

Here's the content of the layout file:

{{ docType() }}
<html lang="en" class="no-js">
    <head>
        <meta charset="utf-8">
        <title>{% block title %}{% endblock title %} - Website</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        {% block meta %}{% endblock meta %}

        <!-- Le styles -->
        <link href="/css/bootstrap.min.css" media="screen" rel="stylesheet" type="text/css">
        <link href="/css/bootstrap-hidden-inline.css" media="screen" rel="stylesheet" type="text/css">
        <link href="/css/theme.min.css" media="screen" rel="stylesheet" type="text/css">
        <link href="/css/bootstrap-datetimepicker.min.css" media="screen" rel="stylesheet" type="text/css">
        <link href="/css/style.css" media="screen" rel="stylesheet" type="text/css">
        <link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
        {% block style %}{% endblock style %}

        <!-- Scripts -->
        <script type="text/javascript" src="/js/jquery.min.js"></script>
        <script type="text/javascript" src="/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="/js/tooltip.min.js"></script>
        <script type="text/javascript" src="/js/bootstrap-datetimepicker.min.js"></script>
        <!--[if lt IE 9]><script type="text/javascript" src="/js/html5.js"></script><![endif]-->
        <script type="text/javascript" src="/js/modernizr.min.js"></script>
        <script type="text/javascript" src="/js/overlays.min.js"></script>
        <script type="text/javascript" src="/js/common.min.js"></script>
        {% block script %}{% endblock script %}
    </head>
    <body>
        <div class="navbar navbar-inverse navbar-static-top">
            <div class="container">
                <div class="navbar-header">
                    {% if user.hasIdentity %}
                        <button class="navbar-toggle" data-target=".navbar-collapse" data-toggle="collapse" type="button">
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                    {% endif %}
                    <a class="navbar-brand" href="{{ url('home') }}">Website</a>
                </div>
                <div class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <!--<li><a class="icon-home" href="{{ url('home') }}"><span></span>Home</a></li>-->
                        {% if user.hasIdentity %}
                            <li class="hidden-sm hidden-xs"><a class="icon-import" href="{{ url('import') }}"><span></span>Import</a></li>
                            <li class="hidden-sm hidden-xs"><a class="icon-export" href="{{ url('export') }}"><span></span>Export</a></li>
                            <li><a class="icon-about" href="{{ url('about') }}"><span></span>About</a></li>
                            <!-- <li><a class="icon-reports" href="#"><span></span>Reports</a></li> -->
                            <li><a class="icon-logout" href="{{ url('logout') }}"><span></span>Logout</a></li>
                        {% endif %}
                    </ul>
                    <ul class="nav navbar-nav navbar-right">
                        {% if user.hasIdentity %}
                            <!--<li><a class="icon-report-bug" href="{{ url('report-bug') }}"><span></span>Report bug</a></li>-->
                        {% endif %}
                    </ul>
                </div>
            </div>
        </div>
        {% if user.hasIdentity %}
            <div class="container hidden-sm hidden-xs">
                <div class="welcome-message"><span></span>Welcome {{ user.username }}</div>
            </div>
        {% endif %}
        <div class="container">
            {% block content %}{{ content|raw }}{% endblock content %}
            <hr>
            <footer>
                Copyright &copy; {{ thisYear }} Company name<br>
                Version {{ version.major }}.{{ version.minor }} &mdash;
                Build {{ version.buildDate }}
            </footer>
        </div> <!-- /container -->
        {% block inline %}{% endblock inline %}
    </body>
</html>

Any help on this would be greatly appreciated as I've really come up against a dead end, despite trying all sorts of things for the past couple of hours.

回答1:

For true global variables that are available inside all layouts you need to do the following inside a onBootstrap function in Module.php so you example would be

public function bootstrapLayout(MvcEvent $e)
{
    $config = $e->getApplication()->getServiceManager()->get('applicationconfig');
    $authService = $e->getApplication()->getServiceManager()->get('AuthService');

    $version = new \stdClass();
    $version->major = $config['version']['major'];
    $version->minor = $config['version']['minor'];

    $dt = new \DateTime();

    $thisYear = $dt->format('Y');

    $dt->setTimestamp($config['version']['build_date']);
    $version->buildDate = $dt->format('d/m/Y H:i');

    $user = new \stdClass();
    $user->hasIdentity = $authService->hasIdentity();
    $user->username = $authService->getStorage()->read('username');

    $enviroment = $e->getApplication()->getServiceManager()->get('ZfcTwigEnvironment');
    $enviroment->addGlobal('version', $version);
    $enviroment->addGlobal('thisYear', $thisYear);
    $enviroment->addGlobal('user', $user);
}

This would then allow you to use your above template like you have set out.

Hope this helps



回答2:

You could use

current ($e->getViewModel ()->getChildren ())->setVariables(array(
    'version' => $version,
    'thisYear' => $thisYear,
    'user' => $user,
));

instead of

$e->getViewModel()->setVariables(array(
    'version' => $version,
    'thisYear' => $thisYear,
    'user' => $user,
));    


回答3:

Ahah! After a bit more digging around on the net for ZF2 concepts in general, I've managed to come up with a solution. Maybe, maybe not, the best, but it's working (at least for now)... the solution was a custom view helper.

In Module.php, I added a getViewHelperConfig() method that I plan to call via globalVars():

public function getViewHelperConfig()
{
    return array(
        'factories' => array(
            'globalVars' => function($helperPluginManager) {
                    $serviceLocator = $helperPluginManager->getServiceLocator();
                    $viewHelper = new View\Helper\GlobalVars();
                    $viewHelper->setServiceLocator($serviceLocator);
                    return $viewHelper;
                }
        ),
    );
}

Then I created the view helper in Application\src\Application\View\Helper\GlobalVars.php:

<?php
namespace Application\View\Helper;

use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceManager;

class GlobalVars extends AbstractHelper
{
    protected $serviceLocator;

    public function __invoke()
    {
        $config = $this->serviceLocator->get('applicationconfig');
        $authService = $this->serviceLocator->get('AuthService');

        $dateTime = new \DateTime();
        $date = new \stdClass();
        $date->thisYear = $dateTime->format('Y');

        $dateTime->setTimestamp($config['version']['build_date']);

        $version = new \stdClass();
        $version->major = $config['version']['major'];
        $version->minor = $config['version']['minor'];
        $version->buildDate = $dateTime->format('d/m/Y H:i');

        $user = new \stdClass();
        $user->hasIdentity = $authService->hasIdentity();
        $user->username = $authService->getStorage()->read('username');

        return array(
            'version' => $version,
            'date' => $date,
            'user' => $user,
        );
    }

    public function setServiceLocator(ServiceManager $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }
}

In layout.twig, I now call this via {{ globalVars().version.major }} for example, and to check whether the user is logged in or not, {% if globalVars().user.hasIdentity %}.

If someone has a better / more elegant method, then I'd most certainly be interested in seeing it, but if not, or if this is the "best" approach, then hopefully it'll be useful for others too.