Ah, thanks, this helps!
Uhm, no. I did not make myself clear enough.
With the “the module template stuff” I mean the complete thing: frame/layout, docheader, button bar, menu, (… what else is in there)
With my approach the controller code looks like this:
No, this isn’t an error. controller code is indeed none. This is important especially for the use case log module, which is rendered once with and once without module menu. Doing that in the controller, would require a condition in the controller to switch how the log is shown. If you go for inheritance and let the controller inherit from a generic backend action controller, then you would be in trouble. As you need the same controller logic once with and once without the inherited module menu functionality, you would need to duplicate code or add another abstraction or hack (trait) to make it work.
In any case, you’ll need a second controller class to get a different view. And that is exactly what I would like to avoid.
If you look at my code again, you’ll notice that there is only one controller and two different Fluid layouts. The only thing that needs to be done in the controller (currently) is to select the layout, but this is only done for convenience and could be added to the configuration layer (settings) instead. Then controller code is identical for both cases, only a different layout is selected.
To illustrate what I mean with an additional example, I pushed a (not completed) change of the be user module: https://review.typo3.org/#/c/51091/
To make it visual here. The concept I propose makes it possible to replace controller code like this:
if ($this->actionMethodName == 'indexAction'
|| $this->actionMethodName == 'onlineAction'
|| $this->actionMethodName == 'compareAction') {
$this->generateMenu();
$this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());
}
if ($view instanceof BackendTemplateView) {
$view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
}
/**
* Generates the action menu
*
* @return void
*/
protected function generateMenu()
{
$menuItems = [
'index' => [
'controller' => 'BackendUser',
'action' => 'index',
'label' => $this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xml:backendUsers')
],
'pages' => [
'controller' => 'BackendUserGroup',
'action' => 'index',
'label' => $this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xml:backendUserGroupsMenu')
],
'online' => [
'controller' => 'BackendUser',
'action' => 'online',
'label' => $this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xml:onlineUsers')
]
];
$uriBuilder = $this->objectManager->get(UriBuilder::class);
$uriBuilder->setRequest($this->request);
$menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
$menu->setIdentifier('BackendUserModuleMenu');
foreach ($menuItems as $menuItemConfig) {
if ($this->request->getControllerName() === $menuItemConfig['controller']) {
$isActive = $this->request->getControllerActionName() === $menuItemConfig['action'] ? true : false;
} else {
$isActive = false;
}
$menuItem = $menu->makeMenuItem()
->setTitle($menuItemConfig['label'])
->setHref($this->getHref($menuItemConfig['controller'], $menuItemConfig['action']))
->setActive($isActive);
$menu->addMenuItem($menuItem);
}
$this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
}
With fluid code that looks like this:
<f:be.moduleLayout>
<f:be.pageRenderer
loadJQuery="true"
jQueryNamespace="none"
includeRequireJsModules="{
0:'TYPO3/CMS/Backend/ClickMenu',
1:'TYPO3/CMS/Backend/Modal'
}"
/>
<f:be.moduleLayout.menu identifier="BackendUserModuleMenu">
<f:be.moduleLayout.menuItem label="{f:translate(id: 'backendUsers')}" uri="{f:uri.action(controller: 'BackendUser', action: 'index')}"/>
<f:be.moduleLayout.menuItem label="{f:translate(id: 'backendUserGroupsMenu')}" uri="{f:uri.action(controller: 'BackendUserGroup', action: 'index')}"/>
<f:be.moduleLayout.menuItem label="{f:translate(id: 'onlineUsers')}" uri="{f:uri.action(controller: 'BackendUser', action: 'online')}"/>
</f:be.moduleLayout.menu>
</f:be.moduleLayout>
I find it: compact, easy to read, write and understand, clean, semantically correct, covers all use cases in the core
I’m happy to be convinced that what you envision is cleaner and easier for devs. Maybe you can also draft some code how beuser module and log module could look like with that, so we can compare the approaches.
I can understand that putting this into view helpers might feel to much and that this might be a style not wanted for the core. I’m fine with that as well, just let me know. In that case I will publish a view helper collection as composer package that enables writing backend modules like I drafted here. Hm, maybe I publish such a package anyway to enable writing modules like that in TYPO3 7.6 as well