-- 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/