Front controller advice

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

matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Thanks again for your explanation Maugrim. Gives something to think about. It doesn't sound too complex (ok, a bit). The biggest challenge will be to find the right implementations.

But my feeling is that my approach should be to start simple and with the big lines. If I know exactly what each little pieces' responsibility is and how the different parts should interact (the API's you talk about), the details should be filled in after that.
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Ok, today I took some time to look at my project again.

I'm planning to use the Frontcontroller code (from Arborint) I gave in my first post. In that post I also gave some code I wrote to get something working.

Code: Select all

<?php
/**
*     Action Contacts
*     show a list of contacts
*
*/

include BASE_DIR . 'adodb/adodb.inc.php';

// this is the model class for contacts
// normally this would be a seperate file and included
class contactsmodel extends ADOConnection {
 
  var $db;
  var $result;

  function contactsmodel(&$db) {
      $this->db = &$db;
      $this->listcontacts();
  }
  function listcontacts() {
      $this->result = &$this->db->Execute("select * from contacts");
  }
  function fetch() {
      return $this->result->FetchRow();
  }     
}

// this is the action controller
class contacts {
 
  function execute(){

      $DB =& NewADOConnection('mysql');
      $DB->debug = true;
      $DB->Connect(dbHost, dbUser, dbPassword, dbName);
      $view = &new contactsmodel($DB);

      // for now just echo out the results. normally this would go to a view
      while ($row = $view->fetch()) {
          echo '<pre>';
                      print_r($row);
          echo '</pre>';
      }
  }
 
}
?>
Obviously there's still some work to do with this spagetti-oop-code. But before I go on, I have this question:

Say the response of an action depends on the request it gets. If we take the example above. Class Contacts should show a list of the contacts by default, plus a form to edit/add/delete (one or more of) the contacts. So by default it just shows a list with the contacts. But if the "page" receives an update/delete/add request, it should handle that request.

How would I do that? Would I build in some sort of loop inside the execute() function?

Code: Select all

<?php
// pseudocode:
class contacts {
 
  function execute(){
     if (  request is update ) {
         // do something
     }
     elseif (  request is delete )
     {
         // do something else
     }
      else 
     {
         // show list
      }

 }
}
?>
So you've got a controller (the if else loop) inside an action-controller (contacts) which is called by the Frontcontroller.

Is this going in the right direction or am I missing something? (this is harder then I thought... but I will persevere :) )
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

This is the (very) old question of where to put stuff. I want to make sure you are clear that the separation between the Model and the Presentation (View+Controller) is the most important thing. People much smarter than me (a very big group! :)) do not have any clear answer about how to split stuff between the View and the Controller -- just guidelines. The only thing I have found is that I try to get as much out of my controllers as possible -- only the request processing and control logic that gets duplicated everywhere goes in there.

So for your example I would push more into the Model, for example:

Code: Select all

<?php
/**
*     Action Contacts
*     show a list of contacts
*
*/

include BASE_DIR . 'adodb/adodb.inc.php';

// this is the model class for contacts
// normally this would be a seperate file and included
class contactsmodel {
 
  var $db;
  var $result;

  function contactsmodel() {
      $this->db =& new ADOConnection('mysql');
      $this->db->debug = true;
      $this->db->Connect(dbHost, dbUser, dbPassword, dbName);   // these values need to come from somewhere
      $this->listcontacts();
  }
  function listcontacts() {
      $this->result = &$this->db->Execute("select * from contacts");
  }
  function fetchAll() {
      $rows = array();
      while $row = $this->result->FetchRow())
        $rows[] = $row;
      }
      return $rows;
  }     
}

// this is the action controller
class contacts {
 
  function execute(){
       $this->view(new contactsmodel());
  }
  function view($model){
      $rows = $model->fetchAll();
      foreach ($rows as $row) {
          echo '<pre>';
                      print_r($row);
          echo '</pre>';
      }
  }
 
}
?>
There is no reason not to put the DB connection stuff in the Model if you don't know where to put it and it is only used once. You might also want to do something like:

Code: Select all

<?php
class contactsmodel {
 
  var $db;
  var $result;

  function contactsmodel(&$locator) {
      $this->db =& $locator->get('DB');
      $this->listcontacts();
  }
...
}
Now creating the DB connection is moved outside the Model so it doesn't need to know specifics about it, just the general interface.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Thanks for your answer Arborint.

Looks like some good advice to me. Indeed, the fact the db handling was split up in every class was bugging me a bit. Looks much better this way. I will also have a look at the second tip you gave about using a locator.

The main question I was after is still hard to grasp though: what if I want to do more then one kind of action with my action controller? As it is now, it's a one-way cascade of commands, something like:
Request -> Frontcontroller -> Action controller -> contactsmodel -> action controller show the results

However, were do the other actions related to the (in this example) contactsmodel belong? I'll have actions like update contact, delete a contact, etc etc.

Something like:

Code: Select all

<?php
// this is the action controller
class contacts {
 
  function execute(){
       if ( .. ) {
         $this->view(new contactsmodel());
       } else {
         $this->update(new contactsmodel());
       }
  }
  function view($model){
      $rows = $model->fetchAll();
      foreach ($rows as $row) {
          echo '<pre>';
                      print_r($row);
          echo '</pre>';
      }
  }
  function update($model) {
      $row = $model->fetchOne();  // I would add methods fetchOne and insertOne to the model
      $newrow = $model->insertOne();
  }
 
}
?>
However, how does the class contacts know what to do? Does the Frontcontroller would have to make another decision? It seems to me that would not be the right thing to do.

In your framework examples I saw there are are "HandleInstance" used in the form controller example. However, at the moment I would like to keep it a bit less complex then that example (in which the class Form extends the FromController which extends appcontroller which extends inputcontroller which initializes a Handle in which the HandleInstance is found 8O )

Maybe I will get there in the end, but for now I would like to take it one step a time.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

matthijs wrote:The main question I was after is still hard to grasp though: what if I want to do more then one kind of action with my action controller? As it is now, it's a one-way cascade of commands, something like:
Request -> Frontcontroller -> Action controller -> contactsmodel -> action controller show the results

However, were do the other actions related to the (in this example) contactsmodel belong? I'll have actions like update contact, delete a contact, etc etc.
Well, to select the class and the method you obviously needs two parameters. The Skeleton was intentionally kept simple (with execute() as the default method) because you can always create another action class named contactsupdate that also uses contactsmodel. This forces you to split things into lots of little action classes which is generally good because it encourages reuse by making things like your contactsmodel obviously a separate reusable class.

More complex dispatchers do allow you to specify both the class and the method. Most big frameworks do this. Struts specifies both in its XML files and this is the RoR style /:controller/:action/ "routing" as it is called.

I recently updated the my Skeleton code to have naming like PEAR/ZF. The updated code supports both this kind of routing and forwarding as well. Forwarding is where the action exits and returns another action to dispatch next, so you can chain actions. I just need to update the examples and I will release it.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Cool, thanx!

The option of making other action classes, say for updatecontact, delete contact etc is indeed a possibility that will keep each class small and easy (important for me at the moment). One might end up with a lot of classes, but that's the price for doing it that way I guess.

The other possibility of a more complex dispatcher is interesting to look into as well. Looking forward to see the updated framework. Already learned a lot by only looking at it.
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 find forwarding eventually is required once an action is itself a reusable component. Depending on the complexity of your framework it may be a rare case, but is does occur somewere along the line for frequently used actions. It's worth putting in from the start rather than trying to hack in later when you're not expecting it (says me ;)).
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

I find forwarding eventually is required once an action is itself a reusable component. Depending on the complexity of your framework it may be a rare case, but is does occur somewere along the line for frequently used actions. It's worth putting in from the start rather than trying to hack in later when you're not expecting it (says me
What you mean by this is that it might be usefull to go with the option of the more complex dispatcher Arborint was talking about? (instead of 100 small classes for every possible action?)

Just trying to understand it correctly.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Well, it needn't be hugely complex (aborint's version will likely demonstrate that when available). The forwarding is the ability to commit one action, and forward to a second action. Both are likely as simple as you've envisioned so far. It's the ability to chain them to run in sequence for a specific request that can come in handy.

This is not a good example, but it's like having an action which alters a user's password. Personally my actions would drag in a User domain object that handles this but by way of an example...

At some stage you may need to enact this action for various scenarios: Account Registration, Password Resetting, etc. Rather than having separate actions replicating the same effect, you can chain the Password setting/generating action to some other action, whether creating a user for the first time, or after verifying a link for resetting an account password. So you have 2 actions in a chain, and 1 resulting view:

Create user -> Generate new password -> View new profile page, or
Validate reset passwd link/hashcode -> Generate new password -> View profile page

It's a weak example - I'd just end up calling $user->setPassword(true); to generate one where $user is an object representing database action - i.e. I wouldn't have it as a separate action (it's just one line!). There are stronger examples, but at the moment they're rare in my own framework's scheme.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

The ability to Forward will not reduce the number of action classes you have -- and may increase it.

Forwarding can be used as Action Chaining where Actions are called in sequence, each doing operations on the Request or Response. You can achieve the same thing by just calling the forwarded Action directly, but having the controller do the call adds a degree of modularity and independence to your code because the Action that forwards does not have any dependency on the next Action (other than shared data).

Forwarding can also be used for the same purpose as Redirecting, but the URL does not change like a Redirect -- which can be useful.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

ok, you guys lost me there I'm afraid. I kind of understand what you are talking about, but the implementation is not.

The only thing I want to build in is some simple loops. In pseudo code:

Code: Select all

if (form_is_submitted && data_are_valid) {  do some action }
elseif ( form_is_submitted && data_is_invalid) { do some other action like redisplaying the form }
else ( default action ) { show form }
In procedural that would be easy. Almost literally the pseudocode above. But how would one do that when using a frontcontroller and actioncontrollers? Should I just start writing action classes for each situation?

Like
class DisplayFormContacts, class DisplayFormContactsAfterSubmitting, class DisplayFormWhenDataIsNotValid, etc etc
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

WIth the current Skeleton code you would have an action class for each of those. If you wanted to reuse code, those classes could inherit from a common DisplayForm or DisplayFormContacts class that you would write. Or just include a common class, such as ContactsGateway, and have that do the common work that needs to be done.

Let me give you an example, for CRUD pages I often create base classes CrudList, CrudEdit and CrudDelete that I can extend to create specifc actiion classes. I have the action class fill in the properties of the base class and then execute it.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

That's a good idea. The project will basicly consist of a couple of CRUD pages, so your example makes sense. I'll start coding some classes according to your example now.
Thanks again for your advice.
Post Reply