diff --git a/README.md b/README.md index 2779fde..232971f 100644 --- a/README.md +++ b/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). @@ -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. @@ -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 @@ -117,6 +122,12 @@ Within each scheme definition the following sections are available: - **mca_layouts** - Array of rules: `[\[\]] =>` `array( 'layout' => , ['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 `[\[\]]` is the same as in `route_layouts` (see above). Default: `array()` +- **error_layouts** - Array of rules: ` =>` `array( 'layout' => , ['child_view_models' => array ( 'capture' => 'childTemplateName', ... )]`, where error is the errorcode from the `DISPATCH_ERROR` event . Setup for each +`` is the same as in `route_layouts` (see above). +Default: `array()` +- **http\_status_layouts** - Array of rules: ` =>` `array( 'layout' => , ['child_view_models' => array ( 'capture' => 'childTemplateName', ... )]`, where status is the status from the `DISPATCH_ERROR` event's response object . Setup for each +`` is the same as in `route_layouts` (see above). +Default: `array()` - **default** - Array of rules: `'global'` => ``. 'global' currently is the only key supported by MxcLayoutScheme. Default: `array()` - **default_child_view_models** - `array => array( => , ...)`. 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. diff --git a/composer.json b/composer.json index f02a354..b7d86a3 100644 --- a/composer.json +++ b/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": [ diff --git a/config/mxclayoutscheme.global.php.dist b/config/mxclayoutscheme.global.php.dist index a13386d..39b02ec 100644 --- a/config/mxclayoutscheme.global.php.dist +++ b/config/mxclayoutscheme.global.php.dist @@ -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( diff --git a/src/MxcLayoutScheme/Service/LayoutSchemeOptions.php b/src/MxcLayoutScheme/Service/LayoutSchemeOptions.php index c975e4e..337f7e9 100644 --- a/src/MxcLayoutScheme/Service/LayoutSchemeOptions.php +++ b/src/MxcLayoutScheme/Service/LayoutSchemeOptions.php @@ -26,6 +26,16 @@ class LayoutSchemeOptions extends AbstractOptions { */ protected $routeLayouts = array(); + /** + * @var array() + */ + protected $errorLayouts = array(); + + /** + * @var array() + */ + protected $httpStatusLayouts = array(); + /** * @var array */ @@ -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; + } + + + } \ No newline at end of file diff --git a/src/MxcLayoutScheme/Service/LayoutSchemeService.php b/src/MxcLayoutScheme/Service/LayoutSchemeService.php index 8045d9e..e4769a0 100644 --- a/src/MxcLayoutScheme/Service/LayoutSchemeService.php +++ b/src/MxcLayoutScheme/Service/LayoutSchemeService.php @@ -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(); @@ -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); } /** @@ -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()) { @@ -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 */ @@ -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 @@ -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; @@ -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 '': - $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 '': $template = null; + break; default: break; } @@ -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) { @@ -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); @@ -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; } @@ -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 @@ -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 * @@ -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; + } }