Search code examples
phpyii2yii-behaviour

Yii2: How to refer to an action attached to a controller with a Behavior?


I am using a Behavior to add an action to a few controllers, the oversimplified behavior is defined as

public class GreetBehavior extends Behavior {
    public function sayHello() {
        return "Hello"
    }

    public function actionGreet() {
        return "Hello";
    }
}

The behavior is attached successfully. From within a Controller (f.i. Person) method I can access sayHello with $this->sayHello();. The actionGreet however, is not recognized if I call it from the address bar: ...index.php?r=person/greet. It gives the message that it cannot be resolved. Is it possible to add actions to a controller with behaviors anyway? If so, can you give me a hint what to do/what I am doing wrong. If not, do you know an alternative?


Solution

  • In Yii2 there are two ways to add actions to a controller; standalone and inline. You are trying to add an inline action - which means that it is declared as a method on the controller's class with the 'action' prefix.

    The other way to load actions are as 'standalone' files, that are defined in the controller's 'actions' method. This returns an array of references to action classes that exist elsewhere.


    If you look at the createAction method on yii\base\Controller (which loads the action when your app runs) you can see that it first looks through its actions array (the standalone ones) and if it can't find it it looks through its own methods (after formatting the name and adding the 'action' prefix - see lines 224-225).

    The problem you have is that when the base controller looks for the inline method it uses php's method_exists function, which is totally blind to yii2's behavior functionality.

    I don't know why it doesn't use the 'hasMethod' method on the base controller instead, which would know about the behavior methods. I'm only just looking at Yii2 at this level of detail though so there may be a reason I'm unaware of.


    So the answer to your question is that the way Yii2 is currently coded you cannot put an inline action on a controller's behavior - because it won't be seen when Yii2 comes to look for it.

    And the solution is to switch to standalone actions, which you can read about on this guide page.