Zend Framework Example Shop Application
Moderator: General Moderators
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Here's the other reason why a factory can help:
Configuring all Views would have to replicate settings down the line for each. Having this centralised in a specialised class would at least boost reusability on my track. Agreed, I am on a specific track so seeing your own point of view in code would offer a better contrast.
Just to round off my fourth (who's counting
) amendment:
Some of the cruft could be removed if we just took out the inherited paths and used a configuration file to allow more flexible path definitions per Module which also extends to any "new" (non-child) views. I'll leave it here until arborint has a chance to drop some code. At least this way the creation logic is separated from the View class completely (on my rutted track at least) which affords more flexibility for this approach.
Code: Select all
public function initView()
{
require_once 'Zend/View/Interface.php';
if (isset($this->view) && ($this->view instanceof Zend_View_Interface)) {
return $this->view;
}
$request = $this->getRequest();
$module = $request->getModuleName();
$dirs = $this->getFrontController()->getControllerDirectory();
if (empty($module) || !isset($dirs[$module])) {
$module = 'default';
}
$baseDir = dirname($dirs[$module]) . DIRECTORY_SEPARATOR . 'views';
if (!file_exists($baseDir) || !is_dir($baseDir)) {
throw new Zend_Controller_Exception('Missing base view directory ("' . $baseDir . '")');
}
require_once 'Zps/View.php';
$this->view = new Zps_View(null, array('basePath' => $baseDir));
$this->view->setEncoding( $this->registry->get('Configuration')->output_encoding );
$this->view->setEscape('htmlentities');
$this->view->strictVars();
return $this->view;
}Just to round off my fourth (who's counting
Code: Select all
/**
* Includes a sub-View by reference to the template filename
* and the Module it's located in assuming the recommended
* basePath is adhered to as a convention.
*
* @todo address independent configuration of sub-Views
* @param string $file
* @param string $module
* @return string
*/
public function attach($file, $module = null)
{
$subViewFactory = new Tmp_View_Factory;
$subView = $subViewFactory->createInstance($this, $module);
return $subView->render($file);
}Code: Select all
class Tmp_View_Factory implements Tmp_View_Factory_Interface
{
public function __construct()
{
}
public function createInstance(Tmp_View $view, $module = null)
{
if (isset($module) && !ctype_alnum($module)) {
require_once 'Tmp/View/Exception.php';
throw new Tmp_View_Exception('Invalid module name; must only contain alphanumeric characters');
}
if (isset($view) && is_null($module)) {
$subView = clone $view;
} else {
$subView = new Tmp_View;
$basePath = $view->_baseModulePath . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'views';
$subView->setBasePath($basePath);
$subView->setMainView($view->getMainView());
$subView->setBaseModulePath($view->getBaseModulePath());
}
return $subView;
}
}- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
I see what you are doing ... but I just keep wondering why you need to keep finding out where these files are when they are all in the same place?! And nothing changes at run-time that you didn't know when you build the Action Controller and View. This is an area where I have just never had any problem/friction and along comes Paul M. Jones with his solution and it ends up in the ZF. I see that it is a solution ... but it's not to a problem I have and it seems to create new problems that I didn't have before.
As I said, I need to solve it "my way" and see if it works better (given different trade-offs).
I think one question I have is what are real-world use cases that people have. Here an example if general page building that I might do:
1. Outer "main" template containing the DTD, HTML <head> block with common styles and scripts. It often has the wrapper HTML that forms the visual border to the site and may include a main menu, global links and a footer. You should be able to attach additional styles, scripts and the HTML content.
2. A "layout" wrapper that might define the number of columns, sidebars and additional navigation.
3. Some "components" or sub-Views such as a sign-in or a shopping cart block that have separate, modularized code located in some common area. These should be able to be simply attached with a template tag and just work.
4. The actual "content" of the requested Action.
Of these, I consider #1 to be associated more with the HTTP Response object. #2 and #3 are more common things that probably reside outside of and one View's directory. #4 is the specific content of the requested View, though it may be in turn generated from sub-Views. How do other's structure their page building?
As I said, I need to solve it "my way" and see if it works better (given different trade-offs).
I think one question I have is what are real-world use cases that people have. Here an example if general page building that I might do:
1. Outer "main" template containing the DTD, HTML <head> block with common styles and scripts. It often has the wrapper HTML that forms the visual border to the site and may include a main menu, global links and a footer. You should be able to attach additional styles, scripts and the HTML content.
2. A "layout" wrapper that might define the number of columns, sidebars and additional navigation.
3. Some "components" or sub-Views such as a sign-in or a shopping cart block that have separate, modularized code located in some common area. These should be able to be simply attached with a template tag and just work.
4. The actual "content" of the requested Action.
Of these, I consider #1 to be associated more with the HTTP Response object. #2 and #3 are more common things that probably reside outside of and one View's directory. #4 is the specific content of the requested View, though it may be in turn generated from sub-Views. How do other's structure their page building?
(#10850)
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Modules are decoupled from other Modules - that's why the paths must all be configured for each class independently. The Front Controller needs the same thing even if we only have a single default Path defined in bootstrap.php.arborint wrote:I see what you are doing ... but I just keep wondering why you need to keep finding out where these files are when they are all in the same place?! And nothing changes at run-time that you didn't know when you build the Action Controller and View.
Code: Select all
/zfPersonal
/data
/cache
/logs
/config
/Zend
/extends
/default
/controllers
/views
/models
/Blog
/controllers
/views
/models
/Plugins
/controllers
/views
/models
/Service
/controllers
/views
/modelsIf I went further I'd likely end up adding parts to our config.ini to drive the Module paths we currently have in our bootstrap.php file. This would help avoid duplicating Module paths - currently we need them for both Views, sub-Views and Front Controller. This is really where the Factory would end up if I pushed it away from being sub-View specific to a more generic View factory. A generic factory would also extract the setup logic from our current Action Controller's initView() method removing the duplication altogether.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
My own is pretty similar - I just don't have a main view with content, usually it's just the layout up front including sub-sections. I wonder if that subtle difference explains why I don't get how a HTTP Response object handles View management at any level? As a response object it collates output, but I have it already organised and laid out by the time it reaches the object so it has no need for View management - it just gets plain HTML or whatever else I might send out along with specific headers when needed.I think one question I have is what are real-world use cases that people have. Here an example if general page building that I might do:
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Not exactly true. A Front Controller need to convert a route into a path. That process is external to the Actions. But each Action should know where it is because it does not need to find itself.Maugrim_The_Reaper wrote:Modules are decoupled from other Modules - that's why the paths must all be configured for each class independently. The Front Controller needs the same thing even if we only have a single default Path defined in bootstrap.php.
Why is it that doing a forward() is so simple and yet all of this pathfinding is so complex. I think if you want a View, then ask for "controller/view" or "module/controller/view" -- why is this different for Views.Maugrim_The_Reaper wrote:There are 4 modules: default, Blog, Plugins, Service. How does any one Module (its Views) reference any other View from another module (bear in mind the common aspect of same Module views is handled using "clone" easily enough). One of the doubts I have about paths in Views is that its duplicating information - the relative path. What if I reuse Blog and change it's directory location? Do I go edit all the Views for the new paths to other Modules? What if I want to put all Views in a central location (not even in a Module) alongside translated templates (fast solution for documented documentation possibly in XML+XSLT). What if I put it somewhere so two applications can share it. What if... It's the "if" part driving a Factory - extract the creational/configuration stuff from Tmp_View and let whoever comes along stamp their own preference on it, and just offer a conventional default implementation.
I really gets down to one of my problems with the Jones/Solar style Views. What is the difference between the Response and the View? They really have all the same stuff. The ZF confuses the issue even more with things like redirects -- is it control flow? or is it a response?Maugrim_The_Reaper wrote:My own is pretty similar - I just don't have a main view with content, usually it's just the layout up front including sub-sections. I wonder if that subtle difference explains why I don't get how a HTTP Response object handles View management at any level? As a response object it collates output, but I have it already organised and laid out by the time it reaches the object so it has no need for View management - it just gets plain HTML or whatever else I might send out along with specific headers when needed.I think one question I have is what are real-world use cases that people have. Here an example if general page building that I might do:
The same with Response and View classes. I don't see how they are that functionally different. If anything the Response is the Root of the View tree. It may have a few more functions and aggregation responsability to support that role as Root -- but the two certainly mostly do the same thing. The ZF View just makes you think they aren't and the Response was tacked on later, ignoring this issue.
(#10850)
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Because forward() is a method which hides all the complex stuff? As for why is it different from Views - it's not. The attach() and _forward() functions both take similar parameters holding a reference to the Module name, and controller/action (or view template) name. Both underneath are all about nesting objects of some type. Both still need to be located and configured as for a single instance. Both need to have relevant paths defined in advance. Where the FC creates Controllers (via Router/Dispatcher) which execute actions, Views create child Views which reference a template to render.Why is it that doing a forward() is so simple and yet all of this pathfinding is so complex. I think if you want a View, then ask for "controller/view" or "module/controller/view" -- why is this different for Views.
All _forward() does is flag a revised Request object as being not-dispatched which is later picked up by the Dispatcher once the current action returns (or just wraps up at the end of the method block) which throws the whole dispatch cycle (once again) at the revised Request. It's kind of odd, but works.
Why would an Action not know where it is? I don't follow where this point comes from. The whole path thing is when other Actions/Views need to push the workflow to other classes - which need to be created and told where to find yet other classes (helpers/filters) as well as what default options to use (escaping/encoding) - the whole "creation logic". That it uses the same information to locate scripts is just a consequence of convention to simplify how the render() parameter is given (name, not relative path). If you want to unsimplify that then you can modify how the script-path is set (or not) via render(). But that still leaves the View object's helper and filter paths, and default settings to be pushed into our new View. All of which are individually configurable and hence not predictable from any one parameter - unless we take a leap and pronounce a conventional filesystem layout as Gospel. If I pass a relative template path to render() will that tell the View object to go load Helper A from Path B using Class Prefix C and then apply Encoding D when escaping with htmlentities()? The template can directly reference some of this - but that's a little like deferring the inevitable and spreading it over a wider area in smaller chunks. This is what nested View objects need to figure out if they intend being consistent. Handling those "creation" tasks is why a factory appeared in my last code kick-around.Not exactly true. A Front Controller need to convert a route into a path. That process is external to the Actions. But each Action should know where it is because it does not need to find itself.
Thing is I know I'm arguing around the same point again - I just haven't seen out how you intend dealing with the same problems I see here. I "get" that you can reference templates by path to render() but that's just one configuration point among many. What about the others? How does each View in your system know to use UTF-8 encoding with htmlentities() without getting information from somewhere beyond or within render()?
You have me thereI really gets down to one of my problems with the Jones/Solar style Views. What is the difference between the Response and the View? They really have all the same stuff. The ZF confuses the issue even more with things like redirects -- is it control flow? or is it a response?
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
OK ... I see that I have ground this thing to a halt with my skepticism. And I am torn about that skepticism because I am not sure I can do anything about it -- or that I should. My feeling that the design of the ZF controllers and view is about half right, but about half wrong too. And I am not sure that I want to live with the half wrong part.
Maugrim, you have headed down the road of extending the current stuff to mold it into your own. That is certainly pragmatic, but it does force some design decisions and it also papers over all of the code you would choose not to use in the ZF classes.
The controllers we will probably come back to. The main problems I see with them are that they now do too much and that a lot of this tacked on functionality has subverted the design.
But it is the View that we are dealing with now. I get the same feelings of bloat and subverted design in them as well. In the current Zend_View class I might agree on about this much:
But that leave out 25-30 functions in Zend_View_Abstract that are largely Helper and Filter stuff, but are also things like a class loader and a PHP template system. I understand that some people might want to use Helpers and Filters that way, but I don't think most do and all are burden by the code that could have been moved to a Zend_View_Deluxe class. The Template should be separate simply for the reason of defining an interface.
Now on top of the minimal functionality in the class above, I think we agree that we need some composite/component functionality. I am not sure whether that should be done with an additional attach() function or just by adding to __set()? You show a different mix of functions in your Composite class, but it might be able to be done as is.
If we allow any object with a render() method to be __set() then we get a hierarchy/render tree. My goal from previous post is that any of these would just work:Those could also be "$this->title =" if you were assigning from within your view.
What has been left out so far is the template class. It would be good to separate out the PHP template class that is part of the Zend_View and define an interface so we can have generic support for any class that has a render() function. A facade for things like Smarty would work and it would simplify and level the playing field for Templates.
Maugrim, you have headed down the road of extending the current stuff to mold it into your own. That is certainly pragmatic, but it does force some design decisions and it also papers over all of the code you would choose not to use in the ZF classes.
The controllers we will probably come back to. The main problems I see with them are that they now do too much and that a lot of this tacked on functionality has subverted the design.
But it is the View that we are dealing with now. I get the same feelings of bloat and subverted design in them as well. In the current Zend_View class I might agree on about this much:
Code: Select all
class Zend_View
{
private $_escape = 'htmlspecialchars';
private $_encoding = 'ISO-8859-1';
private $_strictVars = false;
public function setEscape($spec)
public function escape($var)
public function setEncoding($encoding)
public function getEncoding()
public function strictVars($flag = true)
public function __get($key)
public function __isset($key)
public function __set($key, $val)
public function __unset($key)
public function clearVars()
public function render($name)
}Now on top of the minimal functionality in the class above, I think we agree that we need some composite/component functionality. I am not sure whether that should be done with an additional attach() function or just by adding to __set()? You show a different mix of functions in your Composite class, but it might be able to be done as is.
If we allow any object with a render() method to be __set() then we get a hierarchy/render tree. My goal from previous post is that any of these would just work:
Code: Select all
$view->title = 'Home Page';
$view->content = new MyTemplate("mytemplate.phtml");
$view->menu = new MyView(); // extends Zps_View();What has been left out so far is the template class. It would be good to separate out the PHP template class that is part of the Zend_View and define an interface so we can have generic support for any class that has a render() function. A facade for things like Smarty would work and it would simplify and level the playing field for Templates.
(#10850)
i really like the simplicity of that approach. i am doing things in a similar way and it worked out great this far.arborint wrote: If we allow any object with a render() method to be __set() then we get a hierarchy/render tree. My goal from previous post is that any of these would just work:Those could also be "$this->title =" if you were assigning from within your view.Code: Select all
$view->title = 'Home Page'; $view->content = new MyTemplate("mytemplate.phtml"); $view->menu = new MyView(); // extends Zps_View();
What has been left out so far is the template class. It would be good to separate out the PHP template class that is part of the Zend_View and define an interface so we can have generic support for any class that has a render() function. A facade for things like Smarty would work and it would simplify and level the playing field for Templates.
the rendering of vars assigned to the view should support 3 types of data :
1) a object implementing the renderer interface (render())
2) a object implementing __toString
3) any primitive
what i normally do is introducing a "page" container which deals with integrating page related things like css and javascript:
Code: Select all
public function setMetaTag($name, $value)
public function addMetaTag($name, $value, $glue = ', ')
public function addStyle($style)
public function addStyleSheet($path)
public function addScript($script)
public function addScriptLibrary($path)
public function addBootStrap($script) // window.onload = function () { .... }one suggestion for the composite stuff: it proved useful in some cases to know who your parents are to adjust output slightly.
cheers
Chris
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Hadn't thought of __toString. That is an excellent addition.sike wrote:i really like the simplicity of that approach. i am doing things in a similar way and it worked out great this far.
the rendering of vars assigned to the view should support 3 types of data :
1) a object implementing the renderer interface (render())
2) a object implementing __toString
3) any primitive
This is where I have been mentioning the blurred line between Views and the Response. I agree with you that Views need to be able to support adding/setting many/all of the <head>, headers, DTD information of building a HTTP response. Your interface is similar to what I use. I also add $name's to the scripts and styles so that multiple views can add the same thing, but only one of that name is used.sike wrote:what i normally do is introducing a "page" container which deals with integrating page related things like css and javascript:all templates use a page container as their root and every component now can add specific css or js artifacts to the output without messing things up (i really dislike putting script or css into the html body). if the site grows you could easily add automatic compression to css or js files (like lumping them together in one file and/or minifing them) without changing the other components / templates.Code: Select all
public function setMetaTag($name, $value) public function addMetaTag($name, $value, $glue = ', ') public function addStyle($style) public function addStyleSheet($path) public function addScript($script) public function addScriptLibrary($path) public function addBootStrap($script) // window.onload = function () { .... }
That is certainly possible and easy to add if we find we need it. It also leads to the question of whether Views can be named (optional?) so you can identify parents/children is in a simple way -- rather than inspection.sike wrote:one suggestion for the composite stuff: it proved useful in some cases to know who your parents are to adjust output slightly.
(#10850)
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Nail duly hit on the headarborint wrote:Maugrim, you have headed down the road of extending the current stuff to mold it into your own. That is certainly pragmatic, but it does force some design decisions and it also papers over all of the code you would choose not to use in the ZF classes.
Pragmatic to the core
While obvious bloat, Mr. Pragmatic notes it's easy to ignore the cruft in the template type you describe. Just don't use those methods - they all pull in the crufty class loaders with their fancy Reflection use via __call(). The rest are those Path methods you can equally ignore by including classes within the template PHP. My reason for not ripping them out would be that the average user will be looking for them. Compositing should be decoupled from the unnecessary interface - focusing on a handful of methods as defined by Zend_View_Interface. Even many of those just aren't needed assuming a single composited method - render().arborint wrote:But that leave out 25-30 functions in Zend_View_Abstract that are largely Helper and Filter stuff, but are also things like a class loader and a PHP template system. I understand that some people might want to use Helpers and Filters that way, but I don't think most do and all are burden by the code that could have been moved to a Zend_View_Deluxe class. The Template should be separate simply for the reason of defining an interface.
attach() is a standard composite method - in a View it should have a little more obvious name since in my quick implementation it's responsibility is to request a new View class, ask it to render a template, and return the output as a String. In a sense, I'm nesting the Leaf method totally into the Composite method so template traversal is simplified and obvious. So there are no separate Leaf/Composite objects - both are identical concrete classes extending Zend_View.arborint wrote:Now on top of the minimal functionality in the class above, I think we agree that we need some composite/component functionality. I am not sure whether that should be done with an additional attach() function or just by adding to __set()? You show a different mix of functions in your Composite class, but it might be able to be done as is.
I admit liking this method. If I wanted to add your own personality to this approach I assume an overridden __set() method to detect Zend_View type values in the View Model would allow for rendering of assigned Views as you note in your template example. I don't see any reason to exclude that method - so long as you're happy your created Views can all be configured whichever way you decide. That would allow both approaches flexibly.
never had the need for named css or js files because in my case their path make them unique - so i just run a quick array_unique and voila i am done (;arborint wrote:This is where I have been mentioning the blurred line between Views and the Response. I agree with you that Views need to be able to support adding/setting many/all of the <head>, headers, DTD information of building a HTTP response. Your interface is similar to what I use. I also add $name's to the scripts and styles so that multiple views can add the same thing, but only one of that name is used.
yeah, good idea.arborint wrote:That is certainly possible and easy to add if we find we need it. It also leads to the question of whether Views can be named (optional?) so you can identify parents/children is in a simple way -- rather than inspection.
cheers
Chris
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Maugrim, It sounds like you are headed in the Mr. Pragmatic direction whilst I am going in the Mr. Idealist direction. That's probably fine as we can see where each ends. You have produced a bunch of nice code and I have done nothing so Mr. Pragmatic is certainly winning.
Yeah, it does make sense to use the file names if you are having the code build the HTML for the link. We could actually just use the file path as the array key and you would not even have to the the array_unique check.sike wrote:never had the need for named css or js files because in my case their path make them unique - so i just run a quick array_unique and voila i am done (;
(#10850)
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
I'm still mulling this and trying out some ideas, but not much time right now. I have been experimenting with classes that know where they are. There has to be a better (faster) way to do this without reflection. Obviously __FILE__ does not work in an inherited class. Anyone have a trick for this (or the obvious that I have overlooked
).
On the same topic of paths, one question I have is why Zend chose to put the Action Controllers in a "controllers" directory instead of loose in the module directory? It would certainly make things simpler if everything the Action Controllers needed was in subdirectories below the controllers. The other tack for sorting out where things are is the dirname(__FILE__) method, but with the current scheme you would need to do some sort of dirname(__FILE__) . '/../views/' nonsense -- but it still makes more sense than getting bits and pieces from the Request.
Code: Select all
class Zps_Controller_Action extends Zend_Controller_Action
{
function getFilename() {
$class = new ReflectionClass(get_class($this));
return $class->getFilename();
}(#10850)
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Not a clue. It's a suggested convention - but it's changeable. You're supposed to list the controller paths for the Front Controller anyway in their world view so it's configurable. If it's easier just change the current layout. Not sure if that helps all that much though.On the same topic of paths, one question I have is why Zend chose to put the Action Controllers in a "controllers" directory instead of loose in the module directory? It would certainly make things simpler if everything the Action Controllers needed was in subdirectories below the controllers.