Thanks for the replies all :).

Two-Step View is very useful, but the problem I have is that the View "depth" 
(for want of a better term) is variable. I could have any number of sections, 
each composed of other sections, which themselves are composed of...well, you 
get the picture. Rather than a single sitewide template to define layout, 
there's an arbitrary number of nested layouts - which is where the complexity 
of current approaches falls down.

The problem I have with the widget helper is that it pushes a whole dispatch 
cycle which is an awful lot of activity when all I might need is a quick Model 
fetch - trying 10 or more of those for building a dynamic page won't be pretty 
- and 10 isn't even a high number. What ever happened to Views pulling data 
from the Model? The View Helper pattern is just begging to be used here.

Not the same convention as the current type - the interface for these View 
Helpers would vary depending on the Model they abstract. Just let them grab the 
appropriate data from the Model directly and feed it back to Zend_View. No 
widget helper style dispatching except in very exceptional cases - it's not 
needed at all. One could term these mini-controllers - though personally I 
still put them in the View layer since all they do is allow read access to the 
Model.

The only problem then is nesting the View layer and enforcing layouts per 
sub-section. And that may not even require more than one instance of Zend_View 
and the application of a composite design. If organisation is problematic 
(Views from multiple Modules) it may need a little configuration to help build 
the composite tree of Views to render - not much more work than adding a layout 
templates to start with. I haven't implemented this in full of course - it's 
all grey matter material for now.

I may be crazy, of course ;).

Regards,
Pádraic
 
Pádraic Brady
http://blog.astrumfutura.com
http://www.patternsforphp.com


----- Original Message ----
From: Matthew Weier O'Phinney <[EMAIL PROTECTED]>
To: fw-general@lists.zend.com
Sent: Thursday, April 19, 2007 6:30:27 PM
Subject: Re: [fw-general] Implementing complex Views with ZF

-- Pádraic Brady <[EMAIL PROTECTED]> wrote
(on Thursday, 19 April 2007, 09:40 AM -0700):
> I've been having one of those long discussions about implementing the Zend
> Framework in a sample application (sort of an exercise a few of us are doing 
> to
> improve our own practices and knowledge of working with the ZF ;)).
> 
> When we came to rendering a web page, we assumed there would be lots of
> reuseable widgets and elements included - not all that far fetched really. The
> problem is that Zend_View isn't all that cooked up for the task. I tried
> finding some references to a current practice but all I seem to find is
> references to using a controller's _forward() method to switch in and out of
> other controllers (which grab the Model and View for reusable elements of a
> webpage).
> 
> I'm not sure I follow the logic of this - it seems to be something that 
> becomes
> prohibitively more complex the more reusable elements are added - not to
> mention controller to controller knowledge is basically coupling and seems 
> more
> a case of an afterthought system than something deliberate - especially unless
> it follow some configurable pattern to dynamically build layouts.
> 
> Has anyone found, or can recommend, a simple easy to implement practice? As it
> stands our discussion has leaned towards introducing Layouts and subclassing
> Zend_View to introduce the Composite View pattern (and possibly View Helpers 
> to
> allow Views access the Model read-only style). I can't find anything at 
> present
> which offers a more elegant solution for introducing reusable elements into an
> overall View.

To implement Two Step View, I typically use a dispatchLoopShutdown()
plugin, something like this:

    class Wopnet_Plugins_SiteTemplate extends Zend_Controller_Plugin_Abstract
    {
        /**
        * View script path
        * @var string
        */
        public $directory;

        /**
        * View script for sitewide template
        * @var string
        */
        public $file;

        /**
        * View object
        * @var Zend_View_Interface
        */
        public $view;

        /**
        * Constructor
        *
        * Get the script path to the sitewide view scripts, as well as the site 
        * template name. If not passed in the constructor, grab from the 
sitewide
        * configuration, suing the key $config->vars->path->app to determine the
        * script path for site templates, and $config->templates->site->filename
        * to determine the sitewide view script.
        * 
        * @param  string $file 
        * @param  string $directory 
        * @return void
        */
        public function __construct($file = null, $directory = null)
        {
            if (Zend_Registry::isRegistered('view')) {
                $this->view = Zend_Registry::get('view');
            } else {
                $this->view = new Zend_View();
            }

            $config = Zend_Registry::get('siteConfig');

            if ((null !== $directory) && is_dir($directory)) 
            {
                $this->directory = $directory;
            } else {
                if (!isset($config->vars->path->app)) {
                    throw new Exception('No sitewide app path set');
                }
                $this->directory = $config->vars->path->app . 
DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'scripts';
            }

            if (!is_dir($this->directory)) {
                throw new Exception('Sitewide view script path does not exist');
            }

            if (null !== $file) {
                $this->file = (string) $file;
            } else {
                if (!isset($config->templates->site->filename)) {
                    throw new Exception('Sitewide template not set in config');
                }
                $this->file = $config->templates->site->filename;
            }

            if (!file_exists($this->directory . DIRECTORY_SEPARATOR . 
$this->file)) {
                throw new Exception("Sitewide view script 
'{$this->directory}/{$this->file}' does not exist");
            }

            $this->view->addScriptPath($this->directory);
        }

        /**
        * Inject generated content into sitewide view script
        *
        * @param Zend_Controller_Request_Abstract $request
        * @return void
        */
        public function dispatchLoopShutdown()
        {
            $front    = Zend_Controller_Front::getInstance();
            $response = $front->getResponse();
            $body     = $response->getBody();

            $this->view->setScriptPath($this->directory);
            $this->view->assign(get_object_vars($response));
            $this->view->content = $body;
            $response->setBody($this->view->render($this->file));
        }
    }

Now, that's all fine and dandy, but it doesn't get the widgets in like
you're discussing. For that, I've seen a few ideas. One is a Zend View
Helper somebody posted to JIRA (and I think there's also a similar
proposal in the wiki). In those, the helper basically does something
like this:

    public function widget($action, $controller, $module, array $params = 
array())
    {
        $front   = Zend_Controller_Front::getInstance();
        $request = clone $front->getRequest();
        $request->setActionName($action)
                ->setControllerName($controller)
                ->setModuleName($module)
                ->setParams($params);
        $response = new Zend_Controller_Response_Http();
        $front->getDispatcher()->dispatch($request, $response);
        return $response->getBody();
    }

(this is rough; I haven't tested this at all). Then, you would call this
in your views to grab the necessary components of your page:

    <?= $this->widget('items', 'list', 'blog', array('limit' => 10)) ?>

There's a little overhead, but not much. The con side to this is that
your widgets don't get to tie into the dispatch loop, so if you were
doing any ACL checks in a preDispatch() plugin, for instance, you'd lose
those checks.

Theoretically, you could call $front->dispatch($request, $response), but
this would overwrite the current request/response objects, and could
lead to some unintended behaviour.

-- 
Matthew Weier O'Phinney
PHP Developer            | [EMAIL PROTECTED]
Zend - The PHP Company   | http://www.zend.com/






__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 

Reply via email to