Using __call in a controller
-
13-12-2019 - |
Question
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?
Solution
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;
}
}
OTHER TIPS
[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
.