Yii Framework Forum: Actions by Behavioring - Yii Framework Forum

Jump to content

  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

Actions by Behavioring Is it possible to add actions to a controller via Behaviors? Rate Topic: ***** 1 Votes

#1 User is offline   igorsantos07 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 17
  • Joined: 29-May 10
  • Location:Rio de Janeiro, Brazil

Posted 23 July 2010 - 06:34 AM

Is it possible to create a Behavior with some actions, and implement that Behavior in a Controller, using those actions transparently?

Example:
<?php
class ExampleBehavior extends CBehavior {

public function actionTesting() {

}

}

class CodeController extends CController {

[...]

public function behaviors() {
return array('test' => array('class'=>'ext.behaviors.ExampleBehavior'));
}

}
?>

Calling mysite.com/code/testing would say "The system is unable to find the requested action "testing".

I know the behavioring is successful because I can create an action inside the Controller and call actionTesting() inside it.

But is it possible to call the behavior's actions transparently? Or it's just an unexpected behavior from yii code? A bug?
Is there a "recommended" workaround?
0

#2 User is offline   rAWTAZ 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 102
  • Joined: 08-January 10

Posted 09 August 2010 - 08:44 PM

I would like to know this as well. Haven't been able to get a behavior to serve actions in a controller transparently.

If it's not possible, is there a technical reason for that, or has it just not been done yet?

Thank you!
0

#3 User is offline   Y!! 

  • Advanced Member
  • Yii
  • Group: Yii Dev Team
  • Posts: 978
  • Joined: 18-June 09

Posted 09 August 2010 - 09:11 PM

No this is not possible yet because CController::createAction() checks if "actionExample" is a valid method for the given controller. Behavior methods are no real methods within a controller since they get called via magic __get().

For now, as workaround I guess you can override CController::createAction() or CController::missingAction() in some way to get it work.

But I agree, would be nice enhancement.
0

#4 User is offline   rAWTAZ 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 102
  • Joined: 08-January 10

Posted 11 August 2010 - 12:57 PM

I have the feeling I'm missing something obvious here since I haven't found much questioning about this feature. But anyway I filed a ticket #1465. Vote for it if you'd like to see it happen :)
0

#5 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,016
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 12 August 2010 - 02:24 AM

Did you try to override actions() in the behavior? You would have to create action classes with your behavior for this to work. But at least you can inject actions.

You should also merge $this->owner->actions() with the behaviors action to make behavoir actions() and controller actions() coexist nicely.

Not tested.
0

#6 User is offline   Y!! 

  • Advanced Member
  • Yii
  • Group: Yii Dev Team
  • Posts: 978
  • Joined: 18-June 09

Posted 12 August 2010 - 06:40 AM

View PostMike, on 12 August 2010 - 02:24 AM, said:

Did you try to override actions() in the behavior? You would have to create action classes with your behavior for this to work. But at least you can inject actions.

You should also merge $this->owner->actions() with the behaviors action to make behavoir actions() and controller actions() coexist nicely.

Not tested.


I don't think this will work. Behavior methods won't override any methods from the controller.
0

#7 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,016
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 12 August 2010 - 06:52 AM

You're right - partially. ;)

I forgot that CComponent::__call() is only used, if the called method isn't found. So this method will only work, if the controller doesn't already have a action() method.

Maybe using a behavior for adding actions to a controller is a bad idea. We already have the actions() method to import actions from outside. So better use this mechanism if you want to create reusable actions.
0

#8 User is offline   Y!! 

  • Advanced Member
  • Yii
  • Group: Yii Dev Team
  • Posts: 978
  • Joined: 18-June 09

Posted 12 August 2010 - 08:36 AM

View PostMike, on 12 August 2010 - 06:52 AM, said:

You're right - partially. ;)

I forgot that CComponent::__call() is only used, if the called method isn't found. So this method will only work, if the controller doesn't already have a action() method.


For now it won't work at all since CController checks for valid method in controller instance (no magic involved there). Or do you mean the actions() method? This won't work as well since CController returns empty array by default. Means actions() in a behavior won't have any effect.

View PostMike, on 12 August 2010 - 06:52 AM, said:

Maybe using a behavior for adding actions to a controller is a bad idea. We already have the actions() method to import actions from outside. So better use this mechanism if you want to create reusable actions.


Not sure yet if/how I would use it in a project, but I'm pretty sure there is some use for it. At least it makes sense to implement it I think.
0

#9 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,016
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 12 August 2010 - 09:03 AM

View PostY!!, on 12 August 2010 - 08:36 AM, said:

For now it won't work at all since CController checks for valid method in controller instance (no magic involved there). Or do you mean the actions() method? This won't work as well since CController returns empty array by default. Means actions() in a behavior won't have any effect.


How right you are :). I forgot the default implementation of actions() in CController.
0

#10 User is offline   igorsantos07 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 17
  • Joined: 29-May 10
  • Location:Rio de Janeiro, Brazil

Posted 12 August 2010 - 09:09 AM

I don't think how I could help to improve this discussion, I'm just thinking it's being very productive (:

View PostY!!, on 12 August 2010 - 08:36 AM, said:

Not sure yet if/how I would use it in a project, but I'm pretty sure there is some use for it. At least it makes sense to implement it I think.


BTW, I can give you an example on how I would use this (I'm using the missingAction() tip rawtaz gave me at IRC and Y!! here)

I created a controller behavior to make it easier to upload videos to YouTube.
In fact, it have some internal methods that calls some Zend Classes and then an actionFormData() to retrieve POST URL and Token from YouTube.
This action is used in an AJAX request before the form submission, and would simply print a JSON.
0

#11 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,892
  • Joined: 04-October 08
  • Location:DC, USA

Posted 12 August 2010 - 09:14 AM

Why not develop an action class instead of a behavior?
0

#12 User is offline   igorsantos07 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 17
  • Joined: 29-May 10
  • Location:Rio de Janeiro, Brazil

Posted 12 August 2010 - 09:19 AM

View Postqiang, on 12 August 2010 - 09:14 AM, said:

Why not develop an action class instead of a behavior?


"Because I didn't think about that" would be a nice answer? haha

I'll consider your somewhat OBVIOUS idea and try to implement it this night (: (brazilian guy here, 11AM now)
0

#13 User is offline   Mimin 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 03-January 11
  • Location:Moscow

Posted 03 January 2011 - 09:48 AM

This is a modification of createAction to provide possibility of attaching several actions using behaviors. But there is one inconvenience - you should use $this->owner to operate with controller object.


public function createAction($actionID)
{
   if($actionID==='') $actionID=$this->defaultAction;
   if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method
   {
       return new CInlineAction($this,$actionID);
   }
   elseif ($a = $this->createActionFromMap($this->actions(),$actionID,$actionID))
   {
        return $a;
   }
   elseif (($behaviorList = $this->behaviors()) && is_array($behaviorList))
   {	
        foreach ($behaviorList as $behaviorId=>$data)
        {
             if(is_object($behaviorObj = $this->asa($behaviorId)) &&  
method_exists($behaviorObj,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method
             {
                 return new CInlineAction($behaviorObj, $actionID);
             }
         }
    }
}	


0

#14 User is offline   Mimin 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 03-January 11
  • Location:Moscow

Posted 04 January 2011 - 08:41 AM

i found that the solution i provided is useless :) you guys, should use modules instead of actions by behavioring
0

#15 User is offline   FaceySpacey 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 28
  • Joined: 27-October 09
  • Location:New York City

Posted 26 March 2011 - 12:33 PM

@Mimin your solution worked perfectly. What was the problem? Also, I don't understand how modules does something comparable to having a behavior with multiple actions?

so unless your method breaks other stuff, there's nothing wrong with it. It makes perfect sense for a scenario where you have a bunch of related actions, perhaps for ajax purposes all for one page. You can easily port the behavior to a new project and have all the ajax stuff running instantaneously just by adding this behavior. that's what im doing, and it's awesome! thanks bro.
http://www.faceyspacey.com - I'm a Web 2.0 Development Monster.
0

#16 User is offline   Mimin 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 03-January 11
  • Location:Moscow

Posted 26 March 2011 - 01:55 PM

@FaceySpacey you're welcome! Actually, i can't remember now what made myself confused about this solution. lol :) But it doesn't break other stuff definitely.
1

#17 User is offline   rAWTAZ 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 102
  • Joined: 08-January 10

Posted 18 September 2011 - 12:32 PM

View Postqiang, on 12 August 2010 - 09:14 AM, said:

Why not develop an action class instead of a behavior?

Using action classes is fine (and I've done so a number of times) when it's about one/the occasional action, but if you are making for example an extension that provides a number of actions actions (possibly in combination with other stuff like a widget needing to call home to the actions you want to provide here), then it's way cleaner for the user of the extension to add one little behavior instead of a list of action classes, to the controller. That is the main reason I want actions in behaviors, for extensions (base controller classes are not a good option here, for obvious reasons).

When a request comes in, the controller checks for an action method on itself. If none is found it uses the action class map to find an action. Adding to that a third check for action methods in the controller's behaviors, that runs after the two aforementioned checks, shouldn't add a notable overhead except for when the behavior *should* actually be used to handle the request (in which case it should be fine). Right, or am I missing something obvious in that reasoning?

Apart from that I guess there's a slight overhead when the behavior is added to the controller, but seriously that cannot be much, can it? In worst case, if someone has such *extreme* performance requirements that they cannot afford adding a behavior to a controller with the overhead of having it initialized, then they can simply use action classes instead.

Thanks for listening!
1

#18 User is offline   samdark 

  • Having fun
  • Yii
  • Group: Yii Dev Team
  • Posts: 3,616
  • Joined: 17-January 09
  • Location:Russia

Posted 19 September 2011 - 02:38 PM

I think it was done in one of extensions. If I remember correctly it was http://www.yiiframew...stedtreeactions
Yii 1.1 Application Development Cookbook

Enjoying Yii? Star us at github: 1.1 and 2.0.
0

#19 User is offline   arash 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 38
  • Joined: 11-April 11

Posted 21 September 2011 - 01:09 AM

View PostrAWTAZ, on 18 September 2011 - 12:32 PM, said:

Using action classes is fine (and I've done so a number of times) when it's about one/the occasional action, but if you are making for example an extension that provides a number of actions actions (possibly in combination with other stuff like a widget needing to call home to the actions you want to provide here), then it's way cleaner for the user of the extension to add one little behavior instead of a list of action classes, to the controller. That is the main reason I want actions in behaviors, for extensions (base controller classes are not a good option here, for obvious reasons).


Why not develop a widget class and use it as an action provider? see this wiki for more detail.
0

#20 User is offline   Kevin Higgins 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 16
  • Joined: 05-May 11

Posted 08 June 2012 - 10:54 AM

I found what Mimin posted to be pretty useful. Here's an easy version to plug into your code.

Just have your controller extend BehaviorProxyController, and then either override behaviors() or use attachBehavior() to add actions to the controller.

<?php
class BehaviorProxyController extends Controller
{
	private $_behaviorIDs = array();

	public function createAction($actionID)
	{
		$action = parent::createAction($actionID);
		if($action !== null)
			return $action;
		foreach($this->_behaviorIDs as $behaviorID)
		{
			$object = $this->asa($behaviorID);
			if($object->getEnabled() && method_exists($object,'action'.$actionID))
				return new CInlineAction($object,$actionID);
		}
	}

	public function attachBehavior($name, $behavior)
	{
		$this->_behaviorIDs[] = $name;
		parent::attachBehavior($name, $behavior);
	}
}


public function init()
{
	$this->attachBehavior('SomeControllerBehavior', array(
		'class'=>'SomeControllerBehavior',
	));
	return parent::init();
}

OR

public function behaviors()
{
	return array(
		'SomeControllerBehavior'=>array(
			'class'=>'SomeControllerBehavior',
		)
	);
}


1

Share this topic:


  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users