Я хотел бы иметь возможность писать классы, подобные контроллерам, и предоставлять их в качестве поставщика конечным приложениям. Затем контроллер конечного приложения может просто создать экземпляр моего класса и вызвать их методы действия.
Дело в том, что я хочу, чтобы они вызывались автоматически. Я должен избегать повторения каждого метода действия в контроллере конца.
Я пробовал это:
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'
Я предполагаю, что Я проверил что 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, что действие действительно существует в контроллере?
Или, если это невозможно сделать, тогда:
Вы можете попробовать добавить EventManager с помощью dispatch:beforeDispatch
крепление. В 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.
Других решений пока нет …