处理每个页面视图所需的应用程序范围数据的正确方法


Correct way to deal with application-wide data needed on every pageview

我目前正在开发一个用PHP编写的更大的网络应用程序,该应用程序基于MVC框架,在架构方面与Zend框架有很多相似之处。

当用户登录后,我有一个地方应该显示当前用户虚拟积分的余额。这个显示需要在每个控制器的每个页面上。

用于获取侧边模型数据的代码放在哪里?这些数据不是特定于控制器的,但需要在每个页面视图的站点范围内进行布局,与当前控制器无关MVC或ZF负责人将如何做到这一点?你们其他人呢?

我曾想过在用户登录时加载余额并将其存储在会话中,但由于余额经常被更改,这似乎不正确——每次加载页面时都需要对其进行检查和更新。我也想过在每个控制器中添加获取例程,但这似乎也不对,因为这会导致代码重复。

好吧,你是对的,在每个控制器上都有例程将是一种代码重复,并且不会使代码可重用。

与你的问题评论中建议的不同,我不会选择基本控制器,因为基本控制器不是一个好的做法(在大多数情况下),Zend Framework实现了Action Helpers以避免它们。

如果您的部分视图是站点范围的,为什么不编写自己的自定义视图帮助程序并从视图帮助程序中获取模型中的数据呢?然后,您可以直接从布局中调用此视图辅助对象。在我看来,通过视图中的模型获取数据根本不会破坏MVC设计模式,只要你不更新/编辑这些数据。

您可以在/view/helpers/或您的库中添加您的视图助手(然后您也必须注册您的视图帮助程序路径):

class Zend_View_Helper_Balance extends Zend_View_Helper_Abstract
{
    public function balance()
    {
        $html = '';
        if (Zend_Auth::getInstance()->hasIdentity()) {
            // pull data from your model
            $html .= ...;
        }
        return $html;
    }
}

请注意,如果需要以特定方式格式化代码,视图助手还可以调用部分视图(render()partial()partialLoop())。

这是一个非常简单的例子,但对我来说,你的情况就足够了。如果您想对这些数据有更多的控制权,并能够根据特定的视图(或控制器)对其进行修改(或不进行修改),那么我建议您查看占位符。Zend在在线文档中有一个非常好的例子。

有关自定义视图帮助程序的详细信息,请单击此处。

当您执行这样的任务时,也可以考虑使用Zend_Cache组件,这样您就不必在每次请求后查询数据库,而是每隔一分钟(根据您的需要)查询一次。

您要查找的是Zend_Registry。当您认为需要某种形式的全局变量时,应该使用此组件。如果你在每个页面上都需要它,那么你最好把它添加到你的引导程序中,如果你只在某些地方需要它,把它添加在relavent控制器的init方法中。

application/Bootstrap.php

public _initUserBalance() 
{
    $userId = Zend_Auth::getInstance()->getIdentity()->userId;
    $user = UserService::getUser($userId);
    Zend_Registry::set('balance', $user->getBalance());
}

应用程序/布局/default.phtml

echo 'Balance = ' . Zend_Registry::get('balance');

这个小片段应该会给你正确的想法!

在这种情况下,我通常使用带有dispatchLoopShutdown()挂钩的前控制器插件,该插件执行所需的数据访问并将数据添加到视图/布局中。然后布局脚本会渲染该数据。

可根据要求提供更多详细信息。

[更新]

假设您想在布局中显示来自数据库(或web服务或RSS提要)的最后X个新闻项目,与请求的控制器无关

你的前控制器插件在application/plugins/SidebarNews.php:中可能看起来像这样

class My_Plugin_SidebarNews
{
    public function dispatchLoopShutdown()
    {
        $front = Zend_Controller_Front::getInstance();
        $view = $front->getParam('bootstrap')->getResource('view');
        $view->sidebarNews = $this->getNewsItems();
    }
    protected function getNewsItems()
    {
        // Access your datasource (db, web service, RSS feed, etc)
        // and return an iterable collection of news items
    }
}

确保您在前端控制器中注册插件,通常在application/configs/application.ini:中

resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"

然后在布局中,像往常一样进行渲染,可能是在application/layouts/scripts/layout.phtml:中

<?php if (isset($this->sidebarNews) && is_array($this->sidebarNews) && count($this->sidebarNews) > 0): ?>
<div id="sidebarNews">
<?php foreach ($this->sidebarNews as $newsItem): ?>
<div class="sidebarNewsItem">
   <h3><?= $this->escape($newsItem['headline']) ?></h3>
   <p><?= $this->escape($newsItem['blurb']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>

明白我的意思了吗?