Front Controller & Command Execution

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Front Controller & Command Execution

Post by Luke »

I just whipped this up... please rip it to shreds. Let me know Every little nit-picky thing that is wrong with it.

EDIT: oh yea... almost forgot to say what it is... if it's not blatently obvious, it's a front controller and command resolver

I'm hoping that maybe with the community's help, I can perfect this and replace my current (more or less procedural) page controllers

Code: Select all

<?php
require_once 'C:\wamp\www\core\classes\Request.inc.php';

// COMMANDS

		class Front_Command{
			protected $action;
			public function execute(){
				echo "Error: Could not find specified command";
			}
			public function doExecute( Front_Command $command ){
				$command->execute();
			}
		}
		
		class Front_Command_default extends Front_Command{
			public function __construct(){
				$this->action = "Default";
			}
			public function execute(){
				echo $this->action . " was executed.";
			}
		}
		
		class Front_Command_login extends Front_Command{
			public function __construct(){
				$this->action = "Login";
			}
			public function execute(){
				echo $this->action . " was executed.";
			}
		}

// ENDCOMMANDS

// Request is just a class that gets request info (from url, forms, command line, etc.)
// Front request is specialized in that it returns information specific to the front controller
class Front_Request extends Request{
	protected $default;
	protected $error;
	public function __construct($default, $error){
		parent::__construct();
		$this->default = $default;
		$this->error   = $error;
	}
	public function getCommand(){
		return $this->action ? $this->action : $this->default;
	}
	public function getCommandDefault(){
		return $this->default;
	}
	public function getCommandError(){
		return $this->error;
	}
}

class Front_Command_Resolver{
	public function getCommandComponent( Front_Request $request ){
		// Get command from request object
		$command = $request->getCommand();
		
		// Assign class name with command appended
		$class_name = "Front_Command_{$command}";
		
		// If this specific class exists, return it
		if(class_exists($class_name)){
			return new $class_name();
		}
		
		// Otherwise return the standard command
		return new Front_Command();
	}
}

class Front_Controller{
	private static $instance = null;
	private function __construct(){
		// Some sort of initialization
	}
	
	public function getInstance(){
		// This shouldn't need an explanation, but I'll explain anyway... it ensures that only one instance of this can be instantiated
		if( ! self::$instance ) {
			self::$instance = new Front_Controller();
		}
		return self::$instance;
	}
	
	public function run(){
		// Get request from user (could be by url/command line... not up to this class to decide... left up to the front request object)
		$request = new Front_Request('default', 'error');
		
		// Get an instance of the command resolver
		$command_resolver = new Front_Command_Resolver();
		// Resolve the user's command into a command component (a descendant of our friend ^^ the Front_Command
		$command = $command_resolver->getCommandComponent( $request );
		
		// Execute the command
		$command->execute();
	}
}
$Controller = Front_Controller::getInstance();
$Controller->run();
?>
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

You are on your way. One note is that those Commands that are dispatched by your Front Controller are called "Actions" by most people. It is a convention that goes back to Struts and beyond. They are often named something like "LoginController" or "LoginAction".
(#10850)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

:D I think I'm finally starting to think OOP :D
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

So how can I modify this to send the action to the model/view?

ModelResolver & ViewResolver? That may work. I'll work on it.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

The Ninja Space Goat wrote:So how can I modify this to send the action to the model/view?
You don't "send the action to the model/view", the Action is the Controller. It can create both the Model and the View -- passing the Model to the View, or the View can create the Model itself. But the Action takes over flow control once it is dispatched by the Front Controller.
(#10850)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

good point... thanks. alright... I've modified it some more... what would be a better name for the action's execute method?
Action:

Code: Select all

<?php
abstract class Controller_Front_Action{
	protected $action;
	//protected $view; <-- this is just something I'm thinking about right now... see any problem with this methodology?
	//protected $model; <-- Same with this
	abstract public function execute();
}
?>
Action Resolver

Code: Select all

<?php
class Controller_Front_ActionResolver{
	protected $action;
	protected $path;
	protected $ext;
	public function __construct( Controller_Front_Request $request ){
		// Get action from request object
		$this->action = $request->getAction();
	}
	// Need to add autoloading functionality
	public function getComponent( $prefix='', $suffix='Action', $ext='.php' ){
		$path_name = $prefix . $this->action . $ext;

		if(file_exists($path_name)){
			require_once $path_name;
			$class_name = $this->action . $suffix;
			
			// If this specific class exists, return it
			if(class_exists($class_name, false)){
				return new $class_name();
			}
		}
		
		// Otherwise return the error action
		return new Front_Action_Error();
		
	}
}
?>
Front Controller

Code: Select all

<?php
class Controller_Front_Front{
	private static $instance = null;
	public function getInstance(){
		// This shouldn't need an explanation, but I'll explain anyway... it ensures that only one instance of this can be instantiated
		if( ! self::$instance ) {
			self::$instance = new Controller_Front_Front();
		}
		return self::$instance;
	}
	
	public function run($path){
		// Get request from user (could be by url/command line... not up to this class to decide... left up to the front request object)
		$request = new Controller_Front_Request('login');
		
		// Get an instance of the command resolver
		$action_resolver = new Controller_Front_ActionResolver( $request );
		// Resolve the users action into a command component (a descendant of our friend ^^ the Front_Action
		$action = $action_resolver->getComponent( $path );
		
		// Execute the action
		$action->execute();
	}
}
?>
Front Request

Code: Select all

<?php
// Request is just a class that gets request info (from url, forms, command line, etc.)
// Front request is specialized in that it returns information specific to the front controller
class Controller_Front_Request extends Request{
	protected $default;
	public function __construct($default){
		parent::__construct();
		$this->default = $default;
	}
	public function getAction(){
		return $this->action ? $this->action : $this->default;
	}
	public function getActionDefault(){
		return $this->default;
	}
}
?>
Post Reply