Skip to content

Commit

Permalink
Added support for dispatch error layout config.
Browse files Browse the repository at this point in the history
  • Loading branch information
fhein committed Nov 6, 2013
1 parent 879e8b2 commit 530d3a7
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 53 deletions.
15 changes: 13 additions & 2 deletions README.md
@@ -1,6 +1,6 @@
MxcLayoutScheme
===============
Version 0.2.1 created by Frank Hein and the mxc-commons team.
Version 0.3.0 created by Frank Hein and the mxc-commons team.

MxcLayoutScheme is part of the [maxence openBeee initiative](http://www.maxence.de/mxcweb/index.php/themen/open-business/)
by [maxence business consulting gmbh, Germany](http://www.maxence.de).
Expand All @@ -12,7 +12,10 @@ Did you ever want to apply a different layout phtml based on the route matched o

MxcLayoutScheme allows to dynamically exchange the layout template used by the renderer. You define layout schemes which are a collection of rules to select layouts. Within each scheme you can assign a distinct layout to a particular route matched. Further you can define a distinct layout for each module, controller and action.

Further, MxcLayoutScheme supports the configuration child ViewModels together with associated view templates to get rendered to captures you define.
MxcLayoutScheme supports the configuration child ViewModels together with associated view templates to get rendered to captures you define.

Further, MxcLayoutScheme intercepts dispatch errors. You can apply layouts for particular error codes and http status codes the same way you do for routes
and controllers.

MxcLayoutScheme provides an event interface to allow you to select the layout scheme applied at bootstrap time.

Expand Down Expand Up @@ -56,6 +59,8 @@ view template as far as possible. We want to achieve that within the controller

**8. Provide a controller plugin to control scheme selection and setup of layout variables**

**9. Provide support for dispatch errors**

In the current version you can either assign the layout variables within the controller action via `layoutScheme` controller plugin. Alternatively you may supply an event handler for pre- and postprocessing. We provide an example here.

Installation
Expand Down Expand Up @@ -117,6 +122,12 @@ Within each scheme definition the following sections are available:
- **mca_layouts** - Array of rules: `<moduleName>[\<controllerName>[\<actionName>]] =>` `array( 'layout' => <templateName>, ['child_view_models' => array ( 'capture' => 'childTemplateName', ... )]`, where moduleName is the name of the module, controllerName is the name of the controller (without the "Controller" suffix (if class name is IndexController then controllerName is Index)), actionName is the name of the the controller action. Setup for each
`<moduleName>[\<controllerName>[\<actionName>]]` is the same as in `route_layouts` (see above).
Default: `array()`
- **error_layouts** - Array of rules: `<error> =>` `array( 'layout' => <templateName>, ['child_view_models' => array ( 'capture' => 'childTemplateName', ... )]`, where error is the errorcode from the `DISPATCH_ERROR` event . Setup for each
`<error>` is the same as in `route_layouts` (see above).
Default: `array()`
- **http\_status_layouts** - Array of rules: `<status> =>` `array( 'layout' => <templateName>, ['child_view_models' => array ( 'capture' => 'childTemplateName', ... )]`, where status is the status from the `DISPATCH_ERROR` event's response object . Setup for each
`<status>` is the same as in `route_layouts` (see above).
Default: `array()`
- **default** - Array of rules: `'global'` => `<templateName>`. 'global' currently is the only key supported by MxcLayoutScheme. Default: `array()`
- **default_child_view_models** - `array <templateName> => array( <capture> => <childTemplateName>, ...)`. For each template identified by `templateName` you can define a list of child View Models. `capture` identifies the capture the child ViewModel's template idenitified by `templateName` gets rendered to.

Expand Down
2 changes: 1 addition & 1 deletion composer.json
@@ -1,5 +1,5 @@
{
"name": "mxc-commons/mxc-layoutscheme",
"name": "mxc-commons/mxc-layoutscheme",
"description": "Zend Framework 2 Module that allows to define layout schemes and apply custom layouts to routes, modules, controllers and actions.",
"type": "library",
"keywords": [
Expand Down
12 changes: 12 additions & 0 deletions config/mxclayoutscheme.global.php.dist
Expand Up @@ -50,6 +50,18 @@ $mxclayoutscheme = array(
// 'layout' => 'module3/layout',
// ),
// ),
// // Example scheme to demonstrate the setup of error layouts
//
// 'error_layouts' => array(
// 'error-controller-unauthorized' => array
// 'layout' => 'application/error',
// ),
// ),
// 'http_status_layouts' => array(
// '403' => array
// 'layout' => 'application/403',
// ),
// ),
// // default to standard zf2 layout for modules not registered
// // Note: If you do not specify a default global layout the application standard gets used
// 'default' => array(
Expand Down
39 changes: 39 additions & 0 deletions src/MxcLayoutScheme/Service/LayoutSchemeOptions.php
Expand Up @@ -26,6 +26,16 @@ class LayoutSchemeOptions extends AbstractOptions {
*/
protected $routeLayouts = array();

/**
* @var array()
*/
protected $errorLayouts = array();

/**
* @var array()
*/
protected $httpStatusLayouts = array();

/**
* @var array
*/
Expand Down Expand Up @@ -119,5 +129,34 @@ public function setDefaultChildViewModels($defaultChildViewModels) {
$this->defaultChildViewModels = $defaultChildViewModels;
}

/**
* @return the $errorLayouts
*/
public function getErrorLayouts() {
return $this->errorLayouts;
}

/**
* @param \MxcLayoutScheme\Service\array() $errorLayouts
*/
public function setErrorLayouts($errorLayouts) {
$this->errorLayouts = $errorLayouts;
}
/**
* @return the $httpStatusLayouts
*/
public function getHttpStatusLayouts() {
return $this->httpStatusLayouts;
}

/**
* @param \MxcLayoutScheme\Service\array() $httpStatusLayouts
*/
public function setHttpStatusLayouts($httpStatusLayouts) {
$this->httpStatusLayouts = $httpStatusLayouts;
}




}
150 changes: 100 additions & 50 deletions src/MxcLayoutScheme/Service/LayoutSchemeService.php
Expand Up @@ -8,19 +8,20 @@
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ProvidesEvents;
use Zend\Stdlib\Parameters;
use Zend\ServiceManager\ServiceManager;
use Zend\Filter\Word\CamelCaseToDash;
use MxcLayoutScheme\Service\LayoutSchemeServiceOptions;
use MxcLayoutScheme\Service\LayoutSchemeOptions;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class LayoutSchemeService implements ListenerAggregateInterface
class LayoutSchemeService implements ListenerAggregateInterface, ServiceLocatorAwareInterface
{
const HOOK_PRE_SELECT_SCHEME = 'pre-select-scheme';
const HOOK_POST_SELECT_LAYOUT = 'post-select-layout';

use ProvidesEvents;

protected $serviceManager;
protected $serviceLocator;

protected $options = null;
protected $schemeOptions = array();
Expand All @@ -30,15 +31,11 @@ class LayoutSchemeService implements ListenerAggregateInterface
protected $hasPluginSetActiveScheme = false;
protected $skipPreSelectSchemeEvent = false;
protected $skipPostSelectLayoutEvent = false;

public function __construct(ServiceManager $sm) {
$this->setServiceManager($sm);
$this->params = new Parameters;
}

public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'),-1000);
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($this, 'onDispatchError'),-1000);
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'),-1000);
}

/**
Expand Down Expand Up @@ -68,12 +65,11 @@ public function onDispatch(MvcEvent $e)
$controllerclass = get_class($ctrl);
$controller = substr($controllerclass,strrpos($controllerclass,'\\')+1);
$routeMatch = $e->getRouteMatch();
$this->params
->set('controller',$ctrl)
->set('moduleName',substr($controllerclass,0,strpos($controllerclass,'\\')))
->set('controllerName',substr($controller,0,strrpos($controller,'Controller')))
->set('actionName',$routeMatch->getParam('action'))
->set('routeName',$routeMatch->getMatchedRouteName());
$this->setParam('controller',$ctrl)
->setParam('moduleName',substr($controllerclass,0,strpos($controllerclass,'\\')))
->setParam('controllerName',substr($controller,0,strrpos($controller,'Controller')))
->setParam('actionName',$routeMatch->getParam('action'))
->setParam('routeName',$routeMatch->getMatchedRouteName());

// event handler may modify the active scheme
if (!$this->getSkipPreSelectSchemeEvent()) {
Expand All @@ -87,6 +83,28 @@ public function onDispatch(MvcEvent $e)
}
}

public function onDispatchError(MvcEvent $e) {
$model = $e->getResult();
if (!$model instanceof ViewModel) {
return;
}
$response = $e->getResponse();
$statusCode = ($response) ? $response->getStatusCode() : null;
$error = $e->getError();
$this->setParam('statusCode', $statusCode);
$this->setParam('error', $error);

// event handler may modify the active scheme
if (!$this->getSkipPreSelectSchemeEvent()) {
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_PRE_SELECT_SCHEME, $this);
}
$this->SelectErrorLayout();
// event handler may add variables to the layout
if (!$this->getSkipPostSelectLayoutEvent()) {
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_POST_SELECT_LAYOUT, $this);
}
}

/**
* select template according to active scheme
*/
Expand Down Expand Up @@ -114,6 +132,24 @@ public function selectLayout() {
if ($this->applyLayout($schemeOptions->getDefault(),'global')) return;

}
/**
* select template according to error or response status scheme
*/
public function selectErrorLayout() {

$schemeOptions = $this->getActiveSchemeOptions();

$templates = $schemeOptions->getErrorLayouts();
$error = $this->getParam('error');
if ($this->applyLayout($templates, $error)) return;

$templates = $schemeOptions->getHttpStatusLayouts();

$status = (string) $this->getParam('statusCode');
if ($this->applyLayout($templates, $status)) return;

if ($this->applyLayout($schemeOptions->getDefault(),'global')) return;
}

/**
* @param array $templates
Expand All @@ -124,22 +160,20 @@ public function selectLayout() {
public function applyLayout($templates,$key) {

if (!isset($templates[$key]['layout'])) return false;
$this->params->get('controller')->layout($templates[$key]['layout']);
$this->applyChildViewModels($templates, $key);
$view = $this->getServiceLocator()->get('view_manager')->getViewModel();
$view->setTemplate($templates[$key]['layout']);
$this->applyChildViewModels($view, $templates, $key);
return true;
}

/**
* @param $templates
* @param $key
* @param ViewModel $layout
* @param array $templates
* @param string $key
*
* @return bool
*/
public function applyChildViewModels($templates, $key) {
$controller = $this->getParam('controller', null);

//--- return if we do not have a controller instance
if (!$controller) return;
public function applyChildViewModels($layout, $templates, $key) {
$templateName = $templates[$key]['layout'];
$childViewModelList = $this->getActiveSchemeOptions()->getDefaultChildViewModels();
$childViewModels = isset($childViewModelList[$templateName]) ? $childViewModelList[$templateName] : null;
Expand All @@ -152,21 +186,20 @@ public function applyChildViewModels($templates, $key) {
$childViewModels = array_merge($childViewModels,$overrideChildViewModels);
}

$layout = $controller->layout();

$filter = new CamelCaseToDash();

foreach ($childViewModels as $capture => $template) {
switch ($template) {
case null:
case '<default>':
$template = strtolower($filter->filter($this->params->get('moduleName').'\\'.
$this->params->get('controllerName').'\\'.
$this->params->get('actionName').'-'.
$template = strtolower($filter->filter($this->getParam('moduleName').'\\'.
$this->getParam('controllerName').'\\'.
$this->getParam('actionName').'-'.
$capture));
break;
case '<none>':
$template = null;
break;
default:
break;
}
Expand Down Expand Up @@ -196,10 +229,7 @@ public function pluginSetVariables($controller, $variables, $override = false) {
*/
public function setVariables($variables, $override = false) {
// apply variables to main layout view model
$controller = $this->getParam('controller');
if($controller) {
$controller->layout()->setVariables($variables, $override);
}
$layout = $this->getServiceLocator()->get('view_manager')->getViewModel()->setVariables($variables, $override);
// apply variables to all child view models
$views = $this->getChildViewModels();
foreach ($views as $view) {
Expand Down Expand Up @@ -234,7 +264,7 @@ protected function setChildViewModel($capture, $view) {
* @return mixed | ViewModel
*/
protected function pluginGetChildViewModel($controller, $capture, $default = null) {
if (!$this->completed) {
if (!$this->completed && $controller) {
$this->onDispatch($controller->getEvent());
}
return $this->getChildViewModel($capture, $default);
Expand All @@ -256,7 +286,7 @@ protected function getChildViewModel($capture, $default = null) {
*/
public function getOptions() {
if (!$this->options) {
$this->setOptions($this->getServiceManager()->get('mxclayoutscheme_service_options'));
$this->setOptions($this->getServiceLocator()->get('mxclayoutscheme_service_options'));
}
return $this->options;
}
Expand All @@ -267,20 +297,6 @@ public function getOptions() {
public function setOptions($options) {
$this->options = $options;
}
/**
* @return $serviceManager
*/
public function getServiceManager() {
return $this->serviceManager;
}

/**
* @param ServiceManager $serviceManager
*/
public function setServiceManager(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}


/**
* @return the $childViewModels
Expand Down Expand Up @@ -312,9 +328,19 @@ public function setChildViewModels($childViewModels) {
* @return param($name,$default)
*/
public function getParam($name, $default = null) {
return $this->params->get($name, $default);
return $this->getParams()->get($name, $default);
}

/**
* @param string $name
* @param mixed $value
* @return $this
*/
public function setParam($name, $value) {
$this->getParams()->set($name, $value);
return $this;
}

/**
* set active scheme in associated options object
*
Expand Down Expand Up @@ -389,4 +415,28 @@ public function setSkipPreSelectSchemeEvent($skipPreSelectSchemeEvent) {
public function setSkipPostSelectLayoutEvent($skipPostSelectLayoutEvent) {
$this->skipPostSelectLayoutEvent = $skipPostSelectLayoutEvent;
}

/**
* @return the $params
*/
public function getParams() {
if (!$this->params) {
$this->params = new Parameters();
}
return $this->params;
}

/**
* @param ServiceLocatorInterface $serviceLocator
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}

/**
* @return the $serviceLocator
*/
public function getServiceLocator() {
return $this->serviceLocator;
}
}

0 comments on commit 530d3a7

Please sign in to comment.