One of the much talked about patterns of the moment is the model-view-controller or model-view-presenter or any of the undoubtedly thousands of variants thereof. Here’s a deliberately really simple one to illustrate the pattern. If you can read it and understand it I hope its useful to you.
Interestingly a vague attempt at inversion of control, another design pattern which IMHO is more of a guide line, is in play here. It is done specifically to allow good component separation and useful test patterns. Hence as long as you have a class that fits the dependency obligations you can use it with the ones given here.
Now for the seriously crazy bit, the code on this page has not been tested to work. Its just here to give you some idea of what the model-view-controller pattern and how it could be implemented.
mvc.controller.php
Overview:
At the heart of each variant implementation of the model view controller pattern is the controller. A controller basically directs requests to the appropriate model or view processing functionality. In the case below it only makes the distinction between the two types and not the instances of functionality.
Some controllers combine a lot more but its not actually necessary and generally ends up making the controller very configuration and platform specific. By keeping configuration out of the controller we make it as general as it can be.
Notes:
Note that there is no exception handling here either. It is not wanted. Consider for a moment that if an exception was not caught, there would be no response to the client, which is a security requirement in many instances. However it there would be a log to trace to the point of failure because of the log entry call.
If exception handling were introduced it would compromise the generic nature of the controller as the controller would need to know what type of response to send to the client. For example if the error was thrown before the content of the request could be analysed and what data would you respond to the request with? HTML? XML? A media stream? A GIF? A JPG? The rule of thumb here is only respond if you know what language to talk in, if you don’t know the requested format you can’t even use clever configuration to determine how respond to a request.
Documented dependencies:
- class ‘Model’ : method ‘process_request’ : parameters ‘request to process’ : returns ‘new request for either model processing or view generation’
- class ‘View’ : method ‘process_request’ : parameters ‘request to process’ : returns ‘a view generated from the accumulated data in the request’
Undocumented dependencies:
- class ‘Log’ : method ‘writestr’ : parameters ‘string to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Log’ : method ‘writereq’ : parameters ‘request to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Request’ : method ‘is_a_model_request’ : parameters ‘none’ : returns ‘true if the request is to be processed by the model’
Listing:
<?php
Log::getInstance()->writestr( "file:".__FILE__.":loaded", "debug" );
class Controller {
private $model;
private $view;
/*
* build this controller object
*/
public function __Construct ( $model, $view ) {
$this->model = $model;
$this->view = $view;
}
/*
* process a request
*/
public function process_request ( $request ) {
Log::getInstance()->writestr(
"class:".__CLASS__.":process_request", "debug" );
Log::getInstance()->writereq( $request, "debug" );
/*
* first process the model request
*/
$new_request = $this->model->process_request( $request );
/*
* if this request results in another model request
*/
if ( $new_request->is_a_model_request( ) )
return $this->process_request( $new_request );
/*
* otherwise generate view
*/
return $this->view->process_request( $new_request );
}
}
?>
mvc.model.php
Overview:
A model is responsible for the production of data for the view. Essentially it contains access points to the various methods of the underlying application called actions. In this case it simply has a list of actions that it locates and then executes the action sending the results back to the caller.
Notes:
As with the controller (see above) the model does little more than the basics leaving other classes that know what they are doing to handle the specifics of content. In line with this the model simply logs progress and fails silently.
Undocumented dependencies:
- class ‘Log’ : method ‘writestr’ : parameters ‘string to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Log’ : method ‘writereq’ : parameters ‘request to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Actions’ : method ‘find_requested_action’ : parameters ‘request to locate action for’ : returns ‘an Action object’
- class ‘Action’ : method ‘process_request’ : parameters ‘request to process’ : returns ‘new request for either model processing or view generation’
Listing:
<?php
Log::getInstance()->writestr( "file:".__FILE__.":loaded", "debug" );
class Model {
private $actions;
/*
* build this model object
*/
public function __Construct ( $actions ) {
$this->actions = $actions;
}
/*
* process a request
*/
public function process_request ( $request ) {
Log::getInstance()->writestr(
"class:".__CLASS__.":process_request", "debug" );
Log::getInstance()->writereq( $request, "debug" );
/*
* find the action from the list of actions
*/
$action = $this->actions->find_requested_action( $request );
/*
* run the action and return the resulting request
*/
return $action->process_request( $request );
}
}
?>
mvc.view.php
Overview:
A view is called to format the data retrieved from the model into the format that the original client requested. Essentially it contains access points to the various view methods of the underlying application. In this case it simply has a list of views that it locates and then executes sending the results back to the caller.
Notes:
Now those sharp of eye will realise something; this looks pretty much like the model class above, and you’d be right. Apart from a a simple renaming of a few items the code is the same. In practice both the above model class and this view class would be implemented as a single class and simply configured differently. They are only provided in their separate formats here to highlight the model-view-controller form.
Undocumented dependencies:
- class ‘Log’ : method ‘writestr’ : parameters ‘string to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Log’ : method ‘writereq’ : parameters ‘request to log’, ‘name of log level to record string at’ : returns ‘nothing’
- class ‘Views’ : method ‘find_requested_view’ : parameters ‘request to locate view for’ : returns ‘an View object’
- class ‘View’ : method ‘process_request’ : parameters ‘request to process’ : returns ‘the final generated view’
Listing:
<?php
Log::getInstance()->writestr( "file:".__FILE__.":loaded", "debug" );
class View {
private $views;
/*
* build this view object
*/
public function __Construct ( $views ) {
$this->views = $views;
}
/*
* process a request
*/
public function process_request ( $request ) {
Log::getInstance()->writestr(
"class:".__CLASS__.":process_request", "debug" );
Log::getInstance()->writereq( $request, "debug" );
/*
* find the view from the list of views
*/
$view = $this->views->find_requested_view( $request );
/*
* process the request and return the resulting view
*/
return $view->process_request( $request );
}
}
?>
