2009年12月4日星期五

Re: [fw-mvc] PageController + ActionStack vs. Simple MVC

-- Christian Ehmig <ehmig@boerse-go.de> wrote
(on Friday, 04 December 2009, 07:16 AM -0800):
> Yes, I understand how to construct a view helper for this case - but this
> simply does not solve the "usage issue". The view script you mentioned will
> be used by a specific action which passes the category 'zend_framework' to
> the view script. But this just renders one simple news box. Now I like to
> re-use the view script several times within a given page.

The example I showed was simply returning the articles -- but it could
very easily also perform the markup.

And don't forget: you can also re-use view scripts:

<?php echo $this->render('controller/action.phtml') ?>

I think you're getting stuck in your current implementation and not
really trying or experimenting with other solutions due to your
familiarity with ActionStack. As I mentioned, read Paddy's blog, read my
blog, read the ZF manual, google for examples -- but most importantly,
perform some experiments, and see what might work. ActionStack is an
expedient means to an end -- but one that many of us have realized is
both expensive and hard to maintain and test.

> Therefore I can either use:
>
> (1) a single action for that page that renders several of those view
> templates, each with different categories -> now, the logic "which articles
> to display in which order" remain in this action

No, not really. That's *display* logic, and should be in the _view_. And
just because an application view is triggered from a given action does
not mean you cannot inject information into that view from elsewhere,
nor does it mean it needs to be the only view you render. It's perfectly
acceptable to trigger other views from your controller plugins, action
helpers, etc.

> - to change the displayed
> article categories, the number or articles per rendered view template ... I
> need to adjust the controller (passing those parameters via REQUEST is
> obviously no choice). this action would be called something like
> "homepageAction", "page1Action", "page2Action" for each single page. This is
> not configurable.
>
> (2) one global "pageAction" which reads a $pageId parameter from REQUEST and
> the config for that page from db. Now it assembles the page based upon the
> configuration e.g. display the view script 3 times with the categories a, b
> and c. So this is exactly the case I mentioned - you would have a "single
> action" system where there is no need for any other action since the whole
> output is generated via one pageAction (any request can be handled by that
> action) - this would justify the use of view helpers but deny the usage of
> controllers and actions in general. To "break up" the code one would maybe
> _forward to other actions within the pageAction, but this is even worse to
> configure and maintain.
>
> To state it again, neither (1) or (2) are acceptable for me - are there any
> other solutions about "configuring" the page rendering process from "outside
> controllers and views"?

Listen, ZF is flexible enough to allow you to develop however you want.
You asked for some ideas, and several of us have presented our opinions;
you do not agree with them, obviously.

I'll state the why's behind why several of us recommend not using action
stack and instead building rich models and view helpers:

* First, and most importantly: they're testable, and controllers, while
we have the ControllerTestCase, are tricky to test. I can test my
models in isolation, and I can actually test my view helpers fairly
easily in isolation. This allows me to ensure consistency within the
most important aspects of my application.

* Proper separation of concerns. Put display logic in the views,
business logic in the models. Your controllers should only be acting
as traffic cops, determining what models and views to use, and
injecting the appropriate dependencies.

* ActionStack is expensive. Dispatching actions is expensive due to the
use of reflection and I/O stat calls in the dispatcher. Additionally,
every time you run through the dispatch loop, you're triggering
plugins -- and the more plugins you have, the slower the loop will
be.

You can configure your views and layouts -- including what will render,
and where -- in a variety of locations, including the view and layout
scripts themselves. There's no reason to get locked into thinking only
the controllers can determine what views to run.

> weierophinney wrote:
> >
> > -- Christian Ehmig <ehmig@boerse-go.de> wrote
> > (on Friday, 04 December 2009, 05:15 AM -0800):
> >> weierophinney wrote:
> >> > -- Christian Ehmig <ehmig@boerse-go.de> wrote
> >> > (on Friday, 04 December 2009, 02:39 AM -0800):
> >> >> I've read some discussions about the "evil" ActionStack in this forum
> >> and
> >> >> specifically here:
> >> >>
> >> http://www.rmauger.co.uk/2009/03/why-the-zend-framework-actionstack-is-evil/
> >> >>
> >> >> I commented on that (#19).
> >> >>
> >> >> Now my question is - how do you setup Zend MVC for complex, highly
> >> >> configurable websites or portals (not only simple blog applications
> >> with
> >> >> basic CRUD operations). In particular, websites that consist of many
> >> >> conrete
> >> >> pages that make use of (hopefully) reusable components.
> >> >>
> >> >> For me the best solution is still:
> >> >>
> >> >> 1. Writing a Zend_Controller_Plugin that hooks into routeShutdown()
> >> >> 2. in routeShutdown() compile the set of actions, their parameters,
> >> >> templates and layout targets from a given page configuration (xml
> >> file,
> >> >> database) and the layout file to be used for that page
> >> >> 3. Push these actions to the ActionStack
> >> >> 4. done
> >> >>
> >> >> Any other approaches seem not reusable and "hard-coded" for me. I can
> >> >> give
> >> >> further details on my implementation if needed. I am just curious how
> >> >> others
> >> >> solve the issue "a page consists of several actions and their
> >> respective
> >> >> views".
> >> >
> >> > I've spoken and blogged a ton on this in the last year. The answer:
> >> this
> >> > stuff is typically either part of your domain model or part of your
> >> view
> >> > layer, but has nothing to do with your controllers.
> >> >
> >> > If the reason you're using ActionStack is to re-use business logic...
> >> > then that logic should be pushed into your models. If the reason you're
> >> > using ActionStack is to re-use view scripts, then simply consume those
> >> > view scripts. A judicious use of view placeholders and view helpers can
> >> > achieve some tremendous results.
> >>
> >> I tried to find some posts / blogs about this subject - I would be happy
> >> if
> >> you could provide me with some links to your posts.
> >
> > Most recently, a talk at ZendCon:
> >
> > http://www.slideshare.net/weierophinney/architecting-your-models
> >
> > I've also blogged on the subject a few times, though most of the entries
> > are a bit dated in regards to how I actually code now:
> >
> > http://weierophinney.net/matthew/plugin/tag/mvc
> >
> > Paddy also has some great articles; browse his blog at:
> >
> > http://blog.astrumfutura.com/
> >
> >> About ActionStack - we are using it to re-use both business logic and
> >> view
> >> scripts. If working with only one action per request where is the point
> >> in
> >> having more than one action anyway? I am talking about sth like a
> >> "PageController" architecture. A request is handled by a PageController
> >> which knows (from configuration) how to render the page. This includes
> >> the
> >> layout, the actions + their parameters and templates. If you try to
> >> implement re-usable actions, a single page will always consist of several
> >> action calls.
> >
> > The PageController pattern is different than the FrontController
> > pattern. Your description of PageController is spot on -- but it's not
> > how FrontController works. FrontController decomposes a request, and
> > from there determines what actions to take. The workflow is different,
> > in that there is not an assumption of a single course of action, or of a
> > specific handler for a given request. As such, a "controller" is merely
> > a group of related actions -- typically actions that operate on a common
> > resource. This is the rationale for multiple actions per controller.
> >
> > Within the ZF FrontController implementation, there are several other
> > components where responsibility may be delegated: front controller
> > plugins, action helpers (which also can be used to automate, via init()
> > and pre/postDispatch() hooks), even bootstrap resources.
> >
> > Another thing: view helpers can be aware of models, and make calls on
> > them. This provides some tremendous flexibility in regards to re-usable
> > presentation logic.
> >
> >> Simple example:
> >>
> >> listArticlesAction() which lists a set of articles using a given template
> >> -
> >> the action itself queries it's data from the model: $model->getArticles()
> >> by
> >> passing parameters.
> >>
> >> Now, you like to display different sets of articles on different pages
> >> and
> >> it should be completely configurable - much like in a typical CMS with db
> >> backend.
> >>
> >> I cannot see any benefit from using view helpers or placeholders here
> >> instead of an ActionStack. Where would you effectively configure which
> >> set
> >> of articles to display on a given page. Also think of several different
> >> sets
> >> of articles per page. This sould easily be re-configurable in any point
> >> of
> >> time.
> >
> > This is actually *trivial* to do with a well-defined model and a view
> > helper.
> >
> > Consider this helper definition:
> >
> > class Blog_View_Helper_Articles extends Zend_View_Helper_Abstract
> > {
> > protected $_model;
> >
> > public function articles($set = null)
> > {
> > if (null === $set) {
> > return $this;
> > }
> >
> > return $this->getModel()->fetchArticlesBySet($set);
> > }
> >
> > public function setModel(Blog_Model_Article $model)
> > {
> > $this->_model = $model;
> > }
> >
> > public function getModel()
> > {
> > if (null === $this->_model) {
> > $this->setModel(new Blog_Model_Article);
> > }
> > return $this->_model;
> > }
> > }
> >
> > Within a view script, you would then do something like this:
> >
> > <ul>
> > <?php foreach ($this->articles('zend_framework') as $article): ?>
> > <li>
> > "<?php echo $this- url(array('id' => $article->id),
> > 'blog-entry') ?>">
> > <?php echo $this->escape($article->title) ?>
> >
> > </li>
> > <?php endforeach ?>
> > </ul>
> >
> > Because the helper takes an argument, you can then re-use it several
> > times on the page. This takes less overhead than dispatching multiple
> > actions in the request, and pushes the business logic to an easily
> > _testable_ layer as well (view helpers and models are more easily tested
> > than controllers).
> >
> > Hopefully this example and the above resources will give you some ideas
> > for how to accomplish these goals.
> >
> > --
> > Matthew Weier O'Phinney
> > Project Lead | matthew@zend.com
> > Zend Framework | http://framework.zend.com/
> >
> >
>
> --
> View this message in context: http://n4.nabble.com/PageController-ActionStack-vs-Simple-MVC-tp948403p948601.html
> Sent from the Zend MVC mailing list archive at Nabble.com.
>

--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/

没有评论: