Create a backend module with Core functionality
This page covers the backend template view, using only Core functionality without Extbase. See also the Backend module API.
Tip
If you want to do extensive data modeling, you may want to use Extbase templating. If you are building a simple backend module, it makes sense to work without Extbase.
Basic controller
When creating a controller without Extbase an instance of Module
is required to return the rendered template:
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Core\Imaging\IconFactory;
final readonly class AdminModuleController
{
public function __construct(
private ModuleTemplateFactory $moduleTemplateFactory,
private IconFactory $iconFactory,
private UriBuilder $uriBuilder,
// ...
) {}
}
Note
A backend controller should be tagged with the
\TYPO3\
(#
) attribute.
If the controller is not tagged with the \TYPO3\
attribute, it must be registered in Configuration/
with the backend.
tag for dependency injection to work:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
T3docs\Examples\:
resource: '../Classes/*'
exclude: '../Classes/Domain/Model/*'
T3docs\Examples\Controller\AdminModuleController:
tags: ['backend.controller']
Main entry point
The handle
method is the main entry point which triggers only the allowed actions.
This makes it possible to include e.g. Javascript for all actions in the controller.
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
final readonly class AdminModuleController
{
public function handleRequest(ServerRequestInterface $request): ResponseInterface
{
$languageService = $this->getLanguageService();
$allowedOptions = [
'function' => [
'debug' => htmlspecialchars(
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:debug'),
),
'password' => htmlspecialchars(
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:password'),
),
'index' => htmlspecialchars(
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:index'),
),
],
];
$moduleData = $request->getAttribute('moduleData');
if ($moduleData->cleanUp($allowedOptions)) {
$this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray());
}
$moduleTemplate = $this->moduleTemplateFactory->create($request);
$this->setUpDocHeader($request, $moduleTemplate);
$title = $languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang_mod.xlf:mlang_tabs_tab');
switch ($moduleData->get('function')) {
case 'debug':
$moduleTemplate->setTitle(
$title,
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:module.menu.debug'),
);
return $this->debugAction($request, $moduleTemplate);
case 'password':
$moduleTemplate->setTitle(
$title,
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:module.menu.password'),
);
return $this->passwordAction($request, $moduleTemplate);
default:
$moduleTemplate->setTitle(
$title,
$languageService->sL('LLL:EXT:examples/Resources/Private/Language/AdminModule/locallang.xlf:module.menu.log'),
);
return $this->indexAction($request, $moduleTemplate);
}
}
}
Actions
Now create an example debug
and assign variables to your view
as you would normally do.
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
final readonly class AdminModuleController
{
protected function debugAction(
ServerRequestInterface $request,
ModuleTemplate $view,
): ResponseInterface {
$body = $request->getParsedBody();
if (is_array($body)) {
$cmd = $body['tx_examples_admin_examples']['cmd'] ?? 'cookies';
switch ($cmd) {
case 'cookies':
$this->debugCookies();
break;
default:
// do something else
}
$view->assignMultiple(
[
'cookies' => $request->getCookieParams(),
'lastcommand' => $cmd,
],
);
}
return $view->renderResponse('AdminModule/Debug');
}
}
The DocHeader
To add a DocHeader button use $view->get
and make
to create the button. Finally, use add
to add it.
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Core\Imaging\IconSize;
final readonly class AdminModuleController
{
private function setUpDocHeader(
ServerRequestInterface $request,
ModuleTemplate $view,
): void {
$buttonBar = $view->getDocHeaderComponent()->getButtonBar();
$uriBuilderPath = $this->uriBuilder->buildUriFromRoute('web_list', ['id' => 0]);
$list = $buttonBar->makeLinkButton()
->setHref($uriBuilderPath)
->setTitle('A Title')
->setShowLabelText(true)
->setIcon($this->iconFactory->getIcon('actions-extension-import', IconSize::SMALL->value));
$buttonBar->addButton($list, ButtonBar::BUTTON_POSITION_LEFT, 1);
}
}
See also
Template example
<html data-namespace-typo3-fluid="true" xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers">
<f:layout name="Module" />
<f:section name="Content">
<h1><f:translate key="LLL:EXT:examples/Resources/Private/Language/locallang_examples.xlf:function_debug"/></h1>
<p><f:translate key="LLL:EXT:examples/Resources/Private/Language/locallang_examples.xlf:function_debug_intro"/></p>
<p><f:debug inline="1">{cookies}</f:debug></p>
</f:section>
</html>
Note
Some Fluid tags do not work in non-Extbase context such as
<f:
.