Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

MVC-with-PHP

VIEWS: 509 PAGES: 9

									FEATURES

An Introduction to MVC Using PHP

FEATURES

By Jason E. Sweat
Few design patterns are as 'tried and true' as the Model View Controller pattern (MVC). In this first part of a two part series, we'll go over the general philosophy of MVC and why it is useful; what resources are available to help the budding MVC coder; and we'll wrap things up with a little code to drive the concepts home. What is MVC? By now, you have probably heard the term MVC - an acronym for Model-View-Controller. You may also have heard that this design pattern is a good architecture for a web site. Just what MVC is, and how this concept can be applied in PHP, is what I hope to uncover in this article. MVC is an Object Oriented (OO) design pattern. A Model-View-Controller uses classes to organize Business Logic (where data is stored, who is authorized to manipulate it, and how the data is manipulated) contained in “Models”, Presentation Logic (how the data from the Models should be rendered) in “Views” and has an overall flow for the application within a “Controller”. I was amazed to find that the MVC pattern was developed at Xerox Palo Alto Research Center (PARC) in the late seventies. In addition to the mouse and the concept of a windowed GUI, we also owe thanks to these creative folks for the Model-View-Controller! Why Use Design Patterns Anyway? A “Design Pattern” is a collection of objects, and relationships between those objects, that has been developed and tested to efficiently solve a particular class of problems. If the problem you are solving matches the problem solved by a design pattern, then you can get a significant head start on implementing your solution by adopting the design pattern. You will still need to customize the portions of the code for your system, but you will have a well tested, robust architecture in place as a base for what you code. There are many different design patterns to solve a variety of problems. The “bible” of design pattern information is the book titled “Design Patterns” by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (often called the “Gang of Four”). A good starting point for the investigation of PHP design patterns is the ::phpPatterns() site (http://www.phppatterns.com/). Why use MVC? Now that you have been primed with a high-level overview of the MVC, and understand that it is a design pattern, you may still wonder why you would want to use it with PHP. The reason is that the MVC pattern is REQUIREMENTS PHP Version: 4.0.6+ O/S: Any Additional Software: Smarty

May 2003 · PHP Architect · www.phparch.com

10

FEATURES
a solution for a problem that web sites map to very well. Each page hit is a user interaction (input) with your system (the web server processing the PHP scripts). Assuming you have some kind of need to maintain the state of your data between page hits (using a persistent data store - usually a database or files), and you intend to present this information in a variety of ways to the user, then the MVC pattern may be the architecture you want to use for your application. The MVC pattern addresses three problems in architecting a solution:

An Introduction to MVC Using PHP
• http://java.sun.com/blueprints/patterns/MVC .html • http://java.sun.com/blueprints/guidelines/d esigning_enterprise_applications_2e/webtier/web-tier5.html • http://developer.java.sun.com/developer/onl ineTraining/JSPIntro/contents.html#JSPIntro 4.

1. Maintaining your data in some kind of persistent storage 2. Maintaining the logic controlling the flow of the application, what to display to the user, and what actions the user is allowed to perform on the data in your application. 3. Presenting information to users of your application Each of the three major components of the MVC is closely associated with one of these goals, as depicted below in figure 1. There are a number of resources available on the web to help you start learning about the MVC pattern. Java is a more mature language for enterprise class development, and PHP developers should feel free to borrow from the lessons learned by Java developers. Some good starting points come from the Sun J2EE specification; specifically, I recommend reviewing: Figure 1

Another set of terms you will hear when researching Web Based MVC applications is “Model 1” or “Model 2”. These are left over from the JSP/J2EE specification definitions of a MVC. The “Model 1” style basically means you have classes to encapsulate the Models in your system, but the Views are spread out in different pages, without the flow being guided by the Controller. The “Model 2” version is the recommended approach, with a Controller object in charge of application flow. It is now recommended that all MVC systems be implemented as a “Model 2” controller. Note: Implementing this in PHP implies that your application will have a single PHP script, using URL or Post parameters to generate the different views in your application. While this works well for your coding structure, older search engine applications generally ignore anything after the “?” in a URL, necessitating the use of mod_rewrite, or some other strategy, if making your site search engine friendly is a requirement.

There is one more non-PHP project I would like to introduce to you before taking a closer look at the components of the Model-View-Controller. The Apache Jakarta Project (http://jakarta.apache.org/) is a highprofile collection of java projects. The MVC for Jakarta is called Struts (http://jakarta.apache.org/struts/). As you research the subject of Model-View-Controllers, it quickly becomes apparent that Struts is the “Gold Standard” of MVC implementations. Now that you know what a design pattern is, and why MVC is well suited to web applications, let’s have a closer look at the components of a MVC application. The Model Models are the portion of the MVC application that implement the “Business Logic”. Business logic is any PHP logic relating to how information is stored in your application. The name Model comes from that fact that these objects emulate things from the real world, both rules and data. Models are the only component of your system that should interact with your persistent data store

May 2003 · PHP Architect · www.phparch.com

11

FEATURES
(fancy words for anything that stores and retrieves data between web requests). Models can implement any of the technologies that make PHP such a great language for web development: databases calls, file manipulation, session management, web services, and anything else that stores data for your application. A well designed model could be used for other applications on your web server. When you start to see the power of encapsulating your business logic with these classes, it becomes clear that making good models is a very useful convention, even if you are not using the full MVC architecture. For models that interact with a database table, a single table should ideally be associated with only one model class. This helps you to achieve the first goal we listed for benefits of the MVC: maintaining your data in some kind of persistent data store. By just adhering to the MVC framework, you only have to review the models in your system if the database table changed. By adopting this strategy, you would only have a single class in the model to review. Examples of typical models in web applications might be the user, the browser, a shopping cart, catalog items, orders, topics or articles. I find that for databasedriven web applications, models tend to be associated with a single table, or with a small group of tightly coupled tables (perhaps a view?). The name for your model class is likely to be similar to the name for your database table, assuming you have a reasonable naming convention in place. I also tend to write model classes for features on the site. An example of this could be a multi-page form, which may have a “wizard” model associated with it. The View Views are the portion of the MVC application that present output to the user. The most common output for PHP web applications would be HTML, but views are not restricted to this. Your MVC application might output XML, WML, plain text, images, email or some other content. PHP has a variety of capabilities to use in views, ranging from just being designed to send data back to the browser, to implementing template solutions, to XSLT. Views generally create instances of models, and use methods of these models to get data to present to the user. Views contain all of the PHP code required to transform the raw data from your models into the format you want to present in your application. For example, in the April issue of php|architect, I published an article covering the use of PHP for graphing. This kind of graph generation code would belong to views in a MVC. The Controller The controller is the heart of the MVC application. This component is the object that should be aware of the
May 2003 · PHP Architect · www.phparch.com

An Introduction to MVC Using PHP user’s HTTP request (alternatively, the controller might be aware of the command line arguments or last input in a CLI). From this information, the controller should determine what view to display, or what other application-related action should take place. Controllers are generally the core component of any MVC project you may adopt (primarily because models and views must be customized to your application by their very nature). Various projects have different strategies for determining how the views and models interact. How these objects interact is referred to as system coupling. If the nature of these relationships is defined using a configuration file (often XML based), this is said to be “loose coupling”. This is the opposite of hard coding application flow into your system. To summarize, figure 1b depicts figure 1 with the appropriate PHP technologies added to each components sphere of influence. Will there be any crossover of these technologies in real world applications? Probably. In fact, one example of this is depicted in figure 1b. If your application contained a “User” model and you have a “remember me” feature, you might want the User Model to send a cookie to the client’s browser. This is technically done via HTTP, a task which we assigned to the controller. I have also had cases where a view might be displaying a particular instance of a model, and which one to display is passed as a parameter of the URL. As a result, the view might access this information using the $_GET superglobal (again HTTP bleeding in from the controller to the view). Figure 1b

Where you should not see any crossover of these technologies is between the views and the models. You

12

FEATURES
should never have any HTML in a model, and you should never have any database access functions in a view. Having either of these would be a violation of your principal of separation of content from format, breaking your MVC model and limiting the flexibility of your application. PHP based MVC Projects There are quite a number of PHP based projects, and other resources to help you evaluate the MVC design pattern (Table 1). Several projects I have located are listed here. If I have inadvertently omitted an MVC project you are familiar with, please post a follow up in the php|architect forum for this article. Taking Concept to Practice In my experience with the MVC pattern for PHP web applications, there are two general “flow paths” through the MVC application. The first flow path is that of the user requesting a particular view within the Figure 2

An Introduction to MVC Using PHP application. The second typical flow path is that of the user sending inputs intended to change the state of the models in the application. Figure 2 below represents the “request to view” flow path through the application. The user’s request (generally an HTTP GET request from a browser) is interpreted by the controller in step 1. The controller determines which view is appropriate for this request. Application flow is turned over to the view in step 2. Generally views will need data to display the the response. This information will come from the application Models. Step 3 is the view requesting data from the model. The model is the only component in the application that will have PHP code that accesses a persistent data store. Steps 4 and 5 are the models request and retrieval of data from the data store. The model then returns the data in step 6. If the view needs additional data from this or another model in the application, steps 3 to 6 would be repeated as necessary. Then the view formats the output and trans-

Table 1: PHP based projects

Project Name / URL
Ambivalence http://amb.sourceforge.net/ Eocene http://eocene.net/ php.MVC http://www.phpmvc.net/ phpPatterns() MVC2 http://www.phppatterns.com/index.php/article/artic leview/19/1/1/ Phrame http://phrame.itsd.ttu.edu/ The catchall: google for it http://www.google.com/
May 2003 · PHP Architect · www.phparch.com

Notes
This project is a port of the java project called Maverick (http://mav.sourceforge.net/), which originated as an attempt to simplify Struts. An MVC framework with a goal to port to ASP.NET. A port of the java-based Jakarta Struts framework (http://jakarta.apache.org/struts/) The second article on this site about the MVC pattern. Another port of the Struts framework. Searching for "php mvc" yields many results.

13

FEATURES
mits it to the user as output in the step 7 response. The other common flow path through an MVC web application is an update to data in the application by a user. This flow path is depicted in figure 3. The request enters the controller at step 1 (this time, most likely an HTTP POST from the user’s browser). The controller decides what action is appropriate, and calls a method of the model object with appropriate parameters to reflect the user’s request in step 2. The model would determine if the request is valid based on the business logic you code if the request is valid. If so, the model would alter the data in the store in steps 3 and 4. The model would then turn control back to the controller in step 5. The controller would determine where the user should be directed next, and issue a redirect to that location in step 7. While this is the end of this request (PHP should actually exit after issuing the header() command), you can think of a conceptual step 8, which most likely is another request to the application to display a particular view.

An Introduction to MVC Using PHP A Simple MVC Application Now you have enough general background on ModelView-Controllers to walk through a specific example. In this example I chose to implement the MVC in the framework from the Phrame project. Phrame is a PHP port of the Jakarta Struts MVC implementation, which I indicated earlier was the “Gold Standard” of MVCs. Please do not consider me a heretic, but I also liked the fact that Phrame’s configuration was done with a PHP array rather than an XML file. In Phrame, as in Struts, loose coupling of the models and views is accomplished through the addition of three more classes, Actions, Forms and Forwards. Each time the controller is initiated, it will create an instance of an Action class you extend from the framework base Action. The controller will call the Process() method of your Action with a list of Forwards, and a Form object containing all the HTTP request variables. The expected result of your Action is to determine the proper Forward object to return to the controller. The controller will then process the Forward object and exit PHP processing for that request. This use of the Action object lends itself to an analogy between an HTTP request and a sentence. You can think of Actions as verbs, Models as nouns, and Views as adjectives in a sentence. That is, a particular web request will perform an Action (verb) on a Model (noun), or display a View which describes (adjective) a Model (noun). The analogy works best for the Actions and Models, and should be considered when selecting names for the objects. This example is a modified version of the “Hello World” example from the Phrame project (http://phrame.itsd.ttu.edu/). The source for Phrame can be downloaded from the project’s SourceForge page, http://sourceforge.net/projects/phrame. The

Note: Some people worry about the overhead caused by the redirect, specifically that it requires another round trip between the browser and the server before the page data is sent back. While this is the case, it is a minor performance consideration when compared to the benefit it enables: the user can hit “refresh” after posting data to your application, and they will not be prompted by their browser to “repost” the data (because the “last” operation they requested was to GET the redirected view).

Figure 3

May 2003 · PHP Architect · www.phparch.com

14

FEATURES
modifications I have made are to eliminate PHP warnings (In some cases I modified Phrame library files. For these files I renamed them *.jes.php and included the originals so you can easily identify the differences), restructuring to better use the advantages of the MVC design, and using Smarty templates for View formatting, rather than XSLT transformation for user output. The MVC application uses a “bootstrap” file, which is a single PHP script that acts as the focal point for the application. In this example, the bootstrap file is hello.php. With that in mind, let’s take a look at the hello.php file, which starts with the application setup: mvc_code01.php
error_reporting(E_ALL); define('PHRAME_LIB_PATH', '../../phrame/'); require_once require_once require_once require_once require_once require_once PHRAME_LIB_PATH.'include.jes.php'; 'Smarty.class.php'; 'MappingManager.php'; 'actions/HelloAction.php'; 'models/Person.php'; 'models/HelloErrors.php'; } }

An Introduction to MVC Using PHP “hello” application. mvc_code02.php
class HelloMap extends MappingManager { function HelloMap() { //set options $this->_SetOptions('handle_error'); //add application forms $this->_AddForm('helloForm', 'ActionForm'); //add application actions and forwards $this->_AddMapping('sayHello', 'HelloAction', APPL_VIEW.'index', 'helloForm'); $this->_AddForward('sayHello', 'index'); $this->_AddForward('sayHello', 'hello', APPL_VIEW.'hello');

define('APPL_VIEW', 'hello.php?'._VIEW.'='); define('APPL_ACTN', 'hello.php');

This code includes library files for Phrame and Smarty. Also included are the actions and models customized for this application. Two relative URLs are defined, one for displaying views in the application, and another for HTML Form element action.

"Models can implement any of the technologies that make PHP such a great language for web development"

All of our coding for the HelloMap class is performed in the constructor method. First you have the ability to override the typical Phrame options array. In this case, we define a function to use as an error handler when Phrame is working. Next any forms are identified. In this case, there was no need to extend the standard Phrame ActionForm class. Lastly, any actions and forwards from those actions are defined. In this case, the application has a single action (sayHello) with two forwards, “index” and “hello”. The parameters for the MappingManager::_AddMapping() method are: the name of the mapping, the class that implements the mapping, the default location where the action is called and, lastly, the form mapping associated with this action. The MappingManager::_AddForward() method takes parameters for the action mapping identifier it is associated with, an identifier for the forward, and, optionally, a URL to redirect to if not the default associated with the action. mvc_code03.php

In a Phrame application, the relationships between the Forms, Actions and Forwards are established in a PHP array. I found this array tedious to develop and maintain, and instead created a MappingManager class to maintain these relationships. This class is meant to be extended for each application developed using Phrame, and has methods that return arrays in the format Phrame uses. This class has the advantage of validating some of the relationships between the form and action mappings as you add them (instead of generating an error when you tried to use them in your application with the regular array configuration). Take a look at the MappingManager.php file included in the magazine download if you are interested in the internal workings of this class. Presented below is the HelloMap class, which extends the MappingManager for our
May 2003 · PHP Architect · www.phparch.com

session_start(); $smarty =& new Smarty; $map =& new HelloMap; $controller =& new ActionController($map->GetOptions()); $errors =& new HelloErrors; function handle_error($number, $message, $file, $line, $context) { appl_error($message); } function appl_error($psErrorMsg) { $errors =& new HelloErrors; $errors->AddError($psErrorMsg); }

15

FEATURES
The next step in this application is to start the session, and create instances of several objects we will be using. These include a Smarty template object, an instance of the HelloMap class we just defined, and a Phrame ActionController, which needs the options array returned from the MappingManager::GetOptions() method. This section of code also introduces you to how error handling is taking place in this application. There is a model for errors that encapsulates the actual implementation (which happens to be storing the errors in an array in the $_SESSION, see the HelloErrors.php file included in the download for details). There are also two functions defined. appl_error() allows you to add an error message to the application error object. handle_error() is a PHP error handler function that in turn calls the appl_error() function. This means you can use appl_error() directly, or use trigger_error() whenever the PHP error handling has been set to our handle_error() function. The rest of the application’s bootstrap file implements the controller component of the MVC. mvc_code04.php
if (array_key_exists(_ACTION,$_REQUEST)) { //release control to controller for //further processing $controller->Process($map->GetMappings(), $_REQUEST); } else { //determine and display view $requested_view = (array_key_exists(_VIEW, $_REQUEST)) ? strtolower($_GET[_VIEW]) : 'index'; switch ($requested_view) { case 'hello': $template = $requested_view.'.tpl'; //assign view specific data $person =& new Person; $smarty->Assign('name', $person->GetName()); break; case 'index': default: $template = 'index.tpl'; } //assign common data $smarty->Assign(array( 'appl_link' => APPL_ACTN ,'appl_view' => APPL_VIEW ,'action' => _ACTION )); //assign and clear errors $smarty->Assign('errors', $errors->GetErrors()); $smarty->Display($template); exit; }

An Introduction to MVC Using PHP requested, then a view will be displayed. The code in the ‘else’ statement determines the appropriate view, assigns view-specific data to the Smarty template, assigns common data to the Smarty template, processes any application errors, and then renders the template. With the bootstrap file out of the way, we can look at what we add to the application to enable processing with Phrame. The first thing to consider is what happens in our application if no parameters are passed at all. In this case, it will bypass the action processing and display the default view of index.tpl. Here is the essential portion of the index.tpl Smarty template. mvc_code05.php
<form action="{$appl_link}" method="post"> <div> <input type="hidden" name="{$action}" value="sayHello" /> {if $errors|@count gt 0} <ul> {section name=e loop=$errors} <li><b style="color: red">{$errors[e]}</b></li> {/section} </ul> {/if} What is your name?<br /> <input type="text" name="name" value="{$name}" /> <input type="submit" value="OK" /> </div> </form>

"Various projects have different strategies for determining how the views and models interact. How these objects interact is referred to as 'system coupling'."

It is important to examine what is going on here. There is an HTML form element that is setup to post back to the application script itself. There is a hidden input, named by the template variable $action with a value of “sayHello”. This is the input that directs the controller to initiate the “sayHello” mapping.

The ‘if’ statement determines whether or not an action was requested. If so, then the Phrame ActionController::Process() method is called to activate the framework. If an action is not specifically

Note: The input name was specified using the template variable $action, which was assigned the value of the constant _ACTION, which was defined in the Phrame library to be the index the controller uses to determine if an action was requested. By default, this value is “action”, but as a good coding habit, we do not hard code the value into our template.

May 2003 · PHP Architect · www.phparch.com

16

FEATURES
The next section of the template is a conditional check for errors. If they are present, they are displayed as an unordered list. The last portion of the template is the normally visible portion of the form with a text input for the users name and a submit button. mvc_ss1.png

An Introduction to MVC Using PHP return an ActionForward object to the controller to complete processing. Let’s examine the details of the HelloAction code. First of all, for performance reasons I have specified both of the objects passed to the form are by reference, and the function will return the ActionForward object by reference. Next, two instances of models are created, $person and $errors. $poActionForm is the Phrame ActionForm class instance created by the controller as specified in your mappings. We specified the class of our form to be ActionForm - the Phrame base form class itself - because we do not need to extend the form for any reason. The ActionForm class itself extends the Phrame utility HashMap class, and is “preloaded” with the $_REQUEST associative array. Therefore the $poActionForm->Get() method will retrieve a posted variable if it was posted. In this case, we want to evaluate the value of the text input “name” as the value the user submitted. Next, our action determines which ActionForward mapping to return by checking if it was successful in exercising the Person::SetName() method of our model. It also verifies no errors were triggered as a result of this action. If there were problems, it returns to the “index” view, otherwise, we are clear to show the “hello” view.

So, what happens when the user presses the submit button? The browser will submit an HTTP POST operation to the bootstrap script. The $_REQUEST[_ACTION] will contain the value “sayHello”. In response, the $controller will execute the Perform() method. Reviewing the mappings in our HelloMap class, “sayHello” is associated with the HelloAction class. The ActionController::Perform() method will create an instance of the HelloAction class and execute HelloAction::Perform(), so that is the next section of code to review. mvc_cod06.php
class HelloAction extends Action { function &Perform(&$poActionMapping, &$poActionForm) { $person =& new Person(); $errors =& new HelloErrors; $name = $poActionForm->Get('name'); //get ActionForward depending on if //errors were generated if ((!$person->SetName($name)) || ($errors->HasErrors())) { $actionForward = $poActionMapping->Get('index'); } else { $actionForward = $poActionMapping->Get('hello'); } return $actionForward; } }

"As you research the subject of ModeView-Controllers, it quickly becomes apparent that Struts is the 'Gold Standard' of MVC implementations."

Every action you need to perform in your application will look similar to this file. You will create a class that extends the Phrame Action class. You need to override the Perform() method in your extended class in order to implement an action. Lastly, your action needs to
May 2003 · PHP Architect · www.phparch.com

The last step on our tour is to look at the Person.php file. Models are unique to applications, and therefore do not even have a prototype in the Phrame library (they do not have to extend any Phrame classes). This model makes use of two constants, which we attempt to make sure are uniquely named by prefixing them with the model’s class name. Reviewing the internal details of our model, we find the name is stored in the php session. It is important to note that outside of this class, the details of this storage are not known, and you can change this implementation at any time within this class definition. Since all access to this session array index in our application is performed through this single model class, you can institute proper error handling, business logic (like validation rules), and enforcing the consistency of this data for your application. This all contributes to the MVC goal of better maintenance of the data in your persistent store.

17

FEATURES
Another area of interest in this model is the SetName() method. In particular, our “Business Logic” dictates that a name must be less than 20 characters. If this method is called with an out of range value, an error will be triggered.

An Introduction to MVC Using PHP Most of the model classes I have developed for real world applications deal with databases. This would probably be your next step in developing a more useful implementation. You might also consider how the overall framework interacts. There are two basic facets of the Phrame project I have altered in most of my implementations. First of all I implemented a “default action” to show a view. Secondly, in this Phrame example, the code presented to display views is not implemented in OO fashion. I use the Factory Pattern within the default “ShowView” action to create instances of application views. Another issue with this example is a small inefficiency in inclusion of files: the action class is required for every request, even if the result of the particular request would be to display a view. You might reconfigure your application to be more aware of this context and only include appropriate resources. In Review: We looked at the benefits of implementing PHP scripts in a MVC framework. We have reviewed an example of a model using the session for persistent data storage, how the HelloMap and the Phrame ActionController allow you to control the flow of the application, and how Smarty templates can be used to implement views in your application. This example was very simple, but shows processing user input, data validation, error handling and application flow. Having reviewed this example, you now possess the fundamental building blocks for deploying your own MVC based web application. One benefit people would like to achieve by adopting a coding framework is application development speed. I believe that speed comes with familiarity, and a more immediate benefit is a robust architecture, providing for your deploying a flexible and manageable web application. I hope you have found this exposure to the MVC pattern useful, and you might consider working with the MVC design pattern for your next interactive web application.

Future Directions: How does one go about expanding this Phrame based MVC application? If you need an additional view of the data, you would simply add a new template and extend the case statement for determining valid views in the bootstrap file. To add a new action, you would need to create a new class that extends the Action class, similar to the presented HelloAction class. You would also need to add a mapping in the constructor of the HelloMap class so the MVC knows which class to instantiate when your new action is requested. Finally, you need to add forwards to appropriate views when the action completes.
May 2003 · PHP Architect · www.phparch.com

About The Author

?>

Jason has been an IT professional for over ten years. He is currently an application developer and intranet webmaster for a Fortune 100 company. He has written several tutorials and articles for the Zend website, and has recently contributed to the Wrox “PHP Graphics” handbook. He resides in Iowa with his wife and two children. Jason can be contacted at jsweat_php@yahoo.com.

Click HERE To Discuss This Article
https://www.phparch.com/discuss/viewforum.php?f=19

18


								
To top