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");