Layout 嵌套布局/视图,将内容变量保留在Zend Framework 2中

Layout 嵌套布局/视图,将内容变量保留在Zend Framework 2中,layout,view,zend-framework2,nested,Layout,View,Zend Framework2,Nested,我正在尝试使用以下代码嵌套两个(或更多)视图。我正在努力找到一种方法,在不丢失最终视图内容的情况下成功嵌套这些视图,并在最后一个布局中通过$this->content变量传递它,因为它只返回一个空字符串 core/Framework/Mvc/Controller/BaseActionController.php 这是一个简单的基本控制器,它使用$frame和$layout变量(因此它们可以在扩展此类的任何控制器中使用)。其思想是,框架定义为以开头的页面,布局是使用在框架中显示的HTML。 模块/

我正在尝试使用以下代码嵌套两个(或更多)视图。我正在努力找到一种方法,在不丢失最终视图内容的情况下成功嵌套这些视图,并在最后一个布局中通过
$this->content
变量传递它,因为它只返回一个空字符串

core/Framework/Mvc/Controller/BaseActionController.php 这是一个简单的基本控制器,它使用$frame和$layout变量(因此它们可以在扩展此类的任何控制器中使用)。其思想是,框架定义为以
开头的页面,布局是使用
在框架中显示的HTML。 模块/应用程序/视图/布局/框架.phtml 此模板中的
部分应与自己的
一起呼出layout.phtml模板

模块/用户/视图/用户/测试/索引.phtml

<h1 class="page__title">Test</h1>
<p class="page__content">The final view</p>
core/Framework/Mvc/View/Http/TemplateInjector.php

<?php

namespace Application;

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\Session\Container;
use Framework\Mvc\View\Http\TemplateInjector;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $app = $e->getApplication();

        $request = $app->getRequest();
        $response = $app->getResponse();

        $eventManager = $app->getEventManager();
        $serviceManager = $app->getServiceManager();

        $session = new Container('locale');
        if (!$session->offsetExists('locale')) {
            $session->offsetSet('locale', \Locale::acceptFromHttp($request->getServer('HTTP_ACCEPT_LANGUAGE')));
        }

        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);

        $serviceManager->get('translator')
            ->setLocale($session->locale)
            ->setFallbackLocale('en_GB');

        $eventManager->getSharedManager()
            ->attach(
                'Zend\Stdlib\DispatchableInterface',
                MvcEvent::EVENT_DISPATCH,
                new TemplateInjector(),
                -80
            );

    }

    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }
}
<?php

namespace Framework\Mvc\View\Http;

use Zend\Mvc\MvcEvent;
use Zend\View\Model\ModelInterface as ViewModel;

class TemplateInjector
{
    public function __invoke(MvcEvent $event)
    {
        $model = $event->getResult();

        if (!$model instanceof ViewModel) {
            return;
        }

        if ($model->getTemplate()) {
            return ;
        }

        $controller = $event->getTarget();

        if (!is_object($controller)) {
            return;
        }

        // @todo: Clear this mess up

        $namespace = explode('\\', ltrim(get_class($controller), '\\'));

        $controllerClass = array_pop($namespace);

        array_pop($namespace);

        $moduleName = implode('/', $namespace);

        $controller = substr($controllerClass, 0, strlen($controllerClass) - strlen('Controller'));
        $action = $event->getRouteMatch()->getParam('action');

        $model->setTemplate(strtolower($moduleName.'/'.$controller.'/'.$action.'.phtml'));
    }
}

我认为对您来说,最好是用您自己的模板覆盖默认模板注入器。看看这篇文章。它很好地解释了如何创建和设置自己的模板注入器

基本上,您需要创建一个事件侦听器,并将其附加到由当前控制器触发的事件MvcEvent::event_DISPATCH。在事件侦听器中,您可以将确定请求模板路径的逻辑放入其中。在您的情况下,您可以通过调用$model->getChildrenByCaptureTo('capture')来获取子视图模型;并根据需要为其设置模板名称

解析模板名称的默认逻辑可以在这里找到Zend\Mvc\View\Http\InjectTemplateListener::injectTemplate

更新: 经与@Titanium讨论,发现该溶液是正确的

我试图理解你们的问题,所以这里有另一个解决方案

将以前的模板注入器代码替换为以下代码:

class TemplateInjector
{
    public function __invoke(MvcEvent $e)
    {
        $model = $e->getResult();

        if (!$model instanceof ViewModel)
        {
            return;
        }

        $controller = $e->getTarget();

        if (!is_object($controller))
        {
            return ;
        }

        if (!$controller instanceof LayoutTemplateProviderInterface)
        {
            return ;
        }

        $frameTemplate = $controller->getFrameTemplate();

        if ($frameTemplate !== null)
        {
            $e->getViewModel()->setTemplate($controller->getFrameTemplate());
        }

        $layoutTemplate = $controller->getLayoutTemplate();

        if ($layoutTemplate !== null)
        {
            $model = $e->getResult();
            $layoutModel = new ViewModel();
            $layoutModel->setTemplate($controller->getLayoutTemplate());
            $layoutModel->addChild($model);
            $e->setResult($layoutModel);
        }
    }
} 
现在,您需要定义基本控制器类应实现的接口,以便告诉系统您要使用自定义模板:

interface LayoutTemplateProviderInterface
{
    public function getFrameTemplate();
    public function getLayoutTemplate();
} 
然后,在基本控制器中,应实现如下接口:

abstract class BaseController extends AbstractActionController implements  LayoutTemplateProviderInterface
{
    private $frameTemplate = 'layout/layout';
    private $layoutTemplate = 'layout/admin';

    public function getFrameTemplate()
    {
        return $this->frameTemplate;
    }

    public function getLayoutTemplate()
    {
        return $this->layoutTemplate;
    }

    protected function setFrameTemplate($name)
    {
        $this->frameTemplate = $name;
    }

    protected function setLayoutTemplate($name)
    {
        $this->layoutTemplate = $name;
    }
}
最后一件事是更改执行模板注入器的优先级

$eventManager->getSharedManager()
            ->attach(
                'Zend\Stdlib\DispatchableInterface',
                MvcEvent::EVENT_DISPATCH,
                new TemplateInjector(),
                -91
            );
因此,我们的模板注入器将在默认注入器之后执行,这允许我们避免解析模板名称并依赖默认逻辑

在所有这些之后,您的操作如下所示:

public function testAction()
{
    return new ViewModel();
}
正如您所看到的,您不必在这里创建嵌套视图,它将由TemplateInjector自动完成

如果需要更改动作中的框架模板名称或布局模板,可以这样做:

$this->setFrameTemplate("new/template");
$this->setLayoutTemplate("new/template");

如果此解决方案解决了您的问题,请告诉我,这样我就可以删除第一个解决方案,以使此帖子更清晰。

谢谢您的回答,我尝试了几种不同的方法。我添加了TemplateInjector类,但似乎在TemplateInjector点上没有
$this->layout
变量。我想这是因为在调度之前没有设置布局。不完全确定何时执行哪些部分。我将用我的代码更新我的问题,以便您可以查看。我不确定如何使用
$model->getChildrenByCaptureTo('capture')
正如您所建议的,我对此进行了尝试,但我认为在视图中调用
时,“捕获”将是“内容”。我们的想法是在彼此之间建立一个嵌套的子级,其中
$this->content
将是每个子级的视图,最后一个是控制器中声明的视图,这是正常的。请查看我的上次更新,这是您想要的吗?这允许模板按预期呈现,使用
content
变量,框架和管理布局都可以工作。但是,它不允许有太多的灵活性来更改这些布局。理想情况下,根据我在问题中的代码,更改布局会很好(在本例中,
$this->layout
$this->frame
在控制器本身内。代码示例显示了如何在BaseActionController.php中实现此操作-此示例如何实现?似乎很难传递这些变量(在postDispatch方法中)在模板中,我在我的回答帖子中添加了另一种方法。
abstract class BaseController extends AbstractActionController implements  LayoutTemplateProviderInterface
{
    private $frameTemplate = 'layout/layout';
    private $layoutTemplate = 'layout/admin';

    public function getFrameTemplate()
    {
        return $this->frameTemplate;
    }

    public function getLayoutTemplate()
    {
        return $this->layoutTemplate;
    }

    protected function setFrameTemplate($name)
    {
        $this->frameTemplate = $name;
    }

    protected function setLayoutTemplate($name)
    {
        $this->layoutTemplate = $name;
    }
}
$eventManager->getSharedManager()
            ->attach(
                'Zend\Stdlib\DispatchableInterface',
                MvcEvent::EVENT_DISPATCH,
                new TemplateInjector(),
                -91
            );
public function testAction()
{
    return new ViewModel();
}
$this->setFrameTemplate("new/template");
$this->setLayoutTemplate("new/template");