views:

118

answers:

3

As mentioned in the Zend Framework manual, I created a base controller.

Subclassing the Action Controller

By design, Zend_Controller_Action must be subclassed in order to create an action controller. At the minimum, you will need to define action methods that the controller may call.

Besides creating useful functionality for your web applications, you may also find that you're repeating much of the same setup or utility methods in your various controllers; if so, creating a common base controller class that extends Zend_Controller_Action could solve such redundancy.

But it turns out, that Exceptions are not being called properly from the base... To replicate this, create a file:

/path/to/workspace/library/Joe/Controller.php

Then:

class Joe_Controller extends Zend_Controller_Action
{
    public function init()
    {
        Throw new Zend_Exception('test', 500);
        parent::init();
    }
}

Then in your controller directory, IndexController.php extends off the base:

class IndexController extends Joe_Controller
{

You will find that the exception is uncaught.

If however, you don't extend off the base controller, then throw an exception in init or preDispatch, it will be caught and forwarded to the ErrorController.

Anyone have an idea on getting the exceptions caught from the Base Controller?

Thanks.

UPDATING TO TACKLE THIS ANOTHER WAY

After looking at the various articles and official documentation on how to structure the directories, I went with putting the base controller in library/Joe/...but maybe that's part of the problem...there's no guidance in the manual on how to name and place the base controller. What do the experts with a base controller do?

Another Update

Looking at my code some more, and reading on the net, seems like people suggested in should be:

abstract class Joe_Controller_Action extends Zend_Controller_Action.

Except changing it did not solve the problem...

Now considering that there are articles suggesting to use Base Controllers including the manual, would this be considered a bug in Zend Framework?

To replicate, just throw a Zend_Exception of any kind in the base init or preDispatch. Imagine you poll the database in there, (which is what I'm doing)...and the database is down. No error controller. That's not a good situation unless I'm doing something incorrectly.

My suspicion is that this is a new bug... I don't recall this problem before Zend_Application and I've been using a base controller since ZF 1.5.

A: 

Two thoughts off the top of my head:

  1. Controller names in the default module are usually named like SomethingController. Your name Joe_Controller suggests the module Joe and an empty controller name.

  2. In routing/dispatch, doesn't it look for a matching action before dispatch to the controller? Since the samples have no actions, might you be triggering ControllerNotFound exception?

Just throwin' out some ideas.

David Weinraub
Thanks for helping me think this through! (Having trouble formatting the carriage returns so I'll move each point to a new comment).
joedevon
1. I just changed the name to Joe_BaseController and the code does talk to the ErrorController because although I changed IndexController to extend from Joe_BaseController, I had forgotten to change ErrorController and received an include error. But after fixing it, I still get uncaught exception. :(
joedevon
2. The sample didn't include an indexAction, but my code does have it...
joedevon
Hmmm, ok, let me play a bit more. If I come up with any other ideas, I'll add another answer.
David Weinraub
Thanks very much.
joedevon
After actually playing with some code, it all works for me: exception thrown, caught by the ErrorController. See pastie at http://pastie.org/1177214. Whaddya think is going on? Something different in the environment?
David Weinraub
Finally have some time in front of the computer to play with this...I'm pretty sure it's an issue with ErrorController extending off Joe_Controller causing recursion problem. I'll come back with some more specific reply soon.
joedevon
David, OK that fixed it. So the answer basically is that the error controller can't extend from the base controller...and if you need functionality from the base in the error controller, you need to use an Action helper.
joedevon
That must be it. Yep, my ErrorController was the default one, extending Zend_Controller_Action. Your idea to use action helpers to get re-usable functionality in the ErrorController seems the right approach. Hey, I should be accepting your answer! ;-) Thanks and cheers.
David Weinraub
A: 

Don't go for a base or whatever controller

As documentation says :

By design, Zend_Controller_Action must be subclassed in order to create an action controller.

Agreed, this is misleading What they meant is

class Module_TotoController extends Zend_Controller_Action {}

class Module_TotoController extends Joe_Controller is plain wrong (sorry no offence)

as you said your ErrorController can't extend your intermediate Joe_Controller class because your exception will be thrown again in the instantiation of the ErrorController class (as excepted !!!)

The uncaught Exception error is the result of a protection to avoid loops (dig Zend\Controller\Plugin\ErrorHanlder.php line 200-ish)

If you require something to be done for every action, use a Zend_Controller_Plugin and register it with the frontController

Julien
joedevon
I will check out that ErrorHandler though...Thanks for the lead. Joe.
joedevon
A: 

in Zend philosophy, they provide the init() method to avoid the hassle of

public class Module_TotoController extends Zend_Action_Controller {

    public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
        parent::__construct($request, $response, invokeArgs);

        // some init code here
    }
}

just use :

public class Module_TotoController extends Zend_Action_Controller {

    public function init ()
        // some init code here
    }
}

Now, If you look at the Zend_Controller_Action class source, you will notice that the following methods are empty:

  • init()
  • preDispatch ()
  • postDispatch ()

This means, it is useless to call parent:: in your specific controller

public class Module_TotoController extends Zend_Action_Controller {

    public function init ()
        parent::init(); // useless ;-) but you can go for it 

        // some init code here
    }
}

Now, if you want to put an intermediate class between Module_TotoController and Zend_Action_Controller, you expose yourself to a big hassle as:

  • Some controllers won't extend your "base" controller (e.g your ErrorController in your question)
    • so it is not a really a base of your application, is it ?
  • If you put some logic in your init() of that "base",
    • you must call the parent:: in your init(),
    • all the developers in the project need to be aware of that
  • You will ever add another little features to your "Base" controller
    • This will result to a big bloated file
    • Loading/Initializing plenty of stuff you might not really need in that lambda controller
      • Do you need you database on every page ?
        • No: doesn't a $this->_helper->connect(); look nice instead ?
        • Yes: use a controller plugin
  • This "Base" controller won't fit your other projects needs, so that class won't be reusable
    • Action helpers will
    • Controller Plugins will

Hope it makes sense

Julien
Thanks for your post Julien. Some great points. Just to be clearer, I agree with you. They need to remove that paragraph from the documentation. The new idea of using the helpers is superior. But I just followed the prevailing suggestions when first learning Zend Framework and there's now plenty of legacy code for me to support. That's why I chose to keep the base controller. If I started a new project, the Base would be gone. But changing old code means refactoring a bunch of controllers that currently work just fine....possibly introducing bugs...not to mention investment of time.
joedevon