在控制器分派后修改ViewModel

时间:2014-05-13 10:48:18

标签: php zend-framework2

我试图在呈现之前拦截ViewModel,并将其添加到另一个“父”视图模型 - 就像ZF2布局包裹控制器返回内容的方式一样。

执行此操作的简单(工作)方法是在每个控制器操作中。

public function dashboardAction() { 

  $dashboardContent = new ViewModel(array('foo' => 'bar'));

  $parent = new ViewModel();
  $parent->setTemplate('foo/bar/parent-template'); 

  $parent->addChild($dashboardContent, 'content');

  return $parent;
}

这按预期工作,'child'视图正确嵌套在最终输出的'parent'中。

因为我有许多控制器/动作都应该以相同的方式行事(通过他们的路线名称解决);我希望将其封装在事件监听器中。

public function onBootstrap(MvcEvent $event)
{
    $application  = $event->getApplication();
    $eventManager = $application->getEventManager();

    $eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'addUserAccountLayout'), -100);
}

public function addUserAccountLayout(EventInterface $event)
{
    $routeMatch = $event->getRouteMatch();
    $controller = $event->getTarget();
    $result     = $event->getResult();

    if (! $result instanceof ViewModel || $result instanceof JsonModel) {
        return;
    }

    if (! $routeMatch instanceof RouteMatch || 0 !== strpos($routeMatch->getMatchedRouteName(), 'zfcuser') || $result->terminate()) {
        return;
    }

    $application    = $event->getApplication();
    $serviceManager = $application->getServiceManager();
    $accountService = $serviceManager->get('JobboardUser\Service\AccountService');

    $user = $accountService->getCurrentUser();

    $parent = new ViewModel();
    $parent->setVariables(compact('user'));
    $parent->setTemplate('jobboard-user/widget/user-account');
    $parent->addChild($result, 'content');

    $event->setResult($parent); 
}

然而这不起作用;渲染普通视图(没有父视图)。我的猜测是因为我要么没有使用正确的事件或事件优先级,要么$event->setResult($view)不是分配结果的正确方法。

如何在事件侦听器中修改然后重新分配视图?

2 个答案:

答案 0 :(得分:1)

我无法解释为什么'但-10的优先级似乎是让您使用已有代码的最佳位置。

我确实有一个建议,而不是听每个模块控制器触发的每个调度事件,然后必须检查它是否是一个zfcuser路由,您可以使用共享事件管理器专门收听您对...感兴趣的ZfcUser控制器...

public function onBootstrap(MvcEvent $event)
{
    $application  = $event->getApplication();
    $eventManager = $application->getEventManager();
    $sharedEvents = $eventManager->getSharedManager();

    $sharedEvents->attach(
        'ZfcUser\Controller\UserController', // controller FQCN to listen to
        MvcEvent::EVENT_DISPATCH, 
        array($this, 'addUserAccountLayout'), 
       -10
    );
}

如果您这样做,则可以完全删除回调中的路径匹配检查0 !== strpos($routeMatch->getMatchedRouteName(), 'zfcuser')

答案 1 :(得分:1)

我问过这件事已经有一段时间了;但我确实找到了我追求的解决方案。这确实与新ViewModel如何附加回MvcEvent

有关

之前我曾试图设置事件的结果属性而没有运气:

$mvcEvent->setResult($myNewViewModel);

关键是首先获取MvcEvent自己的视图模型,然后将我的自定义视图模型附加到其中。

$mvcEvent->getViewModel()->addChild($myNewViewModel, 'content');

我还纳入了@Crisp关于活动管理者目标的观点。以前,我附加到主应用程序调度事件,这意味着将在每个调度事件上调用侦听器。

我现在专门针对我想听的控制器。

$eventManager->attach(array(
       'JobboardUser\Controller\AccountController',
       'JobboardUser\Controller\ProfileController'
    ), 
    MvcEvent::EVENT_DISPATCH, 
    array($this, 'addUserAccountLayout'),
    -80
 );

最终的侦听器代码如下所示

public function addUserAccountLayout(EventInterface $event)
{
    $result = $event->getResult();

    if (! $result instanceof ViewModel || $result instanceof JsonModel) {
        return;
    }

    $application    = $event->getApplication();
    $serviceManager = $application->getServiceManager();
    $accountService = $serviceManager->get('JobboardUser\Service\AccountService');
    $currentUser    = $accountService->getCurrentUser();

    $layout = new ViewModel();

    $layout->setVariables(array('user' => $currentUser));
    $layout->setTemplate('jobboard-user/widget/user-account');
    $layout->addChild($result, 'userContent');

    $event->getViewModel()->addChild($layout, 'content');
}