Well in most available PHP frameworks we see the actions/commands being organized as methods inside app controller classes, take Symfony's controller class as example:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Hello '.$name.'!</body></html>');
}
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->bindRequest($request);
// ...
}
}
In Martin Fowler and Matt Zandstra's book however, the actions/commands are organized as separate classes from the controller. The controller will instantiate the appropriate command objects and then act upon them. This is demonstrated by the example on sitepoint.com, the code is a bit old with PHP4 but it is the architecture that we care about. Also note due to the simple nature of the script, a front controller itself is more than enough to handle the command requests, otherwise several different App controllers will be taking care of groups of commands/view objects:
/**
* The FrontController's job is to look at the incoming HTTP request
* to see what task the user of the application wants to perform.
* It will then create an object, which purpose is to handle that
* specific task: a Command
*
* For this example application, people can do the following tasks:
* - add a user account to the system => handled by the UserAddCommand
* - edit a user account => handled by the UserEditCommand
* - view a list of users => handled by the UserListCommand
**/
class FrontController
{
function run()
{
switch ($_GET['page'])
{
case 'add':
$command = new UserAddCommand;
break;
case 'edit':
$command = new UserEditCommand;
break;
case 'list':
$command = new UserListCommand;
break;
}
$command->execute();
$view = $command->getView();
$view->render();
}
}
/**
* This Command is responsible for the task of
* adding a user, and all the events that can occur while the user
* executes that task (submission of a form in this case)
*
**/
class UserAddCommand extends BaseCommand
{
function execute()
{
if (!$formHasBeenSubmitted)
{
$this->view = new UserAddView;
return;
}
if ($formIsValid)
{
$user = new User;
$user->username = $_POST['username'];
$user->password = $_POST['password'];
$user->email = $_POST['email'];
$user->save(); // could be $userDAO->save($user) or whatever
$this->view = new UserHasBeenAddedView;
}
else
{
// Redisplay the form
$this->view = new UserAddView($_POST, $formErrorMessages);
}
}
}
class UserEditCommand extends BaseCommand
{
function execute()
{
if ($userIsNotFoundInDatabase)
{
$this->view = new UserNotFoundView;
return;
}
if (!$formHasBeenSubmitted)
{
$this->view = new UserAddView;
return;
}
if ($formIsValid)
{
$user;
$user->username = $_POST['username'];
$user->password = $_POST['password'];
$user->email = $_POST['email'];
$user->save(); // could be $userDAO->save($user) or whatever
$this->view = new UserHasBeenEditedView;
}
else
{
// Redisplay the form
$this->view = new UserEditView($_POST, $formErrorMessages);
}
}
}
class UserListCommand extends BaseCommand
{
function execute()
{
$users = getUsersFromDatabaseSomehow();
$this->view = new UserListView($users);
}
}
class UserAddView
{
function render()
{
// Print a html form here
}
}
class UserHasBeenAddedView
{
function render()
{
echo 'The user account has been created succesfully.
Click here to return to the main page';
}
}
class UserEditView
{
function UserEditView($userObject)
{
$this->userObject = $userObject;
}
function render()
{
// Print a html form here and fill up the form fields
// with data from the $userObject
}
}
class UserListView
{
function UserListView($users)
{
$this->users = $users;
}
function render()
{
foreach ($this->users as $user)
{
echo ...;
}
}
}
So what is the better MVC approach? The latter looks more object oriented and it decouples actions/commands from controllers to allow possible reuse(though the reusability of command classes may not be obvious). However, there must be a reason why popular frameworks like Symfony, Zend, Cake and Codeigniter all choose the other approach, which simply writes each action/command inside appcontroller as methods. What do you think?