The MVCnPHP Controller
Alright, the glue that makes all this MVC stuff work is the Controller. As the name implies, the Controller is responsible for controlling program execution. It, upon instruction, knows what command or view to call and when. Before we talk about the internals of the controller, let's show how the controller gets instantiated and is instructed to start working.
If you have noticed throughout using the sample Contact Manager Application, the index.php is used for literally everything. If you fire up your IDE and inspect that page you'll see the following:
10 Propel::init($conf['path_propel_config']); 20 30 // Start a PHP-based session 40 session_start(); 50 60 // Load the MVC configuration 70 $configData = $conf['path'] . 'mvcconfig.xml'; 80 90 // Instanstiate the controller 100 $controller = new MVCnPHP_Controller($configData, MVC_XML); 110 $controller->setBaseURL($conf['site_url']); 120 $controller->setViewDir($conf['path_views']); 130 $controller->setCommandDir($conf['path_commands']); 140 150 try { 160 // Process the request 170 $controller->processRequest(); 180 } catch (Exception $e) { 190 // This will directly call a view to show any uncaught exceptions. 200 $_REQUEST['exception'] = $e; 210 require_once "{$conf['path_views']}PHPARCH_showErrorView.php"; 220 $errorView = new PHPARCH_showErrorView(); 230 $errorView->getView(); 240 }
You can ignore line 10 for now, it simply initializes Propel's runtime library which we don't care to get into right now. On line 40 we begin the session...easy enough. But starting at Line 70 is where some magic starts. Line 70 gets a handle to the location of the MVCnPHP configuration file. As you might have noticed, this is an XML file. Now, before you get all worked up about having to have PHP code access an XML file on every request, please note that MVCnPHP is smart enough to compile the XML file into a PHP file with an array representation of the XML. It is also smart enough to only recompile the XML file when the XML file has changed.
On Line 100 we instantiate the controller and give it the configuration data and the configuration method. Don't worry too much about this line..just know this creates the controller and gives it the information it needs to control program execution.
On lines 110-120 we set some key attributes for the controller and then in the TRY block we tell the controller to go do it's thing. How does it know what to do? There thre ways the constructor knows how to take action:
- Either via the GET or POST, it will get a variable called 'cmd' that holds the name for the view or command the controller should give execution control to.
- Upon execution, a view or command may issue a forward back to the Controller telling it what to do next.
- In the absense of the above two methods, it will check the configuration file for the default view or command and give control to it.
Is this mind boggling? For the MVC newbie, probably but let's explain how all this works with reference to our contact manager application.
First, we covered the view PHPARCH_ContactEditor which shows the contact editor form. If you open ContactEditor.thtml, you will notice there is a hidden input tag with the name 'cmd' that has a value of 'saveContact'. That line is what instructs our controller to give control to PHPARCH_SaveContactCommand. But how exactly? Will if you will notice the below snippet from the MVCnPHP XML configuration file:
10 <command id="saveContact" name="PHPARCH_SaveContactCommand"> 20 <forward id="errors" type="view">editContact</forward> 30 <forward id="success" type="view">home</forward> 40 </command>
Line 10 defines the command 'saveContact' which we had in our form. You'll notice the name attribute points to PHPARCH_SaveContactCommand. That tells the controller when it is told to 'saveContact' it should create PHPARCH_SaveContactCommand have it execute. Lines 20 and 30 define two forwards for the PHPARCH_SaveContactCommand. The first forward we saw in our validation check in the command we were working one. If you recall we had this code in PHPARCH_SaveContactCommand:
if (!$this->passesValidation()) { return 'errors'; }
When we returned 'errors' we were telling the controller where to go. Line 20 above instructs the controller that when it gets a forward called 'errors' it should show the view 'editContact'. If you look in the MVCnPHP XML config, you'll notice an entry that matches 'editContact':
<view id="editContact" name="PHPARCH_ContactEditorView" />
As you might guess, then, a forward called 'errors' that is returned from PHPARCH_SaveContactCommand will cause the view PHPARCH_ContactEditorView to be shown. And it is in this manner that the controller knows what to do at all times. It will be important you understand MVCnPHP because many of the plugins in 2.x will use their own MVCnPHP implementation (i.e. each plugin has it's own controller, own views, own commands) and so this will be an integral part of Geeklog 2. It is worth noting, however, that plugins are not required to use MVCnPHP, though, that is outside the scope of this discussion.
So, to this point, you have gotten an overview of the model-vew-controller design pattern. You have seen examples of how views and commands work, what model objects are and how the MVCnPHP controller is the glue that holds it all together. I hope you appreciate how the use of an MVC implementation cause the code to be atomic. Each view and command only does one thing and one thing only. From a maintenance perspective, this will make finding the file you want to work on easier...compare this with the huge COM_, SEC_ and PLG_ libraries in Geeklog 1.3.x. Now let's talk more about Propel