Controllers
In Bedrock, controllers are classes that represent the business logic portion of an application. Anything not related to the user interface or accessing persistent data is typically handled in a controller. A simple Bedrock application can contain only one controller, while a more complex one could contain a full hierarchy of hundreds of controllers.
Definition & Structure
All Bedrock controllers descend from the base abstract controller class Bedrock::Control which provides some common controller functionality and defines some required methods. Let's take a look at the base Bedrock controller class:
abstract class Bedrock_Control extends Bedrock { /** * Redirects the client to the specified location. * * @param string $location the URL to which the client should be sent */ Bedrock_Common_Logger::info('Redirecting: ' . $location); } /** * Determines whether the current page request is a Bedrock-based AJAX * request. * * @return boolean whether or not the request is an AJAX request */ // Setup $result = false; if($_POST['ajax'] == 1) { $result = true; } return $result; } abstract function index($args); }
A simple redirect method is defined on line 7, which simplifies the process of redirecting a client to another page. Alternative methods for redirecting a user (such as prompting for login credentials) can easily be implemented by overriding Bedrock::Control::redirect() in a descendant class.
Another helper method, Control::isAjaxRequest(), is defined on line 18 to allow you to determine if the page is being requested directly or via AJAX. This is useful in cases where you may want to change the page output depending on the type of request.
The third method Control::index() is defined as abstract and enforces the requirement that every controller define at least this method. This method is considered the default and is called by the Bedrock::Common::Router class whenever a method is not specified. This behavior is similar to the use of a default HTML document (such as index.php or index.html). To better understand this concept, observe that the following URLs would both trigger a call to Application::Control::Home::index():
http://www.mydomain.com/home/ http://www.mydomain.com/home/index
Note: The above assumes that A) a controller with the name Application::Control::Index is not defined, and B) a directory with this path also does not exist.
Nesting & Hierarchy
Bedrock controllers take advantage of object inheritance in order to minimize the amount of redundant code in an application. While this method is highly recommended for complex applications, please do so with caution. This approach can be over-implemented and result in your code being spread across too many classes and files, rendering it extremely unreadable and difficult to maintain.
Example
Nesting controllers is ideal for applications that are organized into multi-level sections. In this case, each section can be represented by its own controller, and subsections can be represented by descendent controllers. For example, an online retail website might have the following page structure:
- Main
- Books
- Fiction
- Non-Fiction
- Music
- Rock
- Classical
- Salsa
- Software
- Games
- Productivity
Step 1: The Root Controller
In this example, the Main section can be represented by a root-level controller. This section might contain some common elements for the site, such as a header, a menu bar, and a copyright notice at the bottom of the page. To manage the logic involved with displaying this content, a root-level controller similar to this can be created:
/** * <... docblock ...> */ class Application_Control_Main extends Bedrock_Control { /** * <... docblock ...> */ Bedrock_Common_Logger::logEntry(); try { // ... // Handle all logic for the main section. // ... Bedrock_Common_Logger::logExit(); } catch(Exception $ex) { Bedrock_Common_Logger::exception($ex); Bedrock_Common_Logger::logExit(); } } }
At this point we have a simple barebones controller, which is typical for a root-level controller. To test that everything is working as it should, we can run this controller method by accessing its corresponding URL in the browser:
http://www.mydomain.com/main/
Note that we have left off the name of the method itself, since we are using the default index method which is loaded when none is specified.
Step 2: Child Controllers
Now that we have our root-level controller, we can create the necessary child controllers. Child controllers can be thought of as an exact copy of the root-level controller, with some additional logic defined. For the Main > Books section, additional logic may be required to display popular books, links to reviews, a list of subsections, etc. For this section, a child controller would be defined like this:
/** * <... docblock ...> */ class Application_Control_Main_Books extends Bedrock_Control_Main { /** * <... docblock ...> */ Bedrock_Common_Logger::logEntry(); try { // ... // Handle all logic for the books section. // ... // Execute main section logic. parent::index($args); Bedrock_Common_Logger::logExit(); } catch(Exception $ex) { Bedrock_Common_Logger::exception($ex); Bedrock_Common_Logger::logExit(); } } }
You'll notice that the child controller for the Books section is almost exactly the same as the root-level controller, except for one key difference. On line 17 we make a call to its parent's index() method. This effectively makes a call to Application::Control::Main::index() and executes all relevant logic for our root-level controller. This means any call to Application::Control::Main::Books::index() will execute all code within the index methods of both the Books controller and the Main controller.
At this point we have created a child controller for the Books section. If we point a browser to the proper URL to execute this controller's index() method:
http://www.mydomain.com/main/books/
… we will be executing the logic to load up the page header, menu, and copyright info (as defined in the Main controller), as well as the logic to load information about popular books, links to reviews, and a list of subsections (as defined in the Books controller).
Step 3: The Rest of the Kids
A similar approach would then be applied to the Fiction and Non-Fiction sections within Books by extending the Books controller. In this way, only a single method call is required to execute code for a given section and any relevant code in its parent sections.
This approach is not limited to controller index methods, but can be applied to any controller method (and can even be omitted entirely if necessary). Remember, when using controller methods other than the default index() method, its name must be explicitly defined in the URL.