PHP DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Migrating to Page Controllers
Pages: 1, 2

Migrating to a Page Controller Setup

Converting mixed logic/view pages to their Page Controller equivalents is straightforward. The migration is transparent to users because the original page's URI doesn't need to change unless you want it to.

This section uses a sample page to demonstrate the migration process. Both the "before" and "after" versions (old.zip and new.zip, respectively) are available for download. Feel free to review the files and follow along:

Related Reading

Learning PHP 5
By David Sklar

  1. Draw a map. Draft a flowchart that maps the page's execution into logic and presentation content. Make note of which logic triggers a given set of content, and what dynamic data that content uses.

    The sample code uses the flowchart from Figure 1. The controller (new.php) passes objects generated from database query results (Lines 56-75, then 82) and error messages (Lines 28 and 38) to the views.

  2. Create the views. Extract presentation content into separate pages. The sample code's views are the files new-DBConnectError.php, new-NoResults.php, new-SQLError.php, and new-Success.php. (The view pages may have duplicate content, such as menu bars and footers. Later in the article I offer one solution to this.)

    The remaining code will become the controller. Leaving the controller at the old page's URI makes for a seamless transition, as old links to that URL will continue to work.

    Notice that the sample pages have no logic for error handling. That should take place within the controller itself, which can dispatch to the appropriate error page. (If you haven't done so already, set display_errors = 0 in php.ini to avoid interleaving PHP's code-level error messages with your view pages' content.) View pages should have just enough logic to format the data that the controller provides.

  3. Formalize the controller. Rework the remaining code (the controller) to dispatch to the view pages based on the results of each decision. Place data in a variable that both the controller and views can access.

    The controller in the sample app puts all of the data in the array $viewData. The view pages will call this same variable.

  4. Update the views. Rework the view pages to pull data from variables assigned in the controller.

    For example, note how the new-Success.php page iterates through the array $viewData['CustomerList'] (Lines 33-45). The error pages look for a message under $viewData['ErrorMessage'].

Follow a similar process when designing a new app with Page Controllers in mind.

Growth and Scalability

Converting a large, dynamic web application may leave you with tens or hundreds of Page Controllers. These can be pockets of stability in a larger mess unless you take a long-range view of your design. Consider the following ideas to help scale your application accordingly:

Design controllers to fetch URIs from an app-wide map instead of using direct file paths. That makes the app more adaptable to changes in file structure and makes it easier to debug dispatches to nonexistent pages. The "map" could even be a custom object that returns a predefined error page when someone requests a nonexistent alias.

Identify and eliminate duplicate pages. While the "success" page is closely tied to a specific controller, chances are the error pages are very similar. (The sample code's error pages are near clones of one another.) You can reduce your overall page count by sharing a generic error page that looks for a message in a predefined variable.

Don't stop refactoring now. Converting an existing Model 1 app to use Page Controllers will uncover code duplication. The sample app, for example, could encapsulate its data access in a Data Access Object (DAO) or DataObject.

You can also extract your business processes into objects or function libraries, whittling down your controllers to the bare minimum of code:

<?php

// ... any include() or require() calls ...
$busObj = new BusObj( ... ) ;
$result = ${busObj}->exec( ... ) ;

if( defined( $result ) )
{
  $viewData[ 'Result' ] = $result ;
  include( 'success.php' ) ;
}
else
{
  $viewData[ 'ErrorMessage' ] = "No result" ;
  include( 'error.php' ) ;
}

?>

This level of separation lets you put other faces on your business processing. (Think fat-client GUI or web services.)

Mixing Page Controllers and Front Controller

Those of you who have read my previous article on the Front Controller pattern may wonder when to use that instead of Page Controller. You can (and probably should) use both.

The two patterns coexist peacefully within the same application because they hold different, yet complementary, responsibilities. The Front Controller specifies where to go (the page to fetch for the requested URI), while a Page Controller decides what to do (the action to perform).

Furthermore, they work without the knowledge of one another. The Front Controller doesn't realize it's dispatching to a Page Controller, and the Page Controller doesn't know what called it. To integrate the two, map a Page Controller to one of the Front Controller's target URIs.

One reason to use a Front Controller with a Page Controller is to minimize the duplicated content in the extracted view pages. The Front Controller can set up common menu bars, footers, and so on, while the Page Controller will handle the specific request.

That's a Wrap

Migrating all-in-one pages to Page Controllers is the first step in a top-down refactoring of your web application. Your end users will notice that the app is more stable and that it takes you less time to implement new features. The clear separation between logic and display will make it easier for you to fix bugs and make changes.

Page Controller is a small-scale pattern that can apply to several places within an application. Combine it with large-scale patterns such as the Front Controller to keep your design clean both from far away and up close.

Ethan would like to thank "Mr .NET the SB" for reviewing and improving this article.

Resources

  • Download the sample code. It will likely require changes to work on your system, as it uses PostgreSQL-specific database calls. (I opted against PEAR or other abstraction layers in order to keep the code focused on the matter at hand.)
  • Patterns of Enterprise Application Architecture (Fowler) includes a section on the Page Controller pattern.
  • Core J2EE Patterns (Alur, Crupi, Malks) describes the Service to Worker pattern. This twist on the traditional Page Controller uses a separate Dispatcher object that determines the view and passes control to it.
  • Refactoring (Fowler) covers the reasoning behind the practice in addition to several pattern-style refactorings.
  • Java's RequestDispatcher class is the key ingredient to a servlet+JSP Page Controller, which was the model for my PHP implementation thereof.
  • I described the Front Controller pattern in a previous article. That article includes a sample PHP Front Controller implementation.

Q Ethan McCallum grew from curious child to curious adult, turning his passion for technology into a career.


Return to the PHP DevCenter


Valuable Online Certification Training

Online Certification for Your Career
Earn a Certificate for Professional Development from the University of Illinois Office of Continuing Education upon completion of each online certificate program.

PHP/SQL Programming Certificate — The PHP/SQL Programming Certificate series is comprised of four courses covering beginning to advanced PHP programming, beginning to advanced database programming using the SQL language, database theory, and integrated Web 2.0 programming using PHP and SQL on the Unix/Linux mySQL platform.

Enroll today!


Sponsored by: