Как заставить Phalcon делегировать действия другим контроллерам?

Я хотел бы иметь возможность писать классы, подобные контроллерам, и предоставлять их в качестве поставщика конечным приложениям. Затем контроллер конечного приложения может просто создать экземпляр моего класса и вызвать их методы действия.

Дело в том, что я хочу, чтобы они вызывались автоматически. Я должен избегать повторения каждого метода действия в контроллере конца.

Я пробовал это:

Код

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, что действие действительно существует в контроллере?

Или, если это невозможно сделать, тогда:

  1. Как написать свой контроллер контроллеров?
  2. Как объединить их в контроллеры конечных приложений, чтобы действия вызывались автоматически?

3

Решение

Вы можете попробовать добавить 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.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]