2009年12月7日星期一

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

Hi Christian,

this is exactly the kind of discussion I wished to start :)

I think you gave some good ideas how to implement a "block" system using a
generic view helper. Correct me if I'm wrong:

You have a layout file:

layout.phtml (simple example)

<html>
...
<body>
<?php echo $this->block('navigation')?>
<div id="left>
<?php echo $this->block('content-left')?>
</div>
<div id="right>
<?php echo $this->block('content-right')?>
</div>
<div id="footer">
<?php echo $this->block('footer')?>
</div>
</body>
</html>

The view helper "block" internally works on a PortalModel, which knows for a
given page and a given layout block name, which content to display / render.

Block ViewHelper (very simplified):

class Zend_View_Helper_Link extends Zend_View_Helper_Abstract
{
public function block($blockId) {
return PortalModel::render($blockId);
}
}

Now, the PortalModel still doesn't know for which page to render the block.
Would you suggest that the PortalModel itself knows - by e.g. scanning
$_REQUEST for sth like a pageId - which page/block to render?

If a basic ZendMVC setup is given - any request will be handled by a
controller/action pair. There needs to be a controller which decides about
the to-be-used layout file for the requested page. How would you implement
such a decision controller? The only thing that comes to my mind again is
one default controller that handles all requests which are satisfied by
"readonly" actions on models (the block view helper precisely is a readonly
model access). All read&write actions and AJAX requests would be handled by
"traditional" controller / action pairs.

I'll spend some more time thinking on that solution...

Thanks for your ideas!

Christian


Shurakai wrote:
>
> Hi Christian,
>
> I'm currently tackling the very same problem, although in my case, there
> already exist some kind of modules which need to be integrated in a
> "block"-structure afterwards.
>
> On the one hand, using the action stack seems quite comfortable - but this
> might be due to the fact that I'm trying to use this well known approach
> to all of my problems.
>
> So, considering the extent of performance losses and other mentioned
> problems, I decided to try and think about how this could be implemented
> by not using the action stack.
>
> Ralph Schindler gave a good point about that:
>> Another interesting note is that you dont really have to have a
>> controller at all in portals. A controller's duty should be limited to
>> parsing the environment and from that environment, determining which
>> models to instantiate and pass onto which views. That said, it makes a
>> lot of sense, in building a portal, to build a Portal model that can be
>> called to handle the task of finding the proper sub models, assinging
>> them to a view and returning that content back to the response object.
> My ideas are as follows:
>
> In my Layout, I use a blockContainer ViewHelper, which is passed the
> current containername. Afterwards, this viewhelper calls an instance of a
> page / portal / system model:
>
> PortalModel::runBlocks($containerName);
>
> One could use output caching in order to return the content generated.
>
> The runBlocks()-Method itself should iterate through some kind of Queue
> and should call a run()-method on each block contained. The blockmodel
> itself might decide which models to load etc. - although this might be
> exactly the abovementioned "parsing the environment and from that
> environment, determining which models to instantiate and pass onto which
> views".
>
> The major point I'm missing is the use of ActionHelpers / hooks. Two short
> examples:
>
> -> I want each block to be surrounded by a 1px dashed border with admin
> operations available. On the one hand, I could do this in the runBlocks()
> method (check whether in admin mode or not, then behave appropriately),
> but I don't like the idea of binding my application to this behaviour, so
> I assume that one had to implement some kind of EventSystem...
>
> -> Using the ViewRenderer will not be possible and therefore, one had to
> call the render() method manually. I considered the viewRenderer to be
> quite comfortable and I'd hate not being able to use it.
>
>
> For my AJAX CRUD operations, I'll continue using controllers because in
> this case, loading blocks is unnecessary.
>
> But what if you're not using AJAX? Consider some kind of administration
> menu that needs to give you an overview about some items, delete, update
> and create possibilities. (i.e. CRUD interfaces)
>
> Two approaches came into my mind:
>
> 1.) The block had to decide what to do and what to render. In my eyes,
> this is exactly what a controller should do, so maybe in this case, the
> blocks' run()-method should get the frontcontrollers dispatcher and invoke
> a controller. The advantage is that you are still able to use the
> contextswitch actionhelper, which might help you with reusing this code
> when using ajax.
>> ANd the reason you don't need the C
>> is b/c that code generally would not need access to any environment
>> specific stuff like $_POST or $_GET, as that would have been handed by
>> the top most controller.
> 2.) Some kind of naming agreement for block parameters / data is being
> used, e.g. by using the blockid or something like that. In this case, the
> parameters are passed to the block and the run method should invoke the
> appropriate methods.
>
> I think that I really need to get more information on this topic and I
> really want to thank Matthew for supplying a bunch of links.
>
> I'm very interested in a further discussion on approaches and anyones
> oppinion will be warmly welcomed.
>
> Best regards
> Christian Heinrich
>
>
>
> Christian Ehmig wrote:
>>
>> Sorry I didn't want to act close-minded. I am just curious how others
>> solve this issue and I haven't seen a complete example yet. I also think
>> I was not able to state my problem clearly enough.
>>
>> I'll try to read your other ressources soon - btw. I closely followed
>> Paddy's blog back 2 years ago when the first discussion about partials
>> and view helpers came up which I supported gladly.
>>
>> I understand the three major dont's why not to use the action stack
>> except the separation of concerns - we do not have any view /
>> presentation logic in our controllers. Maybe I explained the sth wrong:
>>
>>
>> weierophinney wrote:
>>>
>>>> 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.
>>>
>>
>> I meant to set any parameters which affect the data that is retrieved
>> from a model (number of items, sorting mode, ...) This has to be done
>> somewhere and I surely don't want to to that in a layout file by calling
>> a view helper with those parameters statically.
>>
>> I am still asking about the solution for a central, configurable page
>> renderer based upon Zend MVC.
>>
>> Spreading this logic over plugins, layout files and templates (via view
>> helpers) is, as I said, not globally and centrally configurable. Where do
>> I configure what is displayed on a page alltogether being easily
>> adjustable without changing PHP code? All examples I've seen so far
>> always would need ro register additional plugins or manually specifying
>> the rendering of additional templates for a specific page.
>>
>> Maybe I just got to the point (like Ralph Schindler mentioned) where I
>> realize, that Zend MVC cannot be used the way I need the system to be
>> build. This would finally mean: use only Zend_View and replace the
>> FrontController architecture by a PageController architecture.
>> Furthermore, do not try to build a PageController upon a FrontController
>> system :)
>>
>> Finally - thank you a lot for your time spent on answering my questions
>> and don't get me wrong - I love the Zend Framework :D
>>
>>
>>
>>
>>
>>
>>
>>
>>
>> weierophinney wrote:
>>>
>>> -- 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/
>>>
>>>
>>
>>
>
>

--
View this message in context: http://n4.nabble.com/PageController-ActionStack-vs-Simple-MVC-tp948403p954287.html
Sent from the Zend MVC mailing list archive at Nabble.com.

没有评论: