Playing with the idea of a Composite View, this assumes we have multiple objects of the same interface (typical composite) sharing a render() method - this throws back to arborint's suggestion:
arborint wrote:I agree. Sub-Views should not require a Controller if they are attached to a View. I think that anything with a render() method should be able to be attached to a named part of a View and just work -- that should be our goal.
So rendering any specific class of type (e.g. Zps_View_Interface) will render itself, and obviously call render() on all it's children. Typical Composite pattern really. This removes an arbitrary hierarchy (e.g. header, content, footer) and delegates them to generic classes of implementing the interface, either as composites (seems likely for some) or leafs. Of course each composite is itself a Layout into which its children are rendered.
The question here is what does the actual rendering, and how does it integrate Zend_View? There are two options - firstly and likely the most common, we use multiple Zend_View instances per leaf/composite. A leaf could just be the basic Zps_View subclass (which enforces the refactoring to deny the current Zend_Controller_Action/Zend_View coupling) with the composite being an additional subclass: Zps_View_Composite which is basically the additional code to handle a collection of leafs or other composites.
Code: Select all
interface Zps_View_Interface
{
public function render();
}
class Zps_View extends Zend_View implements Zps_View_Interface
{
public function __construct(){}
public function render(){}
}
interface Zps_View_Composite_Interface
{
public function attach($tag, Zps_View_Interface $view);
public function detach($tag);
public function childExists($tag);
public function hasChildren();
public function getChildren();
}
class Zps_View_Composite extends Zps_View implements Zps_View_Interface, Zps_View_Composite_Interface
{
private $nodes = array();
private $tag = '';
public function __construct(){}
public function render(){}
public function attach($tag, Zps_View_Interface $view){}
public function detach($tag){}
public function isChild($tag){}
public function hasChildren(){}
public function getChildren(){}
public function getTag(){}
}
From there it's a matter of meshing the views. Having each Zps_View render it's assigned template according to whatever model it receives/requests, aggregating those renderings by tag within the parent composite (which becomes a partial model for rendering composite templates for layouts), and so on up the Composite tree until the root parent is rendered and passed into the Response class.
That's a very brief description, so sorry if a few things seem vague - I felt it was better to leave it open ended for discussion before explaining every minute detail. This only addresses the OO part - the templates themselves need to refer to the tags in order for the each composite to correctly put each child's render output in its correct place.
I also haven't addressed getting the model - esp. where it comes from. If there is a re-useable widget which displays a list of News Headlines from the database, then the current controller won't be handling that data - it needs to be drawn into the View (and passed down the chain perhaps?) from another point in the application. Which leads us into whether View Helpers (not necessarily the ZF definition, but the actual pattern) should be brought to bear to allow the View read in a model without relying totally on the controllers.
In other words, Views may have a executable section to make View Helper calls which grab a model (pull, not the ZF's default push system) for the sub-View to use in rendering it's assigned template.
Another thought is how templates are assigned - generic classes given a template name at instantiation, or a single object->template mapping like a traditional Strategy?
hh
P.S. I said I think better in the morning...
More questions:
- When are Views pulled into the composite structure? It can be done manually - think PHP DOM approach, or through an automated means based on templates though not sure exactly how. Doing it within templates seems too much, but then one alternative (configuration) is as bad...
- How should the View and Model interface? Assuming we avoid controllers, this would indicate the View needs to have read access to the Model (not controller pass through). Important to note the distinction between ZF view helpers and the View Helper Pattern. I'm starting to wish the ZF paid more attention to how it named those helper classes...
- Is it viable to use what amounts to multiple Zend_View objects as the basis of leafs and composites? Is there an alternate strategy relying on namespaces in a single object worth persuing? More a thought for now - if using multiple Zend_Views is easier then that's how it should be done (refactor later if it proves an optimisation stick up).
- Should Views carry a reference to the overall parent view, since that's where controllers end up assigning the current Model? I'm thinking they should have access to at least a copy or read-only version to keep all Views isolated (one not being able to influence another by altering the Model).
- Does any of this paticularly long post make any sense?
