2

Is there any examples of using __call in a controller to replace a specific action method.

I have a situation where I need to provide different endpoints to the same controller. A reference to each of these end points is available in the database and so instead of hardcoding a method per endpoint I would like every request that hits the controller to be handled by one method.

The only way I have got this to come close to working is by sticking exit and other similar undesirable functions within the __call method. Surely it's possible to handle the request from here and tell magento not to look for a specific method?

2 Answers 2

5

Alternatively, you could extend the preDispatch method of your controller, check which actions are allowed and which not (I recommend you against calling exit, send a proper HTTP status instead), and redirect the request to a single action in your controller, say foobarAction, by using $this->_forward('foobar').

UPDATE

I tested my solution, have a look at Mage_Core_Controller_Varien_Router_Standard::match. This calls a method hasAction on your controller. If you extend that method in the controller and implement your checks (or simply return true, even though I don't recommend it), then you can do what I suggested. Also, since everything happens in preDispatch, to prevent the loop you get add something like this:

if ($this->getRequest()->getActionName() !== 'foobar') { $this->_forward('foobar'); } 

Here for your reference is what I did:

class Acme_Module_FoobarController extends Mage_Core_Controller_Front_Action { public function preDispatch() { parent::preDispatch(); if ($this->getRequest()->getActionName() !== 'foobar') { $this->_forward('foobar'); } return $this; } public function foobarAction() { $this->getResponse()->setBody('Hey!'); } public function hasAction($action) { return true; } } 
6
  • hey thanks for the answer. I'm getting a 404 though. I have a function named testAction and i call it in preDispath() with $this->_forward('test');. If I go to a url based on a method that exists in the controller there is no problem so I think the controller is set up correctly, Commented Nov 17, 2015 at 9:34
  • I also get an exception if the method exists in the controller Front controller reached 100 router match iterations Commented Nov 17, 2015 at 9:35
  • Sorry, I haven't tested my answer, but now I did and it does work. I'm going to update my answer. Commented Nov 17, 2015 at 10:21
  • Still getting a 404 on that. My controller is now extending Mage_Core_Controller_Varien_Router_Standard and match() returns true. In my preDispath() function I added your code if ($this->getRequest()->getActionName() !== 'foobar') { $this->_forward('foobar'); }. Fyi foobar() exists as foobarAction() Commented Nov 17, 2015 at 10:47
  • @develophper see my code above, works for me. Commented Nov 17, 2015 at 10:54
4

[Edit]
It seams you cannot use __call in controllers starting from version CE - 1.8 (and EE 1.13)
it was possible up to 1.7 because of the method Mage_Core_Controller_Varien_Action::hasAction.
in 1.7 and lower it looked like this:

public functionhasAction($action) { return is_callable(array($this, $this->getActionMethodName($action))); } 

Starting 1.8 it looks like

public functionhasAction($action) { return method_exists(array($this, $this->getActionMethodName($action))); } 

This method is called in the standard router when handling the request:

Mage_Core_Controller_Varien_Router_Standard::match

... if (!$controllerInstance->hasAction($action)) { continue; } ... 

So starting from CE 1.8 the method somethingAction really has to exist, not just be callable.

[Original Answer - works only for versions before CE-1.8]
yes, you can definitely use __call in a controller.
Check this example
but your __call will be called for each method that does not exist in the controller.
So you have to make sure that your method called ends with Action.
Something like this:

public function __call($function, $args){ if (substr($function, -6) == 'Action') { $this->loadLayout('some_default_layout_handle'); $this->renderLayout(); } else {//otherwise throw an exeption throw new Exception ("Method {$function} cannot be called in ". __CLASS__); } } 

or, if you want to also get the function name you can do this:

public function __call($function, $args){ if (substr($function, -6) == 'Action') { $functionName = substr($function, 0, strlen($function) - 6); //do something with $functionName } else {//otherwise throw an exeption throw new Exception ("Method {$function} cannot be called in ". __CLASS__); } } 

so if you call ROOT/module/controller/something (and somethingAction() does not exist) you end up in the __call method.
in the first case you end up with a default page you set, in the second case $functionName will be equal with something.

4
  • Thanks @marius. I'm trying to get the second option you gave to work by just putting echo 'this is a test'; exit; in the if statement but all I'm getting is a 404. Given the controller takes request to other methods I think that part is set up correctly. Are there any other steps you can think of? Commented Nov 17, 2015 at 9:22
  • what version of Magento are you using? I want to check if this still works on newer versions. It worked for me in 1.7 Commented Nov 17, 2015 at 9:29
  • enterprise edition 1.14.2.2 Commented Nov 17, 2015 at 9:30
  • @develophper. I checked again. It seams you cannot use __call starting from ce1.8. I've updated the answer. You will have to go a different direction. Like using a custom router for example. Commented Nov 17, 2015 at 9:44

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.