Zend Framework Example Shop Application

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

sike wrote:yeah i see what you mean. the thing is that i don't distinguish too much between views and templates. on most pages i use a component based approach - each component inherits from template (i do this to avoid juggling template vars around) and is plugged into a page object (which i see as the "view"). works out ok this far. one thing that made me think in the past was the blurry line between view and controller when working with components.
I like to keep some separation. I find a Response object fills in where there is not enough complexity to need a View.

I might do something like this, and then have a replaceable default render for the response that builds the standard page wraparound HTML and fills in you headline, etc. In one way it is just a formalism, but the Response also does redirects and gathers up headers, styles, scripts, etc.

Code: Select all

/**
    * @return   void
    */     
    protected function buildContent()
    {
      $data = $this->getData();
      $response= $this->getResponse();
      
      $Headline = new WebsiteHeadlinePartial($this->getApplication());
      $Headline['Headline'] = $data->get('Headline');
      $Headline['SubHeadline'] = $data->get('SubHeadline');
      $response->addElement($Headline);

      $Images = new WebsiteDistributorLevel1IPartial($this->getApplication());
      $Images['Images'] = $data->getImages();
      $response->addElement($Images);     
      
      $Teasers = new WebsiteTeaserModulesPartial($this->getApplication());
      $Teasers['Teasers'] = $data->getTeasers();
      $response->addElement($Teasers);   
    }
sike wrote:what's your differentiation between a view and a template?
I think of them on a continum of sorts. I think of a Template as doing a transform based on some algorithm. A View adds presentation logic. But that means some things that are called "template systems" like Smarty are actually creating View in my book.
(#10850)
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

having screened the view helper code from zf::view i came to the conclusion that it is a bit bloated and thus extracted a stripped down version into its own class to make it reusable :

Code: Select all

<?php
/**
 * @category   Suv
 * @package    Template
 */

	/**
	 * @category   Suv
	 * @package    Template
	 */
	class Suv_Template_Helpers
	{
	    
#---Properties
	    
	    /**
	     * @var Suv_Template_Interface
	     */
	    protected $template;	        
	    /**
	     * Instances of helper objects.
	     *
	     * @var array
	     */
	    protected $pathes = array();	        
	    /**
	     * Instances of helper objects.
	     *
	     * @var array
	     */
	    protected $instances = array();
	
	    
#---Con/Destructor	

	    /**
	     * @param	Suv_Template_Interface $template
	     * @param 	string $path	
	     */
	    public function __construct(Suv_Template_Interface $template, $path = null)
	    {
	        $this->template = $template;
	        $this->setPath($path);
	    }
	

#---Protected/Private    

	    /**
	     * @param  string $name
	     * @return string
	     * @throws Suv_Exception if unable to locate class
	     */
	    protected function locateClass($name)
	    {
	        $baseClass = ucfirst($name);
	        $baseFilename = $baseClass . '.php';
	        
	        foreach($this->pathes as $info)
	        {
	            $class = $info['prefix'] . $baseClass;
	            $filename = $info['path'] . '/' . $baseFilename;
	            if (class_exists($class, false)) 
	            {
	                return $class;
	            } elseif (Zend_Loader::isReadable($filename)) 
	            {
	                include_once($filename);
	                if (class_exists($class, false)) 
	                {
	                    return $class;
	                }
	            }            
	        }
	        
	        throw new Suv_Exception("Helper '$name' not found in path");
	    }
	        
	    
	    /**
	     * @param  string $name 
	     * @return object
	     */
	    protected function getHelper($name)
	    {
	        if (!isset($this->instances[$name])) 
	        {
	            $class = $this->locateClass($name);
	            $this->instances[$name] = new $class();
	            $this->instances[$name]->view = $this->template;       
	        }
	        return $this->instances[$name];
	    }    
	    

#---Public  	

	    /**
	     * Adds to the stack of helper paths in LIFO order.
	     *
	     * @param 	string $path
	     * @param 	string $classPrefix Class prefix to use with classes in this
	     * directory; defaults to Zend_View_Helper
	     * @return 	void
	     */
	    public function addPath($path, $classPrefix = 'Zend_View_Helper_')
	    {
	        if ( !empty($classPrefix) && 
	             ('_' != substr($classPrefix, -1)) ) 
	        {
	            $classPrefix .= '_';
	        }
	
	        array_unshift($this->pathes,
	                      array('prefix' => $classPrefix,
	                            'path' 	 => $path));
	    }
	    
	    
	    /**
	     * Replaces the stack of helper paths.
	     *
	     * @param 	string $path
	     * @param 	string $classPrefix Class prefix to use with classes in this
	     * directory; defaults to Zend_View_Helper
	     * @return 	void
	     */
	    public function setPath($path, $classPrefix = 'Zend_View_Helper_')
	    {
	        $this->pathes = array();
	        if (strlen($path) > 1)
	        {
	            $this->addPath($path, $classPrefix);
	        }
	    } 

	    
		/**
	     * Execute a helper
	     * 
	     * @param 	string $name
	     * @param 	array $args
	     * @return 	string
	     */
	    public function execute($name, $args)
	    {
	        $helper = $this->getHelper($name);
	        return call_user_func_array(array($helper, $name),
	                                    $args);
	    }	    
	}
it was then a easy step to incorporate the view helpers into the template class. basic template usage goes like this :

Code: Select all

$tpl = new Suv_Template_Composite('maintemplate.phtml');
    $tpl->getHelpers()->addPath('Zend/View/Helper');
    $tpl->set('headline', 'MainHeadline');
    
    $contentTpl = new Suv_Template_Composite('subtemplate.phtml');
    $contentTpl->set('headline', 'SubHeadline');
    $contentTpl->set('copy', 'Lorem Ipsum');
    $tpl->append($contentTpl, 'content');
        
    var_dump($tpl);
    var_dump($tpl->render());
and the accompanying templates :

maintemplate.phtml

Code: Select all

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Template!</title>
</head>
<body>
	<h1><?php echo $this->headline ?></h1>
	<?php echo $this->render('content') ?>
</body>
</html>
subtemplate.phtml

Code: Select all

<h1><?php echo $this->headline ?></h1>
    <p><?php echo $this->copy ?></p>
    <?php echo $this->htmlList(array('one', 'two')) ?>
    <hr />
now i am working on how to integrate the templates into the view.

chris
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

arborint wrote:
sike wrote:yeah i see what you mean. the thing is that i don't distinguish too much between views and templates. on most pages i use a component based approach - each component inherits from template (i do this to avoid juggling template vars around) and is plugged into a page object (which i see as the "view"). works out ok this far. one thing that made me think in the past was the blurry line between view and controller when working with components.
I like to keep some separation. I find a Response object fills in where there is not enough complexity to need a View.

I might do something like this, and then have a replaceable default render for the response that builds the standard page wraparound HTML and fills in you headline, etc. In one way it is just a formalism, but the Response also does redirects and gathers up headers, styles, scripts, etc.

Code: Select all

/**
    * @return   void
    */     
    protected function buildContent()
    {
      $data = $this->getData();
      $response= $this->getResponse();
      
      $Headline = new WebsiteHeadlinePartial($this->getApplication());
      $Headline['Headline'] = $data->get('Headline');
      $Headline['SubHeadline'] = $data->get('SubHeadline');
      $response->addElement($Headline);

      $Images = new WebsiteDistributorLevel1IPartial($this->getApplication());
      $Images['Images'] = $data->getImages();
      $response->addElement($Images);     
      
      $Teasers = new WebsiteTeaserModulesPartial($this->getApplication());
      $Teasers['Teasers'] = $data->getTeasers();
      $response->addElement($Teasers);   
    }
i use the response object only for the outer / main template. my example came frome a project of mine and was a "subview". so yeah, adding to the response is the right way if it's the outer view / template.

regarding the response object : i see its responsibilities only in gathering the html (without knowing anything about it) and handling headers (which includes redirecting and http status codes). anything beyond this is business of the template system (or view, or page object).

arborint wrote:
sike wrote:what's your differentiation between a view and a template?
I think of them on a continum of sorts. I think of a Template as doing a transform based on some algorithm. A View adds presentation logic. But that means some things that are called "template systems" like Smarty are actually creating View in my book.
so most template systems are views. the question is how much presentation logic do you allow in your templates? for me its formatting (which is a qestionable decision), looping and basic if {} else {} structures.


chris
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I see where you are going with you Suv_Template_Helpers. My concern about this Solar/Zend design is that it adds yet-another-loader to the codebase. I really perfer a Service Locator which combines the funcitonality of a Registry and a class loader. Because it is injected, it put the necessary functionality where it is needed. But at the same time it centralizes that functionality into a single place in the codebase.

It is a funny thing about good instincts and bad designs. If you look at what the original Zend class was trying to achieve -- the intentions were pretty good -- the design was terrible. Unfortunately they were not wise enough to go straight to Dependency Injection or Service Locator, which are state-of-the-art. Instead they went intially for more statics and now are trying to do DI willy-nilly in reaction to complaints.
sike wrote:so most template systems are views. the question is how much presentation logic do you allow in your templates? for me its formatting (which is a qestionable decision), looping and basic if {} else {} structures.
To clarify, "The View" (meaning the V in MVC) can be a Template or just a string, or a class that contains custom presentation logic, or a combination of those things. I call "A View Class/Object" a custom class with presentation logic specific -- it is usually a framework formalism. I don't think there are any rules on how much presentation logic that a Template can contain. Usually the template is a separate language from the programming language being used. PHP can obviously be used for both, which is one of the reasons why maintaining a formal separations like MVC or between View and Template is important.
(#10850)
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

arborint wrote:I see where you are going with you Suv_Template_Helpers. My concern about this Solar/Zend design is that it adds yet-another-loader to the codebase. I really perfer a Service Locator which combines the funcitonality of a Registry and a class loader. Because it is injected, it put the necessary functionality where it is needed. But at the same time it centralizes that functionality into a single place in the codebase.

It is a funny thing about good instincts and bad designs. If you look at what the original Zend class was trying to achieve -- the intentions were pretty good -- the design was terrible. Unfortunately they were not wise enough to go straight to Dependency Injection or Service Locator, which are state-of-the-art. Instead they went intially for more statics and now are trying to do DI willy-nilly in reaction to complaints.
the class posted above does two things : it keeps compatibility with the current helper design used by zend and it abstracts the process of loading the helpers. it is in its essence a registry with a loader functionality. maybe you could add registering instantiated classes but i would do this the first time i need it - so my question is what exactly do you dislike?
arborint wrote:
sike wrote:so most template systems are views. the question is how much presentation logic do you allow in your templates? for me its formatting (which is a qestionable decision), looping and basic if {} else {} structures.
To clarify, "The View" (meaning the V in MVC) can be a Template or just a string, or a class that contains custom presentation logic, or a combination of those things. I call "A View Class/Object" a custom class with presentation logic specific -- it is usually a framework formalism. I don't think there are any rules on how much presentation logic that a Template can contain. Usually the template is a separate language from the programming language being used. PHP can obviously be used for both, which is one of the reasons why maintaining a formal separations like MVC or between View and Template is important.
agreed.

chris
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

sike wrote:the class posted above does two things : it keeps compatibility with the current helper design used by zend and it abstracts the process of loading the helpers. it is in its essence a registry with a loader functionality. maybe you could add registering instantiated classes but i would do this the first time i need it - so my question is what exactly do you dislike?
It is not that I dislike it specifically. That is an interesting solution.

The essence of my dislike for compatibility with their design is that I think they have made a fundamental design error. They have taken the path of adding loaders (and now you have implemented a mini-registry) here-and-there around the code. I believe the a better path would have been to add loader capability by extending the Registry into a Service Locater. Then inject that functionality into the Models, Views, and Controllers so that the everything shares a common interface for the the most essential functionality for OOP PHP -- loading class files, instantiating objects, and injecting objects. Once you standardize that then you build a base for replaceablity and interoperatablity.
(#10850)
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

arborint wrote:
sike wrote:the class posted above does two things : it keeps compatibility with the current helper design used by zend and it abstracts the process of loading the helpers. it is in its essence a registry with a loader functionality. maybe you could add registering instantiated classes but i would do this the first time i need it - so my question is what exactly do you dislike?
It is not that I dislike it specifically. That is an interesting solution.

The essence of my dislike for compatibility with their design is that I think they have made a fundamental design error. They have taken the path of adding loaders (and now you have implemented a mini-registry) here-and-there around the code. I believe the a better path would have been to add loader capability by extending the Registry into a Service Locater. Then inject that functionality into the Models, Views, and Controllers so that the everything shares a common interface for the the most essential functionality for OOP PHP -- loading class files, instantiating objects, and injecting objects. Once you standardize that then you build a base for replaceablity and interoperatablity.
right. there are lots of places i dislike in zend but i think if you want to use it you have to accept certain compromise(s?). it is not that i like that too much but i want to avoid a complete rewrite :)
just out of curiosity : could you show a basic interface ?

chris
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

sike wrote:just out of curiosity : could you show a basic interface ?
I am assuming that you are talking about a combined Loader/Registry and not the proxy renderer tree. Here is the basic interface to the Loader and Registry:

Code: Select all

// Registry
    public static function get($index)
    public static function set($index, $value)

// Loader
    public static function loadClass($class, $dirs = null)
Pretty exciting stuff! And merged it would be even more exciting!

Code: Select all

// Registry + Loader
    public static function get($index, $class = null, $dirs = null)
    public static function set($index, $value)
I guess you could throw the loadClass() method back in or just make get() with null for an index would as loadClass does(). Inject this object and you would in a single stroke provide consistent class loading and registration in all MVC components.
(#10850)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

I'd just like to pop in and say nice work! It took me a few hours last night and about 40 minutes or so just now to finally catch up on this thread, but the discussion is excellent. :D
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

If you have also followed the discussion on the Zend framework mailing lists, you would know about their recent attachment to Two Step View as a design direction. I am a little bewildered by the stuff they are doing, but I guess if they keep learning patterns the will eventually get to the right ones. :( I am not sure they are actually doing Two Step View anyway, because Two Step really renders to a what I might call a View Model in the first step and then renders to HTML in the second step. I don't think that's what they are doing or what people commonly want to do (although I think Two Step View is appropriate for some things).

The addition of viewRenderer has been a debacle in my eyes as well. It followed on their adding a render function to the Action Controller. I guess the theory is that if you just slap render functions everywhere maybe you'll get it right. I got out once I realized that Zend seem to be intent on solving the failings of their View class by adding functionality to the Action Controller?!? I know Maugrim has written about viewRenderer() in his blog. He talks about all the problems and give some possible solutions. I am surprised that by the end he doesn't think the thing is a total mess.

There is also currently a Zend_Layout effort that is trying to do an end run around the mess they have made with all the helper bandages. Ultimately that class is a special purpose page builder rather than a generic system. I have concerns about any class that needs a function named setUserContentResponseSegmentName(). If you know ZF fairly well you can see where that is going. ;)

Above in this thread I proposed a Hierarchical Template View with proxied renderers as a more flexible solution to escaping the old sequential page building paradigm. My goal is to create a system when you can associate either a string, a Template or a View with any tag in any template and the whole thing just renders up into a complete page.
(#10850)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Zend is quickly losing it's favor in my books with the recent decisions they have made.. I feel your pain. I rather enjoyed the loose coupling of the components, but this business about the view rendering in the action controller is a serious turn off. No point on pretty much repeating exactly what you've just said arborint and Maugrim have talked about, just want to let you know I am enthusiastic with the direction you guys have proposed about dealing with views. Hopefully I can participate if I can find some time in between work :cry:
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

I've allowed my practical side to become dominant. After some exchanges between myself and Matthew on the mailing list, past blog comments, and other forums, I'm convinced that ViewRenderer is about as good as it gets. I'm actually quite surprised it made it into the source code just in the nick of time for 1.0.0 RC1. "As good as it gets" <> "cool" by the way but it's easier to live with than the alternative (which still lurks as Zend_Controller_Action::render()/initView() etc).

The Two Step View is being taken as interpretive - it's not quite the right name for what's being talked about but it's close enough to use as a common language term. I guess it makes sense now everyone has attached a non-Pattern meaning to it. Even I used it in a blog entry since the mailing list was using it so much. Maybe I shouldn't have encouraged that though ;).
arborint wrote:I got out once I realized that Zend seem to be intent on solving the failings of their View class by adding functionality to the Action Controller?!?
You probably saw my lengthy reply to the Zend_Layout proposal (which is gathering momentum offline from what I can follow from several sources including the new #zftalk on Freenode ;) which is logged daily somewhere online). In the next few weeks I'm hoping to throw together a document on modifying Zend_View to support composite views, layouts, components, and a few other things. Dump it in a proposal and at least try to move something forward. I really want to knock out Zend_Layout - it's useless for real composite views.

To be honest I expect it to get either sidetracked or outwitted by a controller focused alternative. But at least I'll have more code of my own to work with.
I'd just like to pop in and say nice work! It took me a few hours last night and about 40 minutes or so just now to finally catch up on this thread, but the discussion is excellent.
I'm way behind too. I promised arborint a review of his code and never got around to it ;). Will do soon - free time is just really tight at the moment.
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

i am just sticking with my own Zend_View subclass where i removed the whole viewRenderer and initView/render business and replaced it with a simple composite template system. works nicely so far.

chris
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Maugrim_The_Reaper wrote:I've allowed my practical side to become dominant. After some exchanges between myself and Matthew on the mailing list, past blog comments, and other forums, I'm convinced that ViewRenderer is about as good as it gets. I'm actually quite surprised it made it into the source code just in the nick of time for 1.0.0 RC1. "As good as it gets" <> "cool" by the way but it's easier to live with than the alternative (which still lurks as Zend_Controller_Action::render()/initView() etc).
That's what being practical gets you! ;) I stopped giving input a while back out of frustration. They have gone through too many people and the quality of the architecture has suffered. I think Mike Naberezny was the last guy who had the depth of knowledge to make the ZF really excellent -- but that was a while back.
Maugrim_The_Reaper wrote:To be honest I expect it to get either sidetracked or outwitted by a controller focused alternative. But at least I'll have more code of my own to work with.
:(
Maugrim_The_Reaper wrote:I'm way behind too. I promised arborint a review of his code and never got around to it ;). Will do soon - free time is just really tight at the moment.
There is actually very little to review and I feel bad having dragged this out so long. I mainly wanted to if people liked the direction and then planned to build in functionality for everyone's use cases. The essence of what I am proposing is:

1. Every View has an assignable renderer (as does the Response),
2. Every View acts as a View Model that can attach either a string, Template or View to any "name".
3. The Response has the core functionality of a View and is the top level View
4. The Response assembles everything at the end by rendering the tree -- lazy rendering.

The use cases are just something like:

Code: Select all

// set render for view
$view->setRenderer($MyTemplate);
// or
$view->setRenderer($MySubView);

// then attach element to be displayed by name
$view->set('title', 'The Title');
$view->set('content', $SomeOtherTemplate);
$view->set('login', $LoginBlockView);

// attach this view for the body of the response
$response>set('body', $view);

// and finally in index.php render it all
echo $response->render();
Obviously there needs be automation so if you just want to output a PHP template it can be as simple as viewRenderer(). I didn't add any automation in my example -- everything there is done explicitly. But this system gives you the freedom to build as complex a display tree as you want. And it is easy to integrate other template systems -- just a simple wrapper with render() and set().

The other thing I was experimenting with in that code was making the Views and Actions be able to know what directory they are in without being told. I think that could really simplify all the path setting and searching monkey business that is currently gong on in the ZF. That is a separate topic though (but related).
(#10850)
Post Reply