我希望能够编写类似控制器的类,并将它们作为供应商提供给最终应用程序。然后,最终应用程序控制器可以实例化我的类并调用它们的操作方法。
问题是我希望自动调用它们。我必须避免在结束控制器中重复每个动作方法。
我试过这个:
class IndexController
{
protected $vendorController;
public function initialize()
{
$this->vendorController = new VendorController();
}
public function __call($name, $args)
{
call_user_func_args([$this->vendorController, $name], $args);
}
}
VendorController
类看起来像这样:
class VendorController
{
public function testAction()
{
die("this is test action");
}
}
但它给了我
Action 'test' was not found on handler 'index'
我假设 I've checked Phalcon内部做了一些检查,如下所示:
if (method_exists($controller, $actionName)) { // ...
导致未找到行动。
也许我可以以某种方式覆盖这种行为?我正在使用我自己的调度程序类来扩展Phalcon的本地调度程序,如果这有任何帮助的话。但我试图覆盖像getHandlerName()
这样的方法而没有任何成功。
我也试过使用特征:
trait TVendorController
{
public function testAction()
{
die("this is test action");
}
}
class IndexController
{
use TVendorController;
}
这种方法有效,但有一个重大缺陷:
如果IndexController
扩展某种包含BaseController
或initialize()
等方法的beforeExecuteRoute()
(通常就是这种情况),那么很容易忘记调用他们。这里的威胁是BaseController
方法可以包含例如身份验证逻辑:
trait TVendorController
{
public function beforeExecuteRoute()
{
// silently overrides the BaseController::beforeExecuteRoute()
}
public function testAction()
{
die("this is test action");
}
}
abstract class BaseController
{
public function beforeExecuteRoute()
{
// OOPS: this is never called
$username = $this->session->get('username', null);
if (!$username) {
$this->response->redirect('index/login');
}
}
}
class IndexController extends BaseController
{
use TVendorController;
}
如果是第一种方法:如何告诉Phalcon的事件循环该操作实际存在于控制器中?
如果无法完成,那么:
答案 0 :(得分:3)
您可以尝试使用dispatch:beforeDispatch
附件添加EventManager。在dispatch:beforeDispatch
时刻,您知道操作名称,但在检查操作是否确实存在之前应该可以命中它。
在beforeDispatch
函数中,您可能需要检查,如果传递的操作名称在控制器内是可调用的,如果没有,您应该能够转发到execvendorAction
方法的某个王。
我做过这个事件管理:
$di->setShared('dispatcher', function() {
$dispatcher = new Dispatcher();
$eventsManager = new EventsManager();
$eventsManager->attach("dispatch:beforeDispatch", function($event, $dispatcher) {
$controllerName = $dispatcher->getControllerClass();
$action = $dispatcher->getActionName();
if(!method_exists($controllerName, $action . 'Action')) {
$dispatcher->forward(array(
// not delivering 'controller' param to make it stay
// in current one
'action' => 'vendor',
'params' => array(
'name' => $action,
'params' => $dispatcher->getParams()
)
));
}
});
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
});
然后,我在controllerBase
添加了这样一个动作:
public function vendorAction($name, $arguments) {
var_dump(array(
'name' => $name,
'arguments' => $arguments
));
// $name .= 'Action';
if(method_exists(array($this->vendorController, $name))) {
call_user_func_args(array($this->vendorController, $name), $arguments);
} else {
// work it out
}
}
让它运转起来。从controllerBase
延伸的每个控制器都将运行您在其中传递的方法vendorController
。您将不得不以自己的方式处理notFound
异常,因为这是一个非常非标准的实现。
使用经典路由器/调度程序/ Phalcon上的所有内容进行测试。