Skip to content

Commit 6da45a7

Browse files
committed
added layout var manipulation and event control
1 parent aae07cd commit 6da45a7

File tree

3 files changed

+167
-26
lines changed

3 files changed

+167
-26
lines changed

README.md

+34-6
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ view template as far as possible. We want to achieve that within the controller
5252

5353
**6. Allow configuration of a global default layout for each layout scheme**
5454

55-
**7. [TODO] Default (child) ViewModel variables (fixed and custom computed)**
55+
**7. Provide hooks for pre- and postprocessing**
5656

57-
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 postprocessing. We provide an example here.
57+
**8. Provide a controller plugin to control scheme selection and setup of layout variables**
58+
59+
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.
5860

5961
Installation
6062
------------
@@ -126,7 +128,7 @@ How MxcLayoutScheme works
126128
1. On Bootstrap MxcLayoutScheme hooks into the dispatch event of the application's EventManager with low priority (-1000).
127129
2. On Bootstrap MxcLayoutScheme instantiates the controller plugin `'layoutScheme'` to inject a reference to the application's ServiceManager instance.
128130
2. On dispatch MxcLayoutScheme evaluates the current route matched, the module name, the controller name and the action name.
129-
3. Then MxcLayoutScheme triggers an `MxcLayoutSchemeService::HOOK_PRE_SCHEME_SELECT` event. If you registered an event handler for that event in the bootstrap somewhere you can set the active scheme with `$e->getTarget()->setActiveScheme($schemeName)` with a `$schemeName`of your choice.
131+
3. Then MxcLayoutScheme triggers an `MxcLayoutSchemeService::HOOK_PRE_SELECT_SCHEME` event. If you registered an event handler for that event in the bootstrap somewhere you can set the active scheme with `$e->getTarget()->setActiveScheme($schemeName)` with a `$schemeName`of your choice. Alternatively, you can set the active scheme within the controller action using the controller plugin: `$this->layoutScheme()->setActiveScheme($schemeName)`.
130132
4. Then, MxcLayoutScheme loads the currently active scheme.
131133
5. MxcLayoutScheme checks the `route_layouts` for a key matching the matched route name. If the key exists the layout template registered to that match gets applied. If the rule defines child view models they get merged with the (optionally) defined default child view models and get applied to the layout. Go to 10.
132134
6. MxcLayoutScheme then checks the `mca_layouts` for a key matching Module\Conroller\Action. If the key exists the layout template registered to that match gets applied. If the rule defines child view models they get merged with the (optionally) defined default child view models and get applied to the layout. Go to 10.
@@ -151,7 +153,7 @@ Example Event Handler to choose the active scheme
151153
$em = $app->getEventManager();
152154
$sem = $em->getSharedManager();
153155
$sem->attach('MxcLayoutScheme\Service\LayoutSchemeService',
154-
LayoutSchemeService::HOOK_PRE_SCHEME_SELECT,
156+
LayoutSchemeService::HOOK_PRE_SELECT_SCHEME,
155157
function($e) {
156158
switch ($weather) {
157159
case 'sunny':
@@ -182,7 +184,7 @@ Example Event Handler to do some postprocessing after layout selection has finis
182184

183185
$sem = $em->getSharedManager();
184186
$sem->attach('MxcLayoutScheme\Service\LayoutSchemeService',
185-
LayoutSchemeService::HOOK_POST_LAYOUT_SELECT,
187+
LayoutSchemeService::HOOK_POST_SELECT_LAYOUT,
186188
function($e) {
187189
$variables = array(
188190
'berliner' => 'Ich',
@@ -297,9 +299,24 @@ From within a controller action you can access the controller plugin with `$this
297299

298300
`layoutScheme` provides the following interfaces:
299301

302+
300303
**getChildViewModel($capture):** returns the child view model registered for the $capture capture. Null if capture is not registered.
304+
301305
**getChildViewModels():** returns the array of child view models like `array ( 'capture' => ViewModel )`
302-
**setVariables($param):** see ViewModels `setVariables` for $param specification. The `layoutScheme setVariables` function applies the same variables provided through $params to the controller's layout and to all registered child ViewModels.
306+
307+
**setVariables($variables, $override = false):** see ViewModels `setVariables` for parameter specification. The `layoutScheme setVariables` function applies the same variables provided through `$variables` to the controller's layout and to all registered child ViewModels.
308+
309+
**getActiveScheme():** get the currently active layout scheme.
310+
311+
**setActiveScheme($schemeName, $skipPreSelectSchemeEvent = false):** set the layout scheme to use. Set `skipPreSelectSchemeEvent` to `true` then the `LayoutSchemeService::HOOK_PRE_SELECT_EVENT` will not get triggered by `LayoutSchemeService`.
312+
313+
**skipPreSelectSchemeEvent():** disable triggering of `LayoutSchemeService::HOOK_PRE_SELECT_SCHEME` this time. This event would get triggered after the view action before rendering. You may want to disable the event to prevent the global event handler to override the active scheme you set via `$this->layoutScheme()->setActiveScheme()`.
314+
######Note:
315+
The event is not skipped by default. Within the global event handler you can check if the active scheme was set by the controller plugin using `LayoutSchemeService->hasPluginSetActiveScheme()` to prevent overriding.
316+
317+
**skipPostSelectLayoutEvent():** disable triggering of `LayoutSchemeService::HOOK_POST_SELECT_LAYOUT` this time.
318+
319+
**hasPluginSetActiveScheme()**: returns `true` if the active scheme was set previously through by the user through `layoutScheme()->setActiveScheme($schemeName)`.
303320

304321
#####Note
305322

@@ -310,6 +327,17 @@ Example:
310327
$this->layout()->setVariables($varMain); // assign variables to the layout's ViewModel
311328
$this->layoutScheme()->getChildViewModel('panelLeft')->setVariables($varPanelLeft); // assign variables to leftPanel child
312329

330+
Important:
331+
332+
`LayoutSchemeService` registers to the `MvcEvent::EVENT_DISPATCH` with priority of -1000. So layout selection
333+
happens *after* your controller action has returned *before* the page gets rendered. This means in particular that at the time the controller action is executed the `LayoutSchemeService` does not know anything about the layout selected and it's child view models.
334+
335+
If you call for example `layoutScheme()->getChildViewModel("footer")` this would return nothing because the footer child ViewModel has not been computed yet.
336+
337+
`LayoutSchemeService` is designed for early execution of the onDispatch event handler if called via the controller plugin. If early event handling is done the service prevents event processing to happen again when triggered globally afterwards. Even in early processing the service triggers the `HOOK_PRE_SELECT_SCHEME` and `HOOK_POST_SELECT_LAYOUT` events.
338+
339+
If you do not want one or both events to get triggered make sure to call `layoutScheme()->skipPostSelectLayoutEvent()` and/or `layoutScheme()->skipPreSelectSchemeEvent()` *before* you call `getChildViewModel()`, `getChildViewModels()` or `setVariables()`. Otherwise the event(s) will get fired.
340+
313341
Notes
314342
-----
315343

src/MxcLayoutScheme/Controller/Plugin/LayoutSchemePlugin.php

+40-12
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,29 @@ class LayoutSchemePlugin extends AbstractPlugin {
88

99
protected $layoutSchemeService = null;
1010

11+
/**
12+
* Set the active scheme
13+
*
14+
* @param string $activeScheme
15+
*/
16+
public function setActiveScheme($activeScheme, $skipPreSchemeSelectEvent = false) {
17+
$this->getLayoutSchemeService()->pluginSetActiveScheme($activeScheme, $skipPreSchemeSelectEvent);
18+
}
19+
20+
/**
21+
* Get the active scheme
22+
*/
23+
public function getActiveScheme() {
24+
return $this->getLayoutSchemeService()->getActiveScheme();
25+
}
26+
1127
/**
1228
* Retrieve array of child view models
1329
*
1430
* @param $capture
1531
*/
1632
public function getChildViewModels() {
17-
return $this->getLayoutSchemeService()->getChildViewModels();
33+
return $this->getLayoutSchemeService()->pluginGetChildViewModels($this->getController());
1834
}
1935

2036
/**
@@ -23,7 +39,7 @@ public function getChildViewModels() {
2339
* @param $capture
2440
*/
2541
public function getChildViewModel($capture) {
26-
return $this->getLayoutSchemeService()->getChildViewModel($capture);
42+
return $this->getLayoutSchemeService()->pluginGetChildViewModel($this->getController(), $capture);
2743
}
2844

2945
/**
@@ -32,7 +48,25 @@ public function getChildViewModel($capture) {
3248
* @param $variables
3349
*/
3450
public function setVariables($variables, $override = false) {
35-
$this->getLayoutSchemeService()->setVariables($variables, $override);
51+
$this->getLayoutSchemeService()->pluginSetVariables($this->getController(), $variables, $override);
52+
}
53+
54+
/**
55+
* Prevent LayoutSchemeService::HOOK_PRE_SELECT_SCHEME to get triggered (for this run only)
56+
*
57+
* @param $variables
58+
*/
59+
public function skipPreSelectSchemeEvent() {
60+
$this->getLayoutSchemeService()->setSkipPreSelectSchemeEvent(true);
61+
}
62+
63+
/**
64+
* Prevent LayoutSchemeService::HOOK_POST_SELECT_LAYOUT to get triggered (for this run only)
65+
*
66+
* @param $variables
67+
*/
68+
public function skipPostSelectLayoutEvent() {
69+
$this->getLayoutSchemeService()->setSkipPostSelectLayoutEvent(true);
3670
}
3771

3872
/**
@@ -59,15 +93,9 @@ public function getLayoutSchemeService() {
5993
*/
6094
public function getController()
6195
{
62-
if (!$this->controller) {
63-
// seems like we get instantiated besides the normal flow of operation (e.g. in bootstrap)
64-
// so let's ask our service for a controller instance
65-
$this->setController($this->getLayoutSchemeService()->getParam('controller'));
66-
if (!$this->controller) {
67-
throw new RuntimeException(sprintf('No Controller reference available. Sorry.'));
68-
}
69-
}
96+
if (!$this->controller) {
97+
throw new RuntimeException(sprintf('No Controller reference available. Sorry.'));
98+
}
7099
return $this->controller;
71100
}
72-
73101
}

src/MxcLayoutScheme/Service/LayoutSchemeService.php

+93-8
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
class LayoutSchemeService implements ListenerAggregateInterface
1717
{
18-
const HOOK_PRE_SCHEME_SELECT = 'pre-scheme-select';
19-
const HOOK_POST_LAYOUT_SELECT = 'post-layout-select';
18+
const HOOK_PRE_SELECT_SCHEME = 'pre-select-scheme';
19+
const HOOK_POST_SELECT_LAYOUT = 'post-select-layout';
2020

2121
use ProvidesEvents;
2222

@@ -26,6 +26,10 @@ class LayoutSchemeService implements ListenerAggregateInterface
2626
protected $schemeOptions = array();
2727
protected $params = null;
2828
protected $childViewModels = array();
29+
protected $completed = false;
30+
protected $hasPluginSetActiveScheme = false;
31+
protected $skipPreSelectSchemeEvent = false;
32+
protected $skipPostSelectLayoutEvent = false;
2933

3034
public function __construct(ServiceManager $sm) {
3135
$this->setServiceManager($sm);
@@ -57,6 +61,9 @@ public function detach(EventManagerInterface $events)
5761
*/
5862
public function onDispatch(MvcEvent $e)
5963
{
64+
// prevent multiple execution
65+
if ($this->completed) return;
66+
6067
$ctrl = $e->getTarget();
6168
$controllerclass = get_class($ctrl);
6269
$controller = substr($controllerclass,strrpos($controllerclass,'\\')+1);
@@ -69,10 +76,15 @@ public function onDispatch(MvcEvent $e)
6976
->set('routeName',$routeMatch->getMatchedRouteName());
7077

7178
// event handler may modify the active scheme
72-
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_PRE_SCHEME_SELECT, $this);
79+
if (!$this->getSkipPreSelectSchemeEvent()) {
80+
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_PRE_SELECT_SCHEME, $this);
81+
}
7382
$this->selectLayout();
83+
$this->completed = true;
7484
// event handler may add variables to the layout
75-
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_POST_LAYOUT_SELECT, $this);
85+
if (!$this->getSkipPostSelectLayoutEvent()) {
86+
$this->getEventManager()->trigger(LayoutSchemeService::HOOK_POST_SELECT_LAYOUT, $this);
87+
}
7688
}
7789

7890
/**
@@ -170,13 +182,21 @@ public function applyChildViewModels($templates, $key) {
170182
}
171183
}
172184

185+
public function pluginSetVariables($controller, $variables, $override = false) {
186+
if (!$this->completed && $controller) {
187+
// called early -> need to complete first
188+
$this->onDispatch($controller->getEvent());
189+
}
190+
return $this->setVariables($variables, $override);
191+
}
192+
173193
/**
174194
* apply variables to controller view model and all child view models
175195
* @param array: $variables
176196
*/
177197
public function setVariables($variables, $override = false) {
178-
// apply variables to main layout view model
179-
$controller = $this->getParam('controller');
198+
// apply variables to main layout view model
199+
$controller = $this->getParam('controller');
180200
if($controller) {
181201
$controller->layout()->setVariables($variables, $override);
182202
}
@@ -206,14 +226,29 @@ protected function setChildViewModel($capture, $view) {
206226
$this->childViewModels[$capture] = $view;
207227
}
208228

229+
/**
230+
* @param $controller
231+
* @param $capture
232+
* @param $default
233+
*
234+
* @return mixed | ViewModel
235+
*/
236+
protected function pluginGetChildViewModel($controller, $capture, $default = null) {
237+
if (!$this->completed) {
238+
$this->onDispatch($controller->getEvent());
239+
}
240+
return $this->getChildViewModel($capture, $default);
241+
}
242+
243+
209244
/**
210245
* @param $capture
211246
* @param $default
212247
*
213248
* @return mixed | ViewModel
214249
*/
215250
protected function getChildViewModel($capture, $default = null) {
216-
return isset($this->childViewModels[$capture]) ? $this->childViewModels[$capture] : $default;
251+
return isset($this->childViewModels[$capture]) ? $this->childViewModels[$capture] : $default;
217252
}
218253

219254
/**
@@ -245,6 +280,17 @@ public function getServiceManager() {
245280
public function setServiceManager(ServiceManager $serviceManager) {
246281
$this->serviceManager = $serviceManager;
247282
}
283+
284+
285+
/**
286+
* @return the $childViewModels
287+
*/
288+
public function pluginGetChildViewModels($controller) {
289+
if (!$this->completed && $controller) {
290+
$this->onDispatch($controller->getEvent());
291+
}
292+
return $this->childViewModels;
293+
}
248294

249295
/**
250296
* @return the $childViewModels
@@ -266,9 +312,20 @@ public function setChildViewModels($childViewModels) {
266312
* @return param($name,$default)
267313
*/
268314
public function getParam($name, $default = null) {
269-
return $this->params->get($name, $default);
315+
return $this->params->get($name, $default);
270316
}
271317

318+
/**
319+
* set active scheme in associated options object
320+
*
321+
* @param string
322+
*/
323+
public function pluginSetActiveScheme($activeScheme, $skipPreSchemeSelectEvent = false) {
324+
$this->setActiveScheme($activeScheme);
325+
$this->hasPluginSetActiveScheme = true;
326+
$this->skipPreSchemeSelectEvent = $skipPreSchemeSelectEvent;
327+
}
328+
272329
/**
273330
* set active scheme in associated options object
274331
*
@@ -304,4 +361,32 @@ protected function getSchemeOptions($schemeName) {
304361
}
305362
return $this->schemeOptions[$schemeName];
306363
}
364+
365+
/**
366+
* @return the $skipPreSelectSchemeEvent
367+
*/
368+
public function getSkipPreSelectSchemeEvent() {
369+
return $this->skipPreSelectSchemeEvent;
370+
}
371+
372+
/**
373+
* @return the $skipPostSelectLayoutEvent
374+
*/
375+
public function getSkipPostSelectLayoutEvent() {
376+
return $this->skipPostSelectLayoutEvent;
377+
}
378+
379+
/**
380+
* @param boolean $skipPreSelectSchemeEvent
381+
*/
382+
public function setSkipPreSelectSchemeEvent($skipPreSelectSchemeEvent) {
383+
$this->skipPreSelectSchemeEvent = $skipPreSelectSchemeEvent;
384+
}
385+
386+
/**
387+
* @param boolean $skipPostSelectLayoutEvent
388+
*/
389+
public function setSkipPostSelectLayoutEvent($skipPostSelectLayoutEvent) {
390+
$this->skipPostSelectLayoutEvent = $skipPostSelectLayoutEvent;
391+
}
307392
}

0 commit comments

Comments
 (0)