Model–View–Controller (MVC) is an architectural pattern used in software engineering. The pattern isolates business logic from input and presentation, permitting independent development, testing and maintenance of each.An MVC application is a collection of model/view/controller triplets (a central dispatcher is often used to delegate controller actions to a view-specific controller). Each model is associated with one or more views (projections) suitable for presentation (not necessarily visual presentation). When a model changes its state, it notifies its associated views so they can refresh. The controller is responsible for initiating change requests and providing any necessary data inputs to the model.
User interface logic tends to change more frequently than business logic, especially in Web-based applications. For example, new user interface pages may be added, or existing page layouts may be shuffled around. After all, one of the advantages of a Web-based thin-client application is the fact that you can change the user interface at any time without having to redistribute the application. If presentation code and business logic are combined in a single object, you have to modify an object containing business logic every time you change the user interface. This is likely to introduce errors and require the retesting of all business logic after every minimal user interface change.
How do you modularize the user interface functionality of a Web application so that you can easily modify the individual parts?
User interface logic tends to change more frequently than business logic, especially in Web-based applications. For example, new user interface pages may be added, or existing page layouts may be shuffled around. After all, one of the advantages of a Web-based thin-client application is the fact that you can change the user interface at any time without having to redistribute the application. If presentation code and business logic are combined in a single object, you have to modify an object containing business logic every time you change the user interface. This is likely to introduce errors and require the retesting of all business logic after every minimal user interface change.
How do you modularize the user interface functionality of a Web application so that you can easily modify the individual parts?
The MVC pattern separates the modeling of the domain, the presentation, and the actions based on user input into three separate classes [Burbeck92]:
Model. The model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller).
View. The view manages the display of information.
Controller. The controller interprets the mouse and keyboard inputs from the user, informing the model and/or the view to change as appropriate.
Model-View-Controller (MVC) is a classic design pattern often used by applications that need the ability to maintain multiple views of the same data. The MVC pattern hinges on a clean separation of objects into one of three categories — models for maintaining data, views for displaying all or a portion of the data, and controllers for handling events that affect the model or view(s).
Because of this separation, multiple views and controllers can interface with the same model. Even new types of views and controllers that never existed before can interface with a model without forcing a change in the model design.

Events typically cause a controller to change a model, or view, or both. Whenever a controller changes a model’s data or properties, all dependent views are automatically updated. Similarly, whenever a controller changes a view, for example, by revealing areas that were previously hidden, the view gets data from the underlying model to refresh itself.
We explain the MVC pattern with the help of a simple spinner component which consists of a text field and two arrow buttons that can be used to increment or decrement a numeric value shown in the text field. We currently do not have an element type that can directly represent a spinner component, but it easy is to synthesize a spinner using existing element types.
We explain the MVC pattern with the help of a simple spinner component which consists of a text field and two arrow buttons that can be used to increment or decrement a numeric value shown in the text field. We currently do not have an element type that can directly represent a spinner component, but it easy is to synthesize a spinner using existing element types.

The spinner’s data is held in a model that is shared with the text field. The text field provides a view of the spinner’s current value. Each button in the spinner is an event source, that spawns an action event every time it is clicked. The buttons can be hooked up to trampolines that receive action events, and route them to an action listener that eventually handles that event. Recall that a trampoline is a predefined action listener that simply delegates action handling to another listener.
Depending on the source of the event, the ultimate action listener either increments or decrements the value held in the model — The action listener is an example of a controller.
The trampolines that initially receive the action events fired by the arrow buttons, are also controllers — However, instead of modifying the spinner’s model directly, they delegate the task to a separate controller (action listener).
Multiple ControllersDepending on the source of the event, the ultimate action listener either increments or decrements the value held in the model — The action listener is an example of a controller.
The trampolines that initially receive the action events fired by the arrow buttons, are also controllers — However, instead of modifying the spinner’s model directly, they delegate the task to a separate controller (action listener).
The MVC pattern allows any number of controllers to modify the same model. While we have so far focused only on the two arrow buttons as likely source of events, there is, in fact, a third event source in this example — Whenever the text field has focus, hitting the enter key fires off an action event that may potentially be handled by a different action listener than the one handling action events from the buttons.
This part of the topic is taken from http://www.enode.com/
Why Create My Own MVC Framework?
This article doesn't really advocate "You should write your own MVC web framework!" as is tries to explain "This is how MVC web frameworks work in theory, and why they are so great."
As of this writing, there are very few true MVC frameworks written in PHP. In fact, there is only one that I know of, Solar, that is entirely pure PHP 5 code. Another one out there is Cake, which is trying to be the "Ruby on Rails of PHP." I, personally, have a few problems with both of these frameworks. Both Solar and Cake fail to leverage existing code in PEAR, Smarty, etc. Cake appears a bit disorganized at the moment. Finally, Solar is the work of mostly a single person (not that Paul isn't a great coder or person, but there is only a single gatekeeper at the time of this writing). These may not be issues that concern you, and if they don't concern you, by all means check these two out.
The Old Way
If I could go back in time and look at code I wrote in early 2001, I would find a file called template.txt that looked something like:
Oh man, just looking at this code makes me cringe now. The idea with this approach was that every application fit into this set approach and I could just copy template.txt to myapp.php, change some of the variables, and then voila, it would work. However, this top-down approach has some serious flaws.
require_once('config.php'); // Other requires, DB info, etc.
$APP_DB = 'mydb';
$APP_REQUIRE_LOGIN = false; // Set to true if script requires login
$APP_TEMPLATE_FILE = 'foo.php'; // Smarty template
$APP_TITLE = 'My Application';
if ($APP_REQUIRE_LOGIN == true) {
if (!isset($_SESSION['userID'])) {
header("Location: /path/to/login.php");
exit();
}
}
$db = DB::connect('mysql://'.$DB_USER.':'.$DB_PASS.'@localhost/'.$APP_DB);
if (!PEAR::isError($db)) {
$db->setFetchMode(DB_FETCHMODE_ASSOC);
} else {
die($db->getMessage());
}
// Put your logic here
// Output the template
include_once(APP_TEMPLATE_PATH.'/header.php');
include_once(APP_TEMPLATE_PATH.'/'.$APP_TEMPLATE_FILE);
include_once(APP_TEMPLATE_PATH.'/footer.php');
?>
1. What if my boss wanted me to change myapp.php to output a PDF in some cases, HTML in others, and SOAP if the request posted XML directly?
2. What if this app required IMAP or LDAP authentication?
3. How would I go about handling various modes in the script (including edit, update, and delete)?
4. How would I handle multi-level authentication (admin versus non-admin)?
5. How would I turn on output caching?
The New Way
By bringing everything into an MVC framework, I could make my life a lot easier. Compare the following code:
Notice that this code has no apparent concern with connecting to a database, verifying the user is logged in, or outputting anything. The controller handles all of this.If I wanted to authenticate against LDAP, I could create FR_Auth_LDAP. The controller could recognize certain output methods (such as $_GET['output']) and switch to the PDF or SOAP presenter on the fly. The event handler, delete, handles only deleting and nothing else. Because the module has an instance of the FR_User class, it's easy to check which groups that user is in, etc. Smarty, the template engine, handles caching, of course, but the controller could also handle some caching.
Switching from the old way to the MVC way of doing things can be a completely foreign concept to some people, but once you have switched, it's hard to go back. I know I won't be leaving the comforts of my MVC framework anytime soon.
Creating the Foundation
I'm a huge fan of PEAR and the PEAR_Error class. PHP 5 introduced a new class, Exception, which is almost a drop-in replacement for PEAR_Error. However, PEAR_Error has a few extra features that make it a more robust solution than Exception. As a result, the framework and foundation classes will use the PEAR_Error class for error handling. I will use Exception, however, to throw errors from the constructors, as they cannot return errors.
The design goals of the foundation classes are:
- Leverage PEAR to quickly add features to the foundation classes.
- Create small, reusable abstract foundation classes that will enable developers to build applications quickly within the framework.
- Document all foundation classes using phpDocumentor tags.
- Prepend all classes and global variables with FR to avoid possible variable/class/function collisions.
The class hierarchy will look something like this:
- FR_Object will provide the basic features that all objects need (including logging, generic setFrom(), toArray()).
- FR_Object_DB is a thin layer to provide child classes a database connection, along with other functions such classes might need.
- FR_Object_Web is a thin layer that provides session and user information for web-based applications.
- FR_Module is the base class for all applications (AKA "modules," "model," etc.) built in the framework.
- FR_Auth is the base authentication class, which will allow for multiple authentication mechanisms.
- FR_Auth_User is an authentication class to use in modules that require a valid, logged-in user.
- FR_Auth_No is a dummy authentication class used for modules that require no authentication.
- FR_Presenter is the base presentation class (the view) that will handle loading and displaying the applications after they have run.
- FR_Presenter_smarty: the presentation layer will include the ability to load different drivers. Smarty is a great template class that has built in caching and an active community.
- FR_Presenter_debug is a debugging presentation layer that developers can use to debug applications.
- FR_Presenter_rest is a REST presentation layer that developers can use to output applications in XML.
Looking at the foundation classes, you can start to see the separate parts of the MVC framework. FR_Module provides for the basic needs of all of the modules (Model), FR_Presenter provides a way to display applications arbitrarily in different formats (Views). In the next part of this series I will create the controller, which will bring all of our foundation classes together in a single place.
Coding Standards
Before you start coding a cohesive framework, you might want to sit down with your team (or yourself) and talk about coding standards. The whole idea of MVC programming revolves around reusable and standardized code. I recommend talking about, at least:
* What are your coding standards regarding variable naming and indentation? Don't start a holy war, but hammer out the basics and stick to them, especially when it comes to your foundation classes.
* Decide on a standard prefix for your functions, classes, and global variables. Unfortunately, PHP does not support namespaces. As a result, it might be a good idea to prepend your variables to avoid name collisions and confusion. Throughout this article, I've prepended my global variables, functions and classes with FR_, so as to distinguish core foundation code from simple application code.
* I highly recommend using phpDocumentor to document your code as you actually code it. I will document all of the core foundation classes as well as my initial applications in this article. At my own place of employment, I run phpDocumentor via a cron job to compile documentation frequently from my code repository.
Coding the Foundation
With all of that theory out of the way, here are the foundation classes. Be sure to read the comments for my reasonings, ideas, and implementation details. I'm presenting a combination of things I've done in the past that work for me, and the results of a few years of trial and error. By no means is this the only way to program an MVC framework, but I think it provides a good overview of how things should work.
Filesystem Layout
The basic layout is simple and somewhat strictly defined. There is a directory for includes, which will follow a specific pattern to make it easy to use PHP's new __autoload() function. Another directory is for modules, which will have their own layout. The hierarchy looks like:
* /
o config.php
o index.php
o includes/
# No.php
# User.php
+ Module.php
+ Object.php
+ Object/
# DB.php
+ Presenter.php
+ Presenter/
# common.php
# debug.php
# smarty.php
+ Smarty/
o modules/
+ example/
# config.php
# example.php
# tpl/
* example.tpl
o tpl/
+ default/
+ cache/
+ config/
+ templates/
# templates_c/
You're probably thinking that's a lot of code! It is, but you'll get through it. At the end of this article and the series, you'll see that MVC programming will make your life a lot easier and speed up development time.
In the filesystem structure, all of the foundation classes live inside of includes/. The example laid out a sample module, as well. Each module has its own configuration file, at least one module file, and one template file. All modules reside in modules/. I've become accustomed to wrapping my modules in an outer-page template, which is what the tpl/ directory is for. Each "theme" or template group has its own template directory. For now, I'm going to use default/ as my outer-page template. Later I'll show how to create a presentation layer for modules that want to render themselves.
config.php
config.php provides a centralized location for global configuration variables, such as the DSN and log file location. Also, notice that I dynamically figure out the installation location on the file system. This will make installing and migrating your code simple if you use FR_BASE_PATH in your own code.
index.php
This is the controller. I will cover this in depth in the next article.
Object.php
This is the base class for all of the foundation classes. It provides some basic features that most, if not all, classes will need. Additionally, the child class FR_Object_DB extends this object and provides a database connection.
The idea is that, by having all children extend from a central object, all of the foundation classes will share certain characteristics. You could put the database connection directly into FR_Object, but not all classes need a database connection. I will talk about FR_Object_DB later.
Auth.php
* @package Framework
*/
abstract class FR_Object
{
/**
* $log
*
* @var mixed $log Instance of PEAR Log
*/
protected $log;
/**
* $me
*
* @var mixed $me Instance of ReflectionClass
*/
protected $me;
/**
* __construct
*
* @author Joe Stump
* @access public
*/
public function __construct()
{
$this->log = Log::factory('file',FR_LOG_FILE);
$this->me = new ReflectionClass($this);
}
/**
* setFrom
*
* @author Joe Stump
* @access public
* @param mixed $data Array of variables to assign to instance
* @return void
*/
public function setFrom($data)
{
if (is_array($data) && count($data)) {
$valid = get_class_vars(get_class($this));
foreach ($valid as $var => $val) {
if (isset($data[$var])) {
$this->$var = $data[$var];
}
}
}
}
/**
* toArray
*
* @author Joe Stump
* @access public
* @return mixed Array of member variables keyed by variable name
*/
public function toArray()
{
$defaults = $this->me->getDefaultProperties();
$return = array();
foreach ($defaults as $var => $val) {
if ($this->$var instanceof FR_Object) {
$return[$var] = $this->$var->toArray();
} else {
$return[$var] = $this->$var;
}
}
return $return;
}
/**
* __destruct
*
* @author Joe Stump
* @access public
* @return void
*/
public function __destruct()
{
if ($this->log instanceof Log) {
$this->log->close();
}
}
}
?>
This is the base class for authentication. It extends from the FR_Module class from Module.php. Its major function is to define how a basic authentication class should behave.
An alternate approach is to define this as a variable in the module and then have the controller create the authentication module through a factory pattern. However, this way works too (and is simpler to explain).
Child classes should override the authenticate() method. The controller will use this method when determining if a user has access to the given module. For instance, the FR_Auth_No class simply returns true, which allows you to create modules that require no authentication.
Module.php
This is the heart of all of the modules. It extends the FR_Object_DB class and provides all of its children with database access and an open log file.
Additionally, it defines the default presentation layer, the default template file for the module, default page template file, and a few other variables that the controller and presentation layer use.
The class also provides the basic structure of what each module must possess as far as functions. The function set() abstracts the method of setting data into a centralized place, which the getData() function then hands off to the presentation layer for rendering.
* @var string $presenter
* @see FR_Presenter, FR_Presenter_common, FR_Presenter_smarty
*/
public $presenter = 'smarty';
/**
* $data
*
* Data set by the module that will eventually be passed to the view.
*
* @author Joe Stump
* @var mixed $data Module data
* @see FR_Module::set(), FR_Module::getData()
*/
protected $data = array();
/**
* $name
*
* @author Joe Stump
* @var string $name Name of module class
*/
public $name;
/**
* $tplFile
*
* @author Joe Stump
* @var string $tplFile Name of template file
* @see FR_Presenter_smarty
*/
public $tplFile;
/**
* $moduleName
*
* @author Joe Stump
* @var string $moduleName Name of requested module
* @see FR_Presenter_smarty
*/
public $moduleName = null;
/**
* $pageTemplateFile
*
* @author Joe Stump
* @var string $pageTemplateFile Name of outer page template
*/
public $pageTemplateFile = null;
// }}}
// {{{ __construct()
/**
* __construct
*
* @author Joe Stump
*/
public function __construct()
{
parent::__construct();
$this->name = $this->me->getName();
$this->tplFile = $this->name.'.tpl';
}
// }}}
// {{{ __default()
/**
* __default
*
* This function is ran by the controller if an event is not specified
* in the user's request.
*
* @author Joe Stump
*/
abstract public function __default();
// }}}
// {{{ set($var,$val)
/**
* set
*
* Set data for your module. This will eventually be passed toe the
* presenter class via FR_Module::getData().
*
* @author Joe Stump
* @param string $var Name of variable
* @param mixed $val Value of variable
* @return void
* @see FR_Module::getData()
*/
protected function set($var,$val) {
$this->data[$var] = $val;
}
// }}}
// {{{ getData()
/**
* getData
*
* Returns module's data.
*
* @author Joe Stump
* @return mixed
* @see FR_Presenter_common
*/
public function getData()
{
return $this->data;
}
// }}}
// {{{ isValid($module)
/**
* isValid
*
* Determines if $module is a valid framework module. This is used by
* the controller to determine if the module fits into our framework's
* mold. If it extends from both FR_Module and FR_Auth then it should be
* good to run.
*
* @author Joe Stump
* @static
* @param mixed $module
* @return bool
*/
public static function isValid($module)
{
return (is_object($module) &&
$module instanceof FR_Module &&
$module instanceof FR_Auth);
}
// }}}
// {{{ __destruct()
public function __destruct()
{
parent::__destruct();
}
// }}}
}
?>
Presenter.php
This is the foundation for the presentation layer. It uses the factory design pattern to create the presentation layer. FR_Module::$presenter defines which presentation layer to use, which the controller will then create via the factory method. Once the controller has a valid presentation layer, the only thing left to do is to run the common display() function, which presentation classes inherit from FR_Presenter_common.
* @access public
* @param string $type Presentation type (our view)
* @param mixed $module Our module, which the presenter will display
* @return mixed PEAR_Error on failure or a valid presenter
* @static
*/
static public function factory($type,FR_Module $module)
{
$file = FR_BASE_PATH.'/includes/Presenter/'.$type.'.php';
if (include($file)) {
$class = 'FR_Presenter_'.$type;
if (class_exists($class)) {
$presenter = new $class($module);
if ($presenter instanceof FR_Presenter_common) {
return $presenter;
}
return PEAR::raiseError('Invalid presentation class: '.$type);
}
return PEAR::raiseError('Presentation class not found: '.$type);
}
return PEAR::raiseError('Presenter file not found: '.$type);
}
// }}}
}
?>
TO BE ADDED MORE......
No comments:
Post a Comment