Re: [fw-general] Re: Zend_Controller_Action::init() (Was: SVN 2077: Getting parameters from route)
-- Nick Lo [EMAIL PROTECTED] wrote (on Wednesday, 06 December 2006, 03:09 PM +1100): Aha, now I get it ...so what I CAN do is: snip -- code examples So in short init() is a convenience method which is fine. Correct. It's to make it easier on you, the developer, so you don't have to write out that nasty __construct() line. :-) 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. No problem! Glad I was able to clarify things. -- 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,
Re: [fw-general] Re: Zend_Controller_Action::init() (Was: SVN 2077: Getting parameters from route)
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