Page 1 of 1

OO MVC controllers

Posted: Sat Feb 03, 2007 11:36 pm
by alex.barylski
I'm not sure I see the use in having a class contain X number of functions for each action a controller might have...

Something like Zend might look like:

Code: Select all

class aboutController{
  function indexAction();

  function saveAction();
  function updateAction();
  ...
}
I find that awefully wasteful for something which is automated...

I mean, your already getting a system which opens the controller file based on controller/action pairs...and the function is invoked...so why not modularize one step further and have the actions stored in seperate files seeing how you will unlikely have a updateAction and a insertAction invoked in the same request...

Why not:

Controller:Action
============
profile/insert

Code: Select all

profile.module.php -> File to load
function profile_insert() -> Function to invoke (profile_* added for namespace reasons)
This way only the controller action which is actually going to be used/executed get's parsed.

Yes you would more work in terms of file management but you could organize each action into directories instead of classes.

Are there any instances where one might want a base action controller which would be later derived from - in which case requiring OOP? Specific examples please...

Assume a front controller is being used...so many of the reuse practices when using a page controller have already been solved using the FC pattern...this is why I wanted specific examples???

Posted: Sun Feb 04, 2007 10:46 am
by Maugrim_The_Reaper
I find that awefully wasteful for something which is automated...
The key here is that it's automated, and any level of automaton-type methods in the ZF are largely under a public or protected access. This has been discussed in detail on the ZF mailing list and having one action per class (Command Pattern) is quite simple to implement by subclassing the default Router. I'd say knowing the Router code for the ZF (and likely any framework) and how to change it is extremely useful since being stuck with the one routing strategy does not suit everyone.
Are there any instances where one might want a base action controller which would be later derived from - in which case requiring OOP? Specific examples please...
Yes, I highly recommend subclassing Zend_Controller_Action as a minimum for each and every application. The subclass can then use the init() method to append additional setup logic. I use it to inject a list of objects from the FrontController (See Zend_Controller_Front::setParam() ) into the ActionController easily without resorting to a Registry class which can get unwieldy (as well as messy) once overused in too many controller files. I'm sure you can find lots more uses for implementing additional plugin hooks and anything that strikes your fancy. I even subclass Zend_View since I prefer my output escaping to automated (ZF leaves it a developer level decision).

Here's one I have open in my editor for an open source project using ZF's subversion HEAD:

Bootstrap (index.php extract):

Code: Select all

$controller = Zend_Controller_Front::getInstance();

/*
 * Create filters and pass to Controller
 * This will disable the GET/POST superglobals
 * and force access through the filter object
 */
if(isset($_POST) and !empty($_POST))
{
    $controller->setParam('post', new Zend_Filter_Input($_POST));
}
if(isset($_GET) and !empty($_GET))
{
    $controller->setParam('get', new Zend_Filter_Input($_GET));
}

/*
 * Add other common objects to be
 * passed to Controller.
 */
$controller ->setParam('view', $view)
            ->setParam('session', $session)
            ->setParam('registry', $registry)
            ->setParam('cache', $cache)
            ->setParam('dao', $dao);

$controller->returnResponse(true);

$response = $controller
        ->setControllerDirectory('./application/controllers')
        ->setRouter($router)
        ->setBaseUrl($base_url)
        ->dispatch();

/*
 * By default Exceptions are not displayed.
 * That won't do during development.
 * Remove this in a live environment!
 */
$response->renderExceptions(true);


/*
 * Echo the response (with headers) to client.
 * Zend_Controller_Response_Http implements
 * __toString().
 */
echo $response;
The Zend_Controller_Action subclass:

Code: Select all

class Astrum_Controller_Action extends Zend_Controller_Action
{

	protected $_view = null;

	protected $_get = null;

	protected $_post = null;

	protected $_session = null;

	protected $_registry = null;

	protected $_cache = null;

	/**
	 * Initialise the custom Controller and
	 * assign variables for use in Action methods.
	 */
    public function init()
    {
        if($this->getInvokeArg('post'))
        {
            $this->_post = $this->getInvokeArg('post');
        }
        if($this->getInvokeArg('get'))
        {
            $this->_get = $this->getInvokeArg('get');
        }
		$this->_view = $this->getInvokeArg('view');
		$this->_session = $this->getInvokeArg('session');
        $this->_registry = $this->getInvokeArg('registry');
        $this->_cache = $this->getInvokeArg('cache');

        /*
         * Additional settings for View
         */
        $this->_view->URLROOT = $this->getRequest()->getBaseUrl();
        $this->_view->setRegistry($this->_registry);
    }

    public function __call($funcname, $args = array()) // if an action method does not exist, forward back to index
    {
        $this->_forward('index', 'index');
    }

}
This way only the controller action which is actually going to be used/executed get's parsed.
If you're not using OOP then why ask OOP questions? Functions can be used in the same way as controllers/actions. But with functions it becomes a more complex challenge when you need to push your separated concerns (view, input filtering, sessions, etc.) into that function for use. I think the OOP variant is a good deal easier and would require a lot less setup code since it's easily centralised in a single subclass (see above) with all the OOP advantages using a class entails.

Posted: Sun Feb 04, 2007 3:48 pm
by alex.barylski
Yes, I highly recommend subclassing Zend_Controller_Action as a minimum for each and every application.
I was under the impression you absolutely had too?? How else would you get custom behavior?

Otherwise, good answer, thats exactly what i was looking for ;)

Thanks buddy :)

Posted: Sun Feb 04, 2007 4:41 pm
by Maugrim_The_Reaper
NP ;)

Subclassing isn't a necessity with a Registry and the use of plugins for the pre and post dispatch stages of routing, but it's usually cleaner with everything in one place.

Posted: Mon Feb 05, 2007 3:09 am
by Maugrim_The_Reaper
Maugrim_The_Reaper wrote:This has been discussed in detail on the ZF mailing list and having one action per class (Command Pattern) is quite simple to implement by subclassing the default Router.
Just to clarify. Since there is one action per controller class, the action methodname can be a simple execute(). Since the name is the same across all controllers, there's little point to requiring it in any URI (hence the suggestion to alter the Router). However this also requires altering the Dispatcher so it no longer looks for the action name and simply knows to call execute() after instantiating a controller (Command Pattern) class.

Posted: Mon Feb 05, 2007 6:39 am
by Jenk
I've tinkered my controller to use a simple execute() method, and provide an argument of intended action, rather than segregate the action into it's own method.

Looks cleaner, and is more true to the OO way, imo. (My Page Controllers (actions) are more specific for single behaviour, than being a blob of many behaviours.)