Aha, now I get it ...so what I CAN do is:

class IndexController extends Zend_Controller_Action
{
    public function __construct(
        Zend_Controller_Request_Abstract $request = null,
        Zend_Controller_Response_Abstract $response = null,
        array $invokeArgs = null)
    {
        parent::__construct($request, $response, $invokeArgs);
         ....do other stuff
    }

}

...then...

class ArticlesController extends IndexController
{
    public function __construct(
        Zend_Controller_Request_Abstract $request = null,
        Zend_Controller_Response_Abstract $response = null,
        array $invokeArgs = null)
    {
        parent::__construct($request, $response, $invokeArgs);
        Zend_Db_Table::setDefaultAdapter( $this->_database );
         ....do other stuff
    }
}

However, the recommended approach is...

class IndexController extends Zend_Controller_Action
{
    public function init()
    {
        ....do stuff
    }
}

...then...

class ArticlesController extends IndexController
{
    public function init()
    {
        parent::init();
        ....do stuff
    }
}

So in short init() is a convenience method which is fine. I was just uncomfortable with the idea of being forced to use it on the basis that things otherwise broke (which they don't - my mistake) and that I may forget to properly initialise the parent class.

Thanks very much for your detailed (and patient) responses, I now follow it a lot more clearly.

Nick

-- Nick Lo <[EMAIL PROTECTED]> wrote
(on Wednesday, 06 December 2006, 01:00 PM +1100):
Hi Matthew,

init() happens at object instantiation, which happens before
preDispatch() -- it's basically provided so that you don't need to
override the constructor, and thus possibly forget to set the request,
response, and other parameters (as you discovered the hard way).

It does seem like having init() as a means to bypass the constructor
and also having preDispatch() is possibly a little unintuitive.
Surely init() is a constructor of sorts?

It's not meant to bypass the constructor; it's meant as a means to allow further instantiation actions without needing to override the constructor.

Matt Ratzloff asked "why not declare it final?" My answer to this is
backwards compatability; by not declaring it final, developers instead
can fix their classes as they start needing some of the functionality,
such as access to the request object, instead of having their apps break
entirely due to the constructor now being final. (There were a lot of
complaints about this.)

In theory I can set the request and response....

$controller->setRequest( new Zend_Controller_Request_Http );
$controller->setResponse( new Zend_Controller_Response_Http );

...as I do the router...

$controller->setRouter($router);

This latter is only in the front controller.


...but it will still be required to specified in the constructor of
classes overriding __construct...

parent::__construct( new Zend_Controller_Request_Http, new
Zend_Controller_Response_Http );

...however at that point the parameters will not be available.

Umm... yes, they will, if you define your constructor to follow
Zend_Controller_Action's prototype:

    public function __construct(
        Zend_Controller_Request_Abstract $request = null,
        Zend_Controller_Response_Abstract $response = null,
        array $invokeArgs = null)
    {}

then simply do this:

    public function __construct(...)
    {
        parent::__construct($request, $response, $invokeArgs);

        // ...
    }

It seems that if we have to follow the init() path the manual is
going to have to say something like...

 "If you subclass Zend_Controller_Action you should not override the
constructor instead setup code should go in the init() method"

It does say that. In the current manual from subversion:

    "While you may override the constructor, we suggest putting any
    initialization handling into init() to ensure the request and
    response objects are properly registered."

In other words it will need to be a warning. That is where I feel
that init() is unintuitive and most people (I say with hesitation
being a self elected representative) would expect to go with the
constructor for this task.

Note also that I didn't forget to set the request and response
objects when I overrode the constructor, rather that when I did set
them in the subclass no parameters were available in the controller.

You set them improperly, though. You created *new* objects, instead of
simply passing in the parameters that were passed to your constructor.

Theoretically, if something goes wrong during the initialization
sequence, you could throw an exception, in which case the
preDispatch()
will not be called -- this may be handy so that you can, for instance, setup your models, etc., and, if unavailable, prevent the action from
dispatching. preDispatch() might then actually use the models to do
some
determination of whether or not to skip the current action.

I follow the theory and I'd be interested in hearing how others are
using preDispatch and whether their use really does differentiate init
() from preDispatch() in the way you mention.

Here's how I'm using it in one of my applications. I have this in a
Zend_Controller_Action subclass from which my other classes then derive:

    public function init()
    {
// Loads the model based on the current action, which will throw
        // an exception if there are issues
        $this->loadModel();

        // Load up authentication and start it. Again, throws an
        // exception if there are issues
        $this->getAuth()->start();
    }

Then, preDispatch() is defined on a per action controller basis. Some
may set certain ACLs -- which require that authentication has already
begun. Some may query the model based on user information, and decide to
forward to another controller.

Which brings up another point: init() should not change the requested
action; preDispatch() may do so, forwarding to another action if certain
criteria are not met.

Hope this helps answer some questions. I think there's still some wiggle room inthe API, but many of the decisions have been debated or are based
on user feedback already.

--
Matthew Weier O'Phinney
PHP Developer            | [EMAIL PROTECTED]
Zend - The PHP Company   | http://www.zend.com/

Reply via email to