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

Go To StackoverFlow.com

1

I am currently involved in the development of a larger webapplication written in PHP and based upon a MVC-framework sharing a wide range of similarities with the Zend Framework in terms of architecture.

When the user has logged in I have a place that is supposed to display the balance of the current users virtual points. This display needs to be on every page across every single controller.

Where do you put code for fetching sidewide modeldata, that isn't controller specific but needs to go in the sitewide layout on every pageview, independently of the current controller? How would the MVC or ZF-heads do this? And how about the rest of you?

I thought about loading the balance when the user logs in and storing it in the session, but as the balance is frequently altered this doesn't seem right - it needs to be checked and updated pretty much on every page load. I also thought about doing it by adding the fetching routine to every controller, but that didn't seem right either as it would result in code-duplication.

2012-04-05 17:10
by oens
If all controllers will use it maybe you can make a base controller and inherit from that one.. - gosukiwi 2012-04-05 17:17
@gosukiwi In terms of maintainability I don't know if extending the current controller implementation with a fetch method is the way to go. In such cases ActionHelpers are normally used in the ZF-context, as Liyali also states, but I don't know if those are right for the job? I think the use of ViewHelpers as described in the chosen answer better fits my use-case, where nothing but a simple fetch from the model-layer is needed - oens 2012-04-06 09:24


3

Well, you're right, having routines to every controller would be a code-duplication and wouldn't make your code reusable.

Unlike suggested in your question comments, I wouldn't go for a a base controller, since base controllers aren't a good practice (in most cases) and Zend Framework implements Action Helpers in order to to avoid them.

If your partial view is site-wide, why don't you just write your own custom View Helper and fetch the data in your model from your view helper? Then you could call this view helper directly from your layout. In my opinion, fetching data through a model from the view doesn't break the MVC design pattern at all, as long as you don't update/edit these data.

You can add your view helpers in /view/helpers/ or in your library (then you would have to register your view helper path too):

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;
    }
}

Note that you view helper could also call a partial view (render(), partial(), partialLoop()) if you need to format your code in a specific way.

This is a pretty simple example, but to me it's enough is your case. If you want to have more control on these data and be able to modify it (or not) depending on a particular view (or controller), then I recommend you to take a look on Placeholders. Zend has a really good example about them here on the online documentation.

More information about custom view helpers here.

When you perform such a task, consider using the Zend_Cache component too, so you won't have to query the database after each request but let's say, every minute (depending on your needs).

2012-04-05 19:27
by Liyali
Very much what I wanted to know. I will look into Placeholders - I didn't know much about those. For the current application I will go with the ViewHelper approach, hadn't thought about using them this way. Fine point about ZendCache, we are currently working on integrating caching at the Model-layer, so this should mitigate the polling problem when done. Thank you - oens 2012-04-06 09:19
You're welcome ; - Liyali 2012-04-06 09:41


1

What you are looking for is Zend_Registry. This is the component you should use when you think you need some form of global variable. If you need this on EVERY page, then you are best adding it to your bootstrap, if you only need it in certain places add it in init method of relavent controllers.

application/Bootstrap.php

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

application/layouts/default.phtml

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

That wee snippet should give you the right idea!

2012-04-05 19:20
by Jamie Sutherland
In my understanding the bootstrap is merely for setting up the application and initializing ressources etc. Not for actually fetching data, I might be wrong? Secondly, is storing all kinds of arbitrary user data in the registry considered a good practice? I know I only asked about an integer balance in the original question, but if the need for sitewide data grew I think Zend_Registry would be hard to maintain? What has been fetched, where has it been fetched etc. are hard to answer when working with the registry as it is global to the entire application, or what - oens 2012-04-06 09:19
As far as I understand, the bootstrap is used to set the application into it's global state. If you know you're going to need that value on every request somewhere in the application I don't see the problem with it. However, after posting, I saw Liyalis post and upvoted it as that makes more sense for what you are wanting to do. I saw the application-wide part in the title and didn't think of view helpers - Jamie Sutherland 2012-04-06 09:49


1

In this case, I usually go with a front controller plugin with a dispatchLoopShutdown() hook that performs the required data access and adds the data to the view/layout. The layout script then renders that data.

More details available on request.

[UPDATE]

Suppose you wanted to display inside your layout the last X news items from your db (or web service or an RSS feed), independent of which controller was requested.

Your front-controller plugin could look something like this in 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
    }
}

Make sure you register your plugin with the front controller, typically in application/configs/application.ini:

resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"

Then in your layout, just render as usual, perhaps in 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; ?>

See what I mean?

2012-04-06 17:08
by David Weinraub
I am all ears, please elaborate if you feel like it : - oens 2012-04-08 15:30
Added more details about front controller plugin - David Weinraub 2012-04-09 03:23
Sorry for the late answer. This is a great approach as well, thanks for the details! Upvoted - oens 2012-04-22 10:08
Many thanks! ;- - David Weinraub 2012-04-22 12:58
Ads