SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Any opinions on Zend action helpers?

    Here's an advanced question related to framework architecture.

    While trying to figure out how best to implement authorization in an application using Zend Framework, I came across this article: Action Helpers in Zend Framework.

    Many tutorials on Zend Framework would have you believe you should create a base class extending Zend_Controller_Action to provide base functionality for your controllers:

    PHP Code:
    /**
     * Your concrete controllers would now extend My_Controller_Action
     */
    abstract class My_Controller_Action extends Zend_Controller_Action
    {
        
    // create your utility methods here...

    However, this is not only not necessary, but typically not a great move for extensibility. You may find later that a given controller only needs a subset of the methods in your base controller -- or that you're constantly adding methods that only a few of your controllers need, creating bloat.
    To me, this seems lopsided. I'm being asked to use an action helper class and a "helper broker" to basically do nothing more than inject an object into an action class. That's bloat as far as I can tell. It's indirect, hard to follow and hard to understand.

    The best way to solve this, and the way it's being done in some other frameworks (I've recently used Struts 2, which is pretty good at this), is to use interceptors (Intercepting Filter, if you will). An interceptor should be able to access the action controller object and inject stuff (such as an authorization/acl object) into it. ZF has Zend_Controller_Plugin, which is similar to an interceptor, but unfortunately none of the methods are able to access the action controller object after it's instantiated but before the action is executed (dispatched).

    To me, the idea of action helpers looks like a kludge to remedy this shortcoming in ZF. The alternative kludge is the one being rejected in the article, a custom action controller parent class. And the article is correct that if you just keep mindlessly adding methods, you will end up with a bloated class. On the other hand, if you know how to extract classes, you can do that before it's too late. To me, that seems simpler and more direct, obviating the need for a helper broker and the need to conform to the interface of an action helper.

    I've only studied this briefly, so I may have misunderstood or overlooked something. Feel free to tell me I'm wrong.

    EDIT: Reading the Zend Controller code, it seems there are at least two more ways to pass an object to the action controllers. You can use Zend_Registry (or any other variation on a global variable) or you can send it as a parameter to the Front Controller, which passes it on to the action controller via the Dispatcher.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  2. #2
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You're wrong. Wrong, wrong, wrong. Man, how wrong can you be? I've never seen anyone so wrong in my life. Wow. Did you just step off the train to Wrong Kong or something? Good god almighty! Wrong. Did you fall off the giant wrong tree and hit every branch on the wrong way down? Must have! Indeed, when you were born, the doctor said WRONG! Sweet error in the system, you sure are one wrong kinda guy. Why, if you were a superhero, your name would definitely be WrongMan. You are just horribly, utterly, terribly wrong. Wrong! Wrong! WRONG!!!

    Ok, so not really, but I've always wanted to say that.

    But seriously, wondering why HelperBroker and this...

    PHP Code:
    $url $this->_helper->url('bar''foo'); 
    is somehow better than autoload and this...

    PHP Code:
    $helper = new UrlHelper();
    $url $helper->makeUrl('bar''foo'); 
    or if you need to guarantee some sort of global configuration...

    PHP Code:
    $url UrlHelper::load('someConfigName')->makeUrl('bar''foo'); 
    The form example in the article doesn't do anything more than this. It's just a really long winded way to instantiate an object as far as I can tell.

    I'd prefer to refactor the classes as you suggest, rather than split the logic across a bunch of mix-and-match helpers.

  3. #3
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dagfinn View Post
    EDIT: Reading the Zend Controller code, it seems there are at least two more ways to pass an object to the action controllers. You can use Zend_Registry (or any other variation on a global variable) or you can send it as a parameter to the Front Controller, which passes it on to the action controller via the Dispatcher.
    The obvious answer is dependency injection. The problem with a lot of (most?) frameworks though, is that they 1) expect you extend a baseclass and 2) They want to instantiate the controller class for you. This makes it impossible to use a dependency injection container to provide wiring. Maybe, you could write your own Zend_Dispatcher_Phemto, which uses a container? I don't use ZF's controller parts, so I'm not really aware if somebody already did this, or if there is a better place to hook in to the framework?

  4. #4
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    The obvious answer is dependency injection.
    Absolukely, although at this stage I would be happy to have a place in the code where I could just pass the object to the action controller. In other words, just the final stage of dependency injection.
    Quote Originally Posted by kyberfabrikken View Post
    The problem with a lot of (most?) frameworks though, is that they 1) expect you extend a baseclass and 2) They want to instantiate the controller class for you. This makes it impossible to use a dependency injection container to provide wiring.
    I haven't much experience with specific DI containers. But couldn't you just use setter injection on an instantiated controller? That is, if there were a place to do that, which there isn't.
    Quote Originally Posted by kyberfabrikken View Post
    Maybe, you could write your own Zend_Dispatcher_Phemto, which uses a container? I don't use ZF's controller parts, so I'm not really aware if somebody already did this, or if there is a better place to hook in to the framework?
    Searching for Zend dependency injection I find this. I'm not sure it even qualifies as DI, but it's interesting.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  5. #5
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Actually, the problem is that for authorization purposes, I want to be able to both control which action is run and to pass the ACL object to the action controller. I think I may have found a reasonable solution. It relies on the fact that the Zend front controller is a singleton. So I can write a front controller plugin that checks the authorization for the current action in the preDispatch() method. The preDispatch() method gets the Request object passed to it, and can change the action before it's dispatched. Then it can get the singleton instance of the front controller and pass the the ACL object to it using setParam(). The front controller passes the params to the dispatcher which passes them on to the action controller.

    That should work. Let's hope it does.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  6. #6
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It works. And it's even simpler, since I can set the param in the request object, so I don't need the front controller singleton. I knew I could, but it looked hackish, mixing this object into the HTTP request object. Actually, though, there is a separation between the GET and POST variables and the ones you set yourself.

    As it seems now, I was wrong about the inability to make a proper interceptor, but right that the whole idea of action helpers is dubious.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  7. #7
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dagfinn View Post
    It works. And it's even simpler, since I can set the param in the request object, so I don't need the front controller singleton.
    Correct me if I'm wrong, but does this mean that you're passing the object to all controllers? If so, isn't this simply a global variable?

    Quote Originally Posted by dagfinn View Post
    I haven't much experience with specific DI containers. But couldn't you just use setter injection on an instantiated controller? That is, if there were a place to do that, which there isn't.
    Yes. I'm not so happy about setter injection though. You can end up with incomplete objects, you can initialise objects more than once, and there is not one place to go look for dependencies.
    It could certainly work, but I'd rather that the framework didn't force me to do the next-best thing.

  8. #8
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    Correct me if I'm wrong, but does this mean that you're passing the object to all controllers? If so, isn't this simply a global variable?
    I don't think so. First, making an object available to all controllers is hardly the same thing as a global variable. (The controllers are only the C part of MVC, and it will be an instance variable, not a plain $something.) Second, I'm passing it to whatever controller is about to be instantiated, so in principle at least I can decide which controllers should get it.
    Quote Originally Posted by kyberfabrikken View Post
    Yes. I'm not so happy about setter injection though. You can end up with incomplete objects, you can initialise objects more than once, and there is not one place to go look for dependencies.
    It could certainly work, but I'd rather that the framework didn't force me to do the next-best thing.
    Interesting points. I don't have the experience that would allow me to agree or disagree with you. I recently worked in a Java project that used Spring for setter injection. Spring fails in mysterious ways sometimes, but I'm not sure that's related to the way objects are injected.

    I'm not entirely happy with the architecture of the ZF Front Controller, but it's obviously important in the Real World, and I guess enjoy the challenge of bending it to be simpler than it wants to be.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  9. #9
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Dagfinn, I am not sure if I'm missing the point, but I think that the non-hackish way of achieving the same thing would be to put that same code in the preDispatch method of your action helper. Keep in mind that the action helper at that stage still has the power to modify how the action will be dispatched.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •