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