Advanced Search

View RSS Feed

traq

Taking Control

Rate this Entry
Last week, I wrote about taking a "PHP-first" approach to programming - program first, output last. But what should go first in your program?

M-V-C
You may have heard about something called "MVC" (Model-View-Controller). If not, here's your crash course:

Model

Your Model is all of the information needed/used by your program. [Hopefully], the Model is well-organized. Think of it in terms of note-taking: notes are easier to read when they're organized by subject, sub-subject, etc. than when they're just scribbled haphazardly all over the margins of your textbook.

(Yes, I know that some people - and yes, I'm one of them - actually function better with a huge glob of cryptic post-its ...or we seem to prefer things that way, at least... but we do not think like computers. Computers don't like post-its. Trust me.)

View

Your View is how all that information is supposed to be displayed. In our domain - Web Design - it's actually very easy to figure out what belongs in the View: output! If there's HTML, then it belongs in the View. Likewise, CSS, JavaScript, even HTTP Headers, etc., belong here.

Learning "PHP-first" helps by separating all of the output from the rest of the program. However, consider that the View is not the output itself, but all of the stuff that is going to be output. An example, where we show a comment on a page:

PHP Code:
<?php

// Model: the information we need
list( $message,$author,$publish_date ) = get_comment$commentID );

// (`$commentID` is some identifier for the comment we want (e.g., a DB record id))


// View: not output *yet*, but it *will be*!
$comment = <<< HTML
    <article class="comment">
        <p>
$message</p>
        <p class="author">Published by 
$author on $publish_date.</p>
    </article>
HTML
;

// View?  no...   than what?
print $comment;
Controller

The Controller is the missing part of the plan. The Controller controls everything. It's the parts of your script that check conditions, make decisions, and order things to be done. In the above example, the last line is part of the controller: it tells the script what to do (i.e., print something). There is probably some other Controller code [not shown in the example] that decided to build the comment in the first place - some sort of if( this ){ then; }else{ that; }.

So, any given PHP script probably has two types of Controller code:
  • code that makes decisions, and
  • instructions for carrying out decisions.


With a Controller, our example might look something like this:

PHP Code:
<?php

// controller logic (decide what to do):
//   IF the user asked for a comment,
if( !empty( $_GET['comment_id'] ) ){
    
// procedure (instructions on what to do):
    //   get the comment they want.
    
$model getComment$_GET['id'] );
    
$view prepareComment$model );

// controller logic:
//   OTHERWISE,
}else{
    
// procedure: 
    //   show a random comment.
    
$model getCommentNULL );
    
$view prepareComment$model );
}

// procedure:
print $view;
You'll notice that there's not a lot of code inside each if{} and else{} block - just a few calls to functions. This is done on purpose. While a coder's first thought might be to put all of the instructions right inside the block where they're being used, you'll end up with lots of nested blocks that way - it quickly becomes difficult to keep track of your Controller's logic, and mistakes start showing up. Just like we're separating the PHP from its output, we'll separate the Model and View from their Controller. This has the side-effect of making the code reusable (instead of writing all the code twice, just call the function again).

This pattern repeats throughout the program. The functions used in the Controller (to create the Model and the View) contain their own Controllers:

PHP Code:
<?php

function getComment$commentID=NULL ){
    
// controller: two possibilities...
    // 1) we want a random comment (specified by making $commentID=NULL).
    
if( is_null$commentID ) ){
        
/* get a random comment */
        // we might have gotten this info from a database, or somewhere else -
        //   doesn't really matter for this example.
        //   What's important is, if we get the info, we return it in an array:
        
return array( $message,$author,$publish_date );
        
    
// 2) we want a specific comment (specified by a non-NULL $commentID).
    
}else{
        
/* get the comment with commentID = $commentID */
        // again, if you find the info, return it.
        
return array( $message,$author,$publish_date );
    }
    
    
// HA! you say.  A third possibility!
    //   what if we wanted $commentID, but we couldn't find it?
    // well, if we found it, then this function has already returned (finished).
    // if not, then this last bit of code will be executed:
    
return FALSE;
    
//   ...and will indicate that we didn't get what we wanted.
}

function 
prepareComment$model=FALSE ){
    
// controller: two possibilities...
    // 1) $model is an array, returned from getComment().
    
if( is_array$model ) ){
        
// get the info from the Model
        
list( $message,$author,$publish_date ) = $model;
        
        
// use it to create the view
        
$comment = <<< HTML
    <article class="comment">
        <p>
$message</p>
        <p class="author">Published by 
$author on $publish_date.</p>
    </article>
HTML
;
        
// and return the view (for the controller to print later).
        
return $view;
    }
    
// 2) $model is not an array (should be FALSE, indicating the comment wasn't found)
    // return an error message.
    
return <<< HTML
    <p class="error">I'm sorry, the monkeys couldn't find that comment.</p>
HTML
;
}

Hint, Hint, Hint...

You might see some possible complications in the code above: there may be some cases where errors would creep in and break things. But error handling is a topic for another day...

PAQ (Please Ask Questions),
- Adrian

Submit "Taking Control" to del.icio.us Submit "Taking Control" to StumbleUpon Submit "Taking Control" to Google Submit "Taking Control" to Digg

Comments

  1. Nile's Avatar
    Actually, your representation of the control pattern is more of a presenter pattern (MVP, not MVC). You'd benefit from reading a ton of this user's top answers. He's brilliant and has loads of knowledge on this topic. Specifically look at this answer.