The Controller

Controllers in MVC are the entry points to your application, usually triggered by a URL request. A controller's job is to simply formuate a response based on the request. A request isn't limited to a URL request, but can also come from a GET, POST, PUT, DELETE, session and/or combination of any of these. In the front application's case we are defining how a page is loaded. We separate our controllers in two sections. The first one is called our control driver or our bootstrap which is found in web/front.php. The second is called our control definition found in front.php.

Bootstrapping

Figure 1. index.php
if($_SERVER['REQUEST_URI'] == '/assets' 
	|| strpos($_SERVER['REQUEST_URI'], '/assets/') === 0
	|| strpos($_SERVER['REQUEST_URI'], '/assets?') === 0) {
	require('assets.php');
} else { 
	require('front.php'); 
}

So when we open index.php you should see a simple if statement that determines which bootstrap to use. In our framework, inorder to access assets like images, stylesheets and script behind the DMZ, we need to have a controller to pass it through. This is done with our assets bootstrap in assets.php Any other requests should be handled by our front bootstrap in front.php.

Figure 2. front.php Bootstrap
/* Get Application
-------------------------------*/
print front()

/* Set Debug
-------------------------------*/
->setDebug(E_ALL, true)

/* Set Autoload
-------------------------------*/
->setLoader(NULL, '/module')

/* Set Paths
-------------------------------*/
->setPaths()

/* Start Filters
-------------------------------*/
->setFilters()

/* Trigger Init Event
-------------------------------*/
->trigger('init')

/* Set Database
-------------------------------*/
->setDatabases()

/* Set Timezone
-------------------------------*/
->setTimezone('Asia/Manila')

/* Trigger Init Event
-------------------------------*/
->trigger('config')

/* Start Session
-------------------------------*/
->startSession()

/* Trigger Session Event
-------------------------------*/
->trigger('session')

/* Set Request
-------------------------------*/
->setRequest()

/* Trigger Request Event
-------------------------------*/
->trigger('request')

/* Set Response
-------------------------------*/
->setResponse('Front_Page_Index')

/* Trigger Response Event
-------------------------------*/
->trigger('response');

If you open front.php above, you'll see a true bootstrap format. The idea here is that you need to determine what you really need to load. For example, if your application isn't using a database, then you should probably comment out ->setDatabases(). You might also be wondering what are all these trigger methods? When we use this method we broadcast to the application that this event has occured. Any event handlers listening to that event will begin to execute. From a fresh download, there is nothing listening to these events, so you are also free to comment those out if you choose. All of these methods are defined in front.php found in the root of your project and you are free to add extra methods that pertain to your specific project.

Control Definition

front.php found in the root of your project, serves two purposes. The first one is to define available methods that can be used on your bootstrap. From a fresh download, we generically defined typical processes most commonly used to load a page. The second purpose is to make available utility methods to be used throughout your project. An example might be having a method to change a date to "[time] ago" most commonly used on a template.

Of all the available methods the control class offers the one of note is the setRequest() method. This method is in charge of determining what view to load based on the URL request and you are free to change it to anyway you like.

Figure 3. Set Request
public function setRequest() {
	$path = $_SERVER['REQUEST_URI'];
	if(strpos($path, '?') !== false) {
		list($path, $tmp) = explode('?', $path, 2);
	}
	
	$array 		= explode('/',  $path);
	$pages 		= $this->config('pages');
	$variables 	= array();
	
	$page = NULL;
	foreach($pages as $pattern => $class) {
		$regex = '#'.str_replace('*', '.*', $pattern).'#';
		if(preg_match($regex, $path) && strlen($pattern) >= strlen($page)) {
			$page 		= $class;
			$variables 	= $this->_getVariables($path, $pattern);
		}
	}
	
	if(!$page) { 
		$buffer = $array;
		
		while(count($buffer) > 1) {
			$parts = ucwords(implode(' ', $buffer)); 
			$class = 'Front_Page'.str_replace(' ', '_', $parts);
			if(class_exists($class)) {
				$page = $class;
				break;
			}
			
			$variable = array_pop($buffer);
			array_unshift($variables, $variable);
		}
	}
	
	$path = array(
		'string' 	=> $path, 
		'array' 	=> $array,
		'variables'	=> $variables);
	
	//set the request
	$this->registry()
		->set('server', $_SERVER)
		->set('cookie', $_COOKIE)
		->set('get', $_GET)
		->set('post', $_POST)
		->set('files', $_FILES)
		->set('request', $path)
		->set('page', $page);
	
	return $this;
}

The first part of this method simply grabs the URL request from $_SERVER['REQUEST_URI']. Sometimes this variable will have query data, which is already set in the $_GET variable, either way we'll need to chop that part off.

Figure 3a. Get the Request
$path = $_SERVER['REQUEST_URI'];
if(strpos($path, '?') !== false) {
	list($path, $tmp) = explode('?', $path, 2);
}

Next, we want to consider page routing. Page routing is a key value configuration found in config/page.php. The key should be a request path as in /user/*/edit where star(*) is a wildcard. You can have as many wildcards as you need. the value should be the page class to load as in Front_Page_Index. Page route is designed to be a nice to have and is not a required step. Figure 3b1 shows some examples of possible page routes

Figure 3b1. Example Page Routing
return array(
	'/user/*/edit' 				=> 'Front_Page_User_Edit',
	'/post/*/comments/*/detail' => 'Front_Page_Post_Comment_Detail',
	'/article' 					=> 'Front_Page_Post');

Figure 3b2 below shows how setRequest() considers these page routes.

Figure 3b2. Considering Page Routes
foreach($pages as $pattern => $class) {
	$regex = '#'.str_replace('*', '.*', $pattern).'#';
	if(preg_match($regex, $path) && strlen($pattern) >= strlen($page)) {
		$page 		= $class;
		$variables 	= $this->_getVariables($path, $pattern);
	}
}

Another feature is that setRequest() will also auto determine URL variables based on the actual page class that loads. If a user sent /user/chris/edit/avatar and given the page route in Figure 3b1, both chris and avatar would be set as URL variables. You could use these variables in your page class to determine several outputs.

If no page route was found, we then continue to determine the page class based on the folder structure of front/page. What happens for a request of /post/123 for example is that we simply convert that path to Front_Page_Post_123 if no class is found we continue to traverse upward to Front_Page_Post. If we reach the root, then by default it will load Front_Page_Index as set in the web/front.php bootstrap file. One reason why you may want to change this, is in the case you wanted to show a 404 error.

Figure 3c. Finding a Page Class, Traversing Upwards
while(count($buffer) > 1) {
	$parts = ucwords(implode(' ', $buffer)); 
	$class = 'Front_Page'.str_replace(' ', '_', $parts);
	if(class_exists($class)) {
		$page = $class;
		break;
	}
	
	$variable = array_pop($buffer);
	array_unshift($variables, $variable);
}

The last thing we do is store all of the information into a global registry object that can be accessable anywhere in your code.

Figure 3d. Add to the global registry
$path = array(
	'string' 	=> $path, 
	'array' 	=> $array,
	'variables'	=> $variables);

//set the request
$this->registry()
	->set('server', $_SERVER)
	->set('cookie', $_COOKIE)
	->set('get', $_GET)
	->set('post', $_POST)
	->set('files', $_FILES)
	->set('request', $path)
	->set('page', $page);

Global Registry

Figure 4. What's in there?
[path] => Array(
	[root] => /server/public/eden.project.dev/framework
	[module] => /server/public/eden.project.dev/framework/module
	[config] => /server/public/eden.project.dev/framework/config
	[theme] => /server/public/eden.project.dev/framework/theme
	[page] => /server/public/eden.project.dev/framework/front/page
	[library] => /server/public/eden.project.dev/framework/library
	[web] => /server/public/eden.project.dev/framework/web)
[server] => Array(
	[HTTP_HOST] => framework.project.dev
	[HTTP_USER_AGENT] => Mozilla/5.0 
	[HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
	[HTTP_ACCEPT_CHARSET] => ISO-8859-1,utf-8;q=0.7,*;q=0.3
	[HTTP_COOKIE] => PHPSESSID=1m1usfdvunc1i90hcqorkss1q6
	[SERVER_SIGNATURE] => 
	[SERVER_SOFTWARE] => Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8r DAV/2 PHP/5.3.4
	[SERVER_NAME] => framework.project.dev
	[SERVER_ADDR] => 127.0.0.1
	[SERVER_PORT] => 80
	[REMOTE_ADDR] => 127.0.0.1
	[DOCUMENT_ROOT] => /server/public
	[SERVER_ADMIN] => cblanquera@gmail.com
	[SCRIPT_FILENAME] => /server/public/framework.project.dev/current/index.php
	[SERVER_PROTOCOL] => HTTP/1.1
	[REQUEST_METHOD] => GET
	[QUERY_STRING] => 
	[REQUEST_URI] => /user/29
	[SCRIPT_NAME] => /index.php
	[PHP_SELF] => /index.php)

[cookie] => Array( [PHPSESSID] => 1m1usfdvunc1i90hcqorkss1q6 )
[get] => Array()
[post] => Array()
[files] => Array()
[request] => Array(
	[string] => /user/29
	[array] => Array (
		[0] => user
		[1] => 29 )
	[variables] => Array( [0] => 29 ))

[response] => Front_Page_User Object()

The global registry can be accessed in a model, a page class, a template, anywhere by simply calling front()->registry(). See 9. The Registry. To learn more about how to set and retrieve data.

Utility Methods

On the topic of what else is available for your project on top of what Eden already gives is a slew of utility methods designed to get and set data without any hassle.

Figure 5. Control Utilities
front()->registry()->get('path', 'root'); 						//--> /server/public/eden.project.dev/framework
front()->registry()->set('some', 'new', 'data'); 				//adds a value to the global registry
front()->path('web'); 											//--> /server/public/eden.project.dev/framework/web
front()->config('database'); 									// returns the data found in config/database.php
front()->config('page', array('/user/*/edit'));					//saves the given data to config/page.php
front()->template('/path/to/file', array('some' => 'data')); 	//binds variables to a template and returns the output 
front()->output('anything');									//debug method use to inspect variables

If you have a better understanding about how the front controller works. It might be worth studying how the assets controller works as a contrast. If you are ready to move on, our next section covers Modeling.


© 2012 Openovate Labs. All rights reserved.