PHruts

4. Building Controller Components

4.1 Overview

Now that we understand how to construct the Model and View components of your application, it is time to focus on the Controller components. PHruts includes a service that implements the primary function of mapping a request URI to a PHRUTS_Action class. Therefore, your primary responsibilities related to the Controller are:

To deploy your application, you will also need to:

The latter two items are covered in the Configure Applications chapter.

4.2 The ActionService

For those of you familiar with MVC architecture, the ActionService represents the C - the controller. The job of the controller is to:

The PHruts controller delegates most of this grunt work to Action classes.

In addition to being the controller for your application, the ActionService instance also is responsible for initialization and clean-up of resources. When the controller initializes, it first loads the application config corresponding to the "config" init-param. It then goes through an enumeration of all init-param elements, looking for those elements who's name starts with config/. For each of these elements, PHruts loads the configuration file specified by the value of that init-param, and assigns a "prefix" value to that module's ModuleConfig instance consisting of the piece of the init-param name following "config/". For example, the module prefix specified by the init-param config/foo would be "foo". This is important to know, since this is how the controller determines which module will be given control of processing the request. To access the module foo, you would use a URL like:

http://localhost:80/myApp/index.php?do=/foo/someAction

For each request made of the controller, the method process(PHRUTS_HttpServiceRequest, PHRUTS_HttpServiceResponse) will be called. This method simply determines which module should service the request and then invokes that module's RequestProcessor's process method, passing the same request and response.

4.3 Request Processor

The RequestProcessor is where the majority of the core processing occurs for each request. Let's take a look at the helper functions the process method invokes in-turn:

4.4 ActionForm Classes

An ActionForm represents an HTML form that the user interacts with over one or more pages. You will provide properties to hold the state of the form with getters and setters to access them. ActionForms can be stored in either the session (default) or request scopes. If they're in the session it's important to implement the form's reset method to initialize the form before each use. PHruts sets the ActionForm's properties from the request parameters and sends the validated form to the appropriate Action's execute method.

When you code your PHRUTS_ActionForm beans, keep the following principles in mind:

4.5 Action Classes

The PHRUTS_Action class defines the method:

/**
 * @param PHRUTS_ActionConfig $mapping
 * @param PHRUTS_ActionForm $form
 * @param PHRUTS_HttpServiceRequest $request
 * @param PHRUTS_HttpServiceResponse $response
 * @return PHRUTS_ActionForward
 * @throws Exception
 */
 public function execute(PHRUTS_ActionConfig $mapping,
                         $form,
                         PHRUTS_HttpServiceRequest $request,
                         PHRUTS_HttpServiceResponse $response);

The goal of a PHRUTS_Action class is to process a request, via its execute method, and return a PHRUTS_ActionForward object that identifies where control should be forwarded (e.g. a PHP page or another Action) to provide the appropriate response. In the MVC/Model 2 design pattern, a typical PHRUTS_Action class will often implement logic like the following in its execute method:

It is wise to avoid creating lengthy and complex Action classes. If you start to embed too much logic in the PHRUTS_Action class itself, you will begin to find the PHRUTS_Action class hard to understand, maintain, and impossible to reuse. Rather than creating overly complex Action classes, it is generally a good practice to move most of the persistence, and "business logic" to a separate application layer. When an Action class becomes lengthy and procedural, it may be a good time to refactor your application architecture and move some of this logic to another conceptual layer; otherwise, you may be left with an inflexible application which can only be accessed in a web-application environment. PHruts should be viewed as simply the foundation for implementing MVC in your applications. PHruts provides you with a useful control layer, but it is not a fully featured platform for building MVC applications, soup to nuts.

4.6 PlugIn Classes

The PlugIn interface extends Action and so that applications can easily hook into the ActionService lifecycle. This interface defines two methods, init() and destroy(), which are called at application startup and shutdown, respectively. A common use of a Plugin Action is to configure or load application-specific data as the web application is starting up.

At runtime, any resource setup by init would be accessed by Actions or business tier classes. The PlugIn interface allows you to setup resources, but does not provide any special way to access them. Most often, the resource would be stored in application context, under a known key, where other components can find it.

PlugIns are configured using <plug-in> elements within the PHruts configuration file. See PlugIn Configuration for details.

4.7 The ActionConfig Implementation

In order to operate successfully, the PHruts controller service needs to know several things about how each request URI should be mapped to an appropriate PHRUTS_Action class. The required knowledge has been encapsulated in a PHP class named PHRUTS_ActionConfig, the most important properties are as follows:

4.8 Writing Action Mappings

How does the controller service learn about the mappings you want? It would be possible (but tedious) to write a small PHP class that simply instantiated new PHRUTS_ActionConfig instances, and called all of the appropriate setter methods. To make this process easier, PHruts uses the PHigester component to parse an XML-based description of the desired mappings and create the appropriate objects initialized to the appropriate default values.

The developer's responsibility is to create an XML file named phruts-config.xml and place it in the WEB-INF directory of your application. This chapter covers the configuration elements that you will typically write as part of developing your application. There are several other elements that can be placed in the phruts-config file to customize your application. See Configuring Applications for more about the other elements in the PHruts configuration file.

The outermost XML element must be <phruts-config>. Inside of the <phruts-config> element, there are three important elements that are used to describe your actions:

<form-beans>
This section contains your form bean definitions. Form beans are descriptors that are used to create ActionForm instances at runtime. You use a <form-bean> element for each form bean, which has the following important attributes:

<global-forwards>
This section contains your global forward definitions. Forwards are instances of the ActionForward class returned from an Action's execute method. These map logical names to specific resources (typically PHP pages), allowing you to change the resource without changing references to it throughout your application. You use a <forward> element for each forward definition, which has the following important attributes:

<action-mappings>
This section contains your action definitions. You use an <action> element for each of the mappings you would like to define. Most action elements will define at least the following attributes:

Other often-used attributes include:

For a complete description of the elements that can be used with the action element, see the ActionConfig documentation.

Action Mapping Example

Here's a mapping entry based on the CustomerManagement example application. Note that the entries for all the other actions are left out:

<phruts-config>
  <form-beans>
    <form-bean name="loginForm"
               type="form::LoginForm"/>
  </form-beans>
  <global-forwards>
    <forward name="login"
             path="/login.php"
             redirect="false"/>
  </global-forwards>
  <action-mappings>
    <action path="/login"
            type="action::LoginAction"
            name="loginForm"
            scope="request"
            input="/login.php"
            unknown="false"
            validate="true"/>
  </action-mappings>
</phruts-config>

First the form bean is defined. A basic bean of class "form::LoginForm" is mapped to the logical name "loginForm". This name is used as a request attribute name for the form bean.

The "global-forwards" section is used to create logical name mappings for commonly used presentation pages. Each of these forwards is available through a call to your action mapping instance, i.e. $mapping->findForward("logicalName").

As you can see, this mapping matches the path /login. When a request that matches this path is received, an instance of the LoginAction class will be created (the first time only) and used. The controller service will look for a bean in request scope under key loginForm, creating and saving a bean of the specified class if needed.

Optional but very useful are the local "forward" elements. In the CustomerManagement example application, many actions include a local "success" and/or "failure" forward as part of an action mapping.

<!-- Edit customer -->
<action path="/editCustomer"
        type="action.EditCustomerAction"
        name="customerForm"
        scope="session"
        validate="true">
  <forward name="failure"
           path="/customerEdit.php"/>
  <forward name="success"
           path="/index.php?do=/getCustomerList"
           redirect="true"/>
</action>

Using just these two extra properties, the Action classes are almost totally independent of the actual names of the presentation pages. The pages can be renamed (for example) during a redesign, with negligible impact on the Action classes themselves. If the names of the "next" pages were hard coded into the Action classes, all of these classes would also need to be modified. Of course, you can define whatever local forward properties makes sense for your own application.

The PHruts configuration file includes several other elements that you can use to customize your application. See Configuring Applications for details.

Previous: Building View Components

Next: Configuring Applications

This documentation is a modified copy of the Apache Struts 1.1 framework user guide, and so is copyrighted under the ASF license.