Page 1 of 1

Front controller and application levels (again)

Posted: Thu Mar 22, 2007 9:52 pm
by alex.barylski
I can see how a front controller would work magic on an application with a single level menu system

Code: Select all

Dashboard | Articles | Resources | Admin
Assume that to be the primary menu. :)

Instead of using a switch

Code: Select all

switch($page){
  case 'dash':
  case 'articles':
  case 'resources':
  case 'admin':
}
You could seperate each tab into a action and call a controller named master. The master controller is loaded on every page request and delegates the request onto appropriate action handler.

Now assume that each action is to perform some logic and act something like a page controller.

Code: Select all

function actionArticles()
{
  // TODO: Are we doing any of the following:
  // 1) Create new article
  // 2) Update existing
  // 3) Search
  // 4) Listing
}
Note: Creating & Updating both require a seperate actions but don't rely on any GUI so loading of views, etc is redundant.
Now my Master controller is actually derived from a base (ABC) and this master (in ctor) initializes the View (setting tabs to inactive by default). Each Action is then responsible for resetting the view according to it's state. So if Articles is selected, the articles "tab" is made to appear "Active".

Assume I have something like this for Master controller:

Code: Select all

class Master{
  function Master()
  {
    // Initialize master View with all tabs disabled by default
  }
  
  function actionArticles()
  {
    // Reset the Master view so that "Articles" tab is now selected
    // TODO: 
    // Fire up "Articles" sub-controller (which loads "Articles" interface)
  }
}
That last TODO is where I am confusing myself (this is a home-rolled interpretation of model2 MVC - not Zend):

I have managed to wire the Views togather, as the Master view has a member ($_body) which contains the sub-controller viewport. However, this is accomplished by creating View objects inside the Master (either at construction - as required) or inside each controller-action handler, like actionArticles.

I am tempted to try and clean this up(remove some dependencies?) by instead of hard-coding the view creations I simply pass control to a sub-controller (which would act as a page controller, loading appropriate views, handling actions, etc) using a dispatchMessage().

The problem with the latter approach, is the way views are utilized/constructed and then sent to screen. I cannot simply echo the contents of each View and expect the resulting output to be properly organzied.

So I ask, which way is better and why? Obviously, as it stands right now, the latter might now even work (I have to spend some more time thinking).

How do you solve these issues? Using pusedo-code would be best...so it may apply to both my own library and/or Zend... :)

Please be detailed, as anything you can do to offer insight is appreciated ;)

Cheers :)

Posted: Fri Mar 23, 2007 1:51 am
by Kieran Huggins
Have you considered dumping all your output to a middle layer before sending it to the output?

I'm using XML and XSLT these days, which is somewhat overkill in many cases. But back in the day I simply used arrays:

Code: Select all

$output['head']['link']['main-stylesheet'] = '<link rel="stylesheet".../>';
$output['head']['meta']['author'] = '<meta author="me".../>';
$output['body'] = 'blah blah blah...';
$output['page-title'] = 'My Page';
Then I ran them through a very basic template and spat out the page.

Obviously you [wc]ould improve on this example by leaps and bounds, but the principle might be helpful?

Posted: Fri Mar 23, 2007 2:56 am
by alex.barylski
Not sure I follow...

I'm using a template system (bTemplate) and Views (just as Zend) but it sounds as though you are suggesting I send all output to a super global and echo that global when time is appropriate? :?

I think the former method I mention, is the most likely, using a series of sub-controllers attached to the parent controller (as composites) either at construction or specific action handling...

This is how I think most do it...at least this is the impression I got from Maurgrim a while back when I asked this same question and his reply was sub-controllers are standard practice...at least if my memory serves me correct, this is wht he said (or something to that effect).

Just wanted to get an idea on how others did it, but I guess there are only so many ways you can skin a cat? :)

Thanks :)

Posted: Fri Mar 23, 2007 3:21 am
by Kieran Huggins
Maybe I didn't follow... sorry :-)

I think the Zend scheme is to use:

Code: Select all

http://domain.com/(pageController)/(action)?/val1/val2
so you'd have:

Code: Select all

http://domain.com/account/edit/4

Code: Select all

class Master{ // master controller
   function __contruct(){
      // calls the page controller and it's action (defaulting to index if there is no action)
      call_user_func_array(array(new $pageController,$action?$action:'index'),$args);
   }
}


class account{

   function edit($id){
      //edit user with the id of $id
   }

}
Does that make sense? That's what I'm doing in my current project anyway...

Posted: Fri Mar 23, 2007 3:31 am
by alex.barylski
Hmmm... :)

Thats basically the idea, although I don't invoke controllers/actions directly. And what about output/HTML? Where do you echo to screen and when?

Cheers :)

Posted: Fri Mar 23, 2007 3:42 am
by Kieran Huggins
I don't know if it's taboo, but I do that in the master controller. My path:

include my libraries
parse the URL (using regexp)
instantiate a DOM Document (global $doc)
define my __autoload (for data objects)
call the appropriate controller/action (which adds DOM nodes to the global $doc)
then transform that $doc to a view using an XSLT file (depending on the output: xhtml, wap, json, xml, etc...)
then echo the response

I have no idea if that's best practice, but it's what I'm doing...

Posted: Fri Mar 23, 2007 3:54 am
by alex.barylski
hehe...

I don't think calling the controller:action directly is best practice, but thats' neither here not there :P

I'm not familiar XSLT...or how your using it to generate a view, but it sounds like your using a DOM as well...in which case our solutions differ greatly...

You should write a tutorial on how your templating using XSLT and DOM I'd like to read it...but for the sake of this conversation, I'd like to stick to the problem at hand as I'm curious to hear others as well. :)

Thanks mang 8)

p.s-Muchly appreciate your help in the client forum too amigo, that jQuery stuff is awesome :)

Posted: Fri Mar 23, 2007 4:00 am
by Kieran Huggins
glad to help :cool:

Posted: Fri Mar 23, 2007 4:49 am
by Maugrim_The_Reaper
You could seperate each tab into a action and call a controller named master. The master controller is loaded on every page request and delegates the request onto appropriate action handler.
You're making sense here if I read it right - a "master" acts as a central activity point for a group of Actions. In ZF this could be a subclass of Zend_Controller_Action, which all other "group" controllers would extends - similar idea.

Bear in mind the ZF scheme doesn't have page controllers as such - yes, you can implement it in that way but the "controller" class is just a generic grouping of Actions - you could have a dozen controllers for a specific page, module or section.
Thats basically the idea, although I don't invoke controllers/actions directly. And what about output/HTML? Where do you echo to screen and when?
Have you implemented a Response object? This is separate from the View, and can handle headers, View output, and other response stuff. Once it's been packed for the current request cycle you can echo it directly from the Front Controller - or pass it back to your bootstrap file for further manipulation (like exception rendering setting, extra headers, audit trails, etc.). All it needs to do is implement __toString() and you can echo it.
This is how I think most do it...at least this is the impression I got from Maurgrim a while back when I asked this same question and his reply was sub-controllers are standard practice...at least if my memory serves me correct, this is wht he said (or something to that effect).
Subcontrollers are common, but there are better ways. For static View elements like Menus, the simplest is to allow your View classes to utilise "Helpers". These are really just classes which are attached to components of a View. Here's an example:

I have a set of pages which share the same menu - common situation. The menu appears for all templates. I can a) adjust the menu in all Views, b) use a Master Controller, or c) use a dedicated View Helper. Option A adds duplicated code to all Views. Option B requries a single "Master" controller (can have issues since it adds yet another layer to the controller hierarchy). Option C is not duplicative, plus as a bonus it travels well (re-useable) since it can form a simple Menu handler class useful in similar projects.

To implement we have the View_Helper_Menu class and a Menu template. When registered, the Helper will be attached (composition) to the View layer, maybe a $view->menu() or a $view->menu->render() sort of connection. The Helper may also contain a reference to the current View (to access view data easily). Our full page template can then be as simple as:

Code: Select all

<html>
<head>
<title>My Full Page with Menu</title>
</head>
<body>
    <div id="header">Some Page</div>
    <div id="menu">
        <?php echo $this->menu->render(); ?> <!-- render returns the HTML but does not echo it-->
    </div>

<div id="content">
    Some random content...
</div>
</body>
Using subcontrollers can do something similar - just bear in mind they can introduce the same repetitive code in the calling controllers and there's the extra level of dispatching. A quick Helper example - this isn't remotely tested, it's just to give some idea of how it works.

Code: Select all

class View_Helper_Menu
{
    private $_view = null;

    public function __construct(Zend_View_Abstract $view)
    {
        $this->_view = $view;
    }

    public function render()
    {
        /* Do some data collection to see what menu logic needs updating */
        if ($this->_view->ctrName == 'Articles_Controller') {
            /* indirectly attach a "hide" flag to the View */
            $this->_view->hideArticlesItem = true;
        }
}
The menu sub-template would be a typical template for your system - with just the Menu HTML and your hide/enable checks for each element. All the Helper does is manipulate the View data based on a set of conditions (could be anything you want including controller/action names) and includes the sub-template - the main View will do the rest. Your Master controller could feasibly set the conditional View data assuming it can be made aware of what extending controllers are being instantiated. Usually a Request object or separate Dispatcher object helps with this.

In the ZF I can't remember exactly how View Helpers work - but something similar is likely possible with modification.

Posted: Fri Mar 23, 2007 11:50 am
by alex.barylski
Hey Maurgrim, thanks for that. Sounds very similar to how I've done it (after some refactoring). :)

Your input is appreciated :)

Posted: Fri Mar 23, 2007 12:42 pm
by Maugrim_The_Reaper
I managed to forget the include statement in the View Helper that loads the sub-template for a menu...;).