嵌套布局/视图将内容变量保留在Zend Framework 2中 [英] Nesting layouts/views keeping the content variable in Zend Framework 2
问题描述
我正在尝试使用以下代码嵌套两个(或更多)视图.我正在努力寻找一种成功嵌套这些视图而又不会丢失最终视图内容并将其通过最后一个布局中的$this->content
变量传递的方法,因为它只会返回一个空字符串.
I am trying to nest two (or more) views using the following code. I am struggling to find a way to successfully nest these views without losing the final view content and passing it through the $this->content
variable within the last layout, as it just returns an empty string.
核心/框架/Mvc/Controller/BaseActionController.php
这是一个简单的基本控制器,它使用$ frame和$ layout变量(以便可以在扩展此类的任何控制器中使用).这个想法是将框架定义为以<!DOCTYPE html>
开头的页面,而布局是使用<?= $this->content; ?>
在框架中显示的HTML.
core/Framework/Mvc/Controller/BaseActionController.php
This is a simple base controller which uses the $frame and $layout variables (so that they can be used within any controller extending this class). The idea is the frame is defined as the page starting with <!DOCTYPE html>
and the layout is the HTML which gets displayed in the frame using <?= $this->content; ?>
.
namespace Framework\Mvc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class BaseActionController extends AbstractActionController
{
protected $frame;
protected $layout;
protected $layouts = array();
public function preDispatch() {...}
public function dispatch() {..}
public function postDispatch()
{
if ($this->frame !== null) {
$this->layouts[] = $this->frame;
}
if ($this->layout !== null) {
$this->layouts[] = $this->layout;
}
foreach ($this->layouts as $layout) {
$view = new ViewModel();
$layoutView = new ViewModel();
$layoutView->setTemplate($layout);
$layoutView->addChild($view);
}
}
}
模块/应用程序/视图/布局/frame.phtml
此模板中的<?= $this->content; ?>
部分应回显layout.phtml模板以及它自己的<?= $this->content; ?>
.
module/Application/view/layout/frame.phtml
The <?= $this->content; ?>
part within this template should echo out the layout.phtml template along with it's own <?= $this->content; ?>
.
<?= $this->doctype(); ?>
<html>
<head>
<meta charset="utf-8">
<title>Woohoo, I'm a frame</title>
</head>
<body>
<?= $this->content; ?>
</body>
</html>
模块/应用程序/视图/布局/admin/layout.phtml
$this->content
变量应回显module/Users/view/users/test/index.phtml
文件的内容.此时,该变量返回一个空字符串.
module/Application/view/layout/admin/layout.phtml
The $this->content
variable should echo out the contents of the module/Users/view/users/test/index.phtml
file. At this point, the variable returns an empty string.
<header>
<img class="logo" src="<?= $this->basePath() ?>/img/logo.png" alt="Company">
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</header>
<section>
<?= $this->content; ?>
</section>
<footer>
<ul>
<li><a href="#">Copyright</a></li>
<li><a href="#">Sitemap</a></li>
<li><a href="#">Privacy policy</a></li>
</ul>
</footer>
模块/用户/视图/用户/测试/index.phtml
<h1 class="page__title">Test</h1>
<p class="page__content">The final view</p>
临时解决方案(在每次操作中都不太好写)
<?php
namespace Users\Controller;
use Framework\Mvc\Controller\BaseActionController;
use Zend\View\Model\ViewModel;
class TestController extends BaseActionController
{
public function indexAction()
{
$view = new ViewModel();
$view->setTemplate('users/test/index.phtml');
$adminView = new ViewModel();
// This layout is defined in the Application module.config.php file
$adminView->setTemplate('layout/admin');
$adminView->addChild($view);
return $adminView;
}
}
如上所述,我的临时解决方案是手动选择ViewModel()
实例所需的模板.我注意到$view->setTemplate();
可以工作,但是没有定义一个,$view->getTemplate();
返回一个空字符串.我不确定在Zend Framework 2中的默认模板定义位置,以便可以在基本控制器中复制该模板.
As above, my temporary solution is to choose the template the ViewModel()
instance needs, manually. I notice $view->setTemplate();
works but without defining one, $view->getTemplate();
returns an empty string. I am not sure where, in Zend Framework 2, the default template is being defined so I can replicate this within the base controller.
我认为我有(暂时)可以解决的方法,唯一的问题是手册$view->setTemplate('/path/to/my/template.phtml');
.如果我可以复制Zend的操作方式,那么它应该可以正常工作,但是将$this->content
变量传递到layout.phtml
文件中时却无所适从,其内容为最终视图.
I think the solution I have (temporarily) could work, the only issue being the manual $view->setTemplate('/path/to/my/template.phtml');
. If I can replicate how Zend does this, then it should work correctly but I am at a loss passing the $this->content
variable into the layout.phtml
file with the contents being the final view.
根据Next Developer的建议,我添加了以下内容:
module/Application/Module.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__,
),
),
);
}
}
核心/框架/Mvc/View/Http/TemplateInjector.php
<?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'));
}
}
TemplateInjector中的任何更改似乎都不会更改视图,但是这似乎为时已晚.但是,它确实在视图上设置了模板.在创建$view = new VidewModel();
的新实例时,它使用TemplateInjector类中定义的模板,该模板应该允许我自动进行布局过程,但是所有设置的范围似乎为时已晚.我知道我可以在TemplateInjector中访问控制器,视图和模型,但是无论我如何更改视图或添加子代,它都不会出现在前端.如果有人可以提供可行的示例,那将真的很有帮助.
Any changes in the TemplateInjector doesn't seem to change the view, by this time it seems too late. It does however set the template on the view. When making a new instance of $view = new VidewModel();
it uses the template defined in the TemplateInjector class which should allow me to automate the layout process, but the scope of everything being set, it seems too late. I know I can access the controller, the view and the model in the TemplateInjector but no matter how I change the views or add children, it doesn't come out on the front end. If anyone could provide a working example, that would be really helpful.
推荐答案
我认为,在您的情况下,最好的办法是使用您自己的默认模板注入器.看看这篇文章
I think the best would be in your case is to override the default template injector with your own. Take a look at this post http://blog.igorvorobiov.com/2014/10/18/creating-a-custom-template-injector-to-deal-with-sub-namespaces-in-zend-framework-2/. It explains pretty much well how to create and setup your own template injector.
基本上,您需要创建一个事件侦听器并将其附加到由当前控制器触发的事件MvcEvent :: EVENT_DISPATCH.在事件侦听器内部,您可以放置确定所需模板路径的逻辑.在您的情况下,可以通过调用$ model-> getChildrenByCaptureTo('capture');获得子视图模型.并根据需要为其设置模板名称.
Basically, you need to create an event listener and attach it to the event MvcEvent::EVENT_DISPATCH triggered by the current controller. Inside the event listener you can put the logic which determines a path to the requested template. In your case, you can get your child view model by calling $model->getChildrenByCaptureTo('capture'); and set the template name to it as you want.
解析模板名称的默认逻辑可以在这里找到Zend \ Mvc \ View \ Http \ InjectTemplateListener :: injectTemplate
The default logic which resolves template names can be found here Zend\Mvc\View\Http\InjectTemplateListener::injectTemplate
在与@Titanium讨论时,发现此解决方案是正确的.
Upon discussion with @Titanium, this solution was found to be the correct one.
我试图了解您的问题,所以这是另一种解决方案.
I have tried to understand you problem, so here's another solution to it.
用以下代码替换以前的模板注入器代码:
Replace the previous template injector code with this one:
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);
}
}
}
现在,您需要定义基本控制器类应实现的接口,以告知系统您要使用自定义模板:
Now, you need to define interface which your base controller class should implement in order to tell the system that you want to use custom templates:
interface LayoutTemplateProviderInterface
{
public function getFrameTemplate();
public function getLayoutTemplate();
}
然后在基本控制器中,您应该像这样实现接口:
Then in your base controller you should implement the interface like so:
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;
}
}
最后一件事是更改执行模板注入器的优先级.
The last thing is to change the priority at which our template injector is getting executed.
$eventManager->getSharedManager()
->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
new TemplateInjector(),
-91
);
因此,我们的模板注入器将在默认注入器之后立即执行,这使我们避免了解析模板名称并依赖默认逻辑.
So, our template injector will be executed right after the default one, this allows us to avoid resolving the template name and rely on the default logic.
完成所有操作后,您的操作将如下所示:
After all this, your action looks like this:
public function testAction()
{
return new ViewModel();
}
如您所见,您不必在此处创建嵌套视图,它会由TemplateInjector自动完成.
As you can see you don't have to create nesting views here, it will be done automatically by TemplateInjector.
如果您需要在操作中更改框架模板名称或布局模板,可以这样进行:
If you need to change frame template name or layout template within an action you can do it like so:
$this->setFrameTemplate("new/template");
$this->setLayoutTemplate("new/template");
让我知道该解决方案是否可以解决您的问题,因此我可以删除第一个解决方案,以使本帖子更加清晰.
Let me know if this solution solves your problem so I can remove the first one to make this post clearer.
这篇关于嵌套布局/视图将内容变量保留在Zend Framework 2中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!