{"id":82,"date":"2026-10-23T17:00:00","date_gmt":"2026-10-23T15:00:00","guid":{"rendered":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/"},"modified":"2026-10-16T14:00:12","modified_gmt":"2026-10-16T12:00:12","slug":"backend-modules-in-typo3-v13-v14-with-modules-php","status":"publish","type":"post","link":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/","title":{"rendered":"Backend modules in TYPO3 v13\/v14 with Modules.php"},"content":{"rendered":"<p>Hi guys,<\/p>\n<p>In my PIT Solutions days, a backend module meant a call to <code>ExtensionUtility::registerModule()<\/code> in <code>ext_tables.php<\/code>, a long array of options, and a bit of hope. That way was removed in TYPO3 v12, when the new module registration API arrived (<code>Feature-96733<\/code>). Today a module is a plain PHP array in <code>Configuration\/Backend\/Modules.php<\/code> plus a controller that the dependency injection container knows about.<\/p>\n<p>I still see old tutorials ranking high in search, so here is a complete small module that runs on v13 and v14. It lists the redirects of the installation, read-only, so editors can look up where an old URL goes without opening the Redirects module with full rights. The extension key is <code>sitepackage<\/code> and the namespace <code>HarvisoftSitepackage<\/code>; change both for your project.<\/p>\n<h2>Step 1: register the module<\/h2>\n<pre><code class=\"language-php\">&lt;?php\n\n\/\/ EXT:sitepackage\/Configuration\/Backend\/Modules.php\nuse HarvisoftSitepackageControllerRedirectOverviewController;\n\nreturn [\n    'sitepackage_redirects' =&gt; [\n        'parent' =&gt; 'web',\n        'position' =&gt; ['after' =&gt; '*'],\n        'access' =&gt; 'user',\n        'workspaces' =&gt; 'live',\n        'path' =&gt; '\/module\/sitepackage\/redirects',\n        'iconIdentifier' =&gt; 'mimetypes-x-sys_redirect',\n        'labels' =&gt; 'LLL:EXT:sitepackage\/Resources\/Private\/Language\/locallang_mod_redirects.xlf',\n        'routes' =&gt; [\n            '_default' =&gt; [\n                'target' =&gt; RedirectOverviewController::class . '::handleRequest',\n            ],\n        ],\n    ],\n];<\/code><\/pre>\n<p>A few keys need a word:<\/p>\n<ul>\n<li><code>parent<\/code>: in v14 the main module &#8220;Web&#8221; was renamed to &#8220;Content&#8221;, with the identifier <code>content<\/code> (<code>Feature-107628<\/code>). The old identifier <code>web<\/code> is registered as an alias, and the module factory resolves aliases in <code>parent<\/code>. I checked that in the v14.3 source. So <code>web<\/code> works on both versions. For a v14-only extension, write <code>content<\/code>.<\/li>\n<li><code>access<\/code>: <code>user<\/code> means editors can get it through their group rights. <code>admin<\/code> and <code>systemMaintainer<\/code> are the stricter values. Since v14.2 these checks are &#8220;access gates&#8221;, and you can even add your own with <code>#[AsModuleAccessGate]<\/code> (<code>Feature-109365<\/code>), but that does not exist in v13.<\/li>\n<li><code>labels<\/code>: an <code>LLL:<\/code> path to a file with the keys <code>mlang_tabs_tab<\/code>, <code>mlang_labels_tabdescr<\/code> and <code>mlang_labels_tablabel<\/code>. v14 also understands the new translation domain syntax like <code>sitepackage.module<\/code>, but the file path works on both.<\/li>\n<li><code>routes<\/code>: for a non-Extbase module you point <code>_default<\/code> to a controller method. Extbase modules use <code>extensionName<\/code> and <code>controllerActions<\/code> instead.<\/li>\n<\/ul>\n<p>The label file is the classic XLIFF:<\/p>\n<pre><code class=\"language-xml\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;xliff version=\"1.2\" xmlns=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;\n    &lt;file source-language=\"en\" datatype=\"plaintext\" original=\"EXT:sitepackage\/Resources\/Private\/Language\/locallang_mod_redirects.xlf\" date=\"2026-10-15T00:00:00Z\" product-name=\"sitepackage\"&gt;\n        &lt;body&gt;\n            &lt;trans-unit id=\"mlang_tabs_tab\"&gt;\n                &lt;source&gt;Redirect overview&lt;\/source&gt;\n            &lt;\/trans-unit&gt;\n            &lt;trans-unit id=\"mlang_labels_tablabel\"&gt;\n                &lt;source&gt;Where old URLs go&lt;\/source&gt;\n            &lt;\/trans-unit&gt;\n            &lt;trans-unit id=\"mlang_labels_tabdescr\"&gt;\n                &lt;source&gt;Read-only list of all active redirects.&lt;\/source&gt;\n            &lt;\/trans-unit&gt;\n        &lt;\/body&gt;\n    &lt;\/file&gt;\n&lt;\/xliff&gt;<\/code><\/pre>\n<h2>Step 2: the controller<\/h2>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace HarvisoftSitepackageController;\n\nuse PsrHttpMessageResponseInterface;\nuse PsrHttpMessageServerRequestInterface;\nuse TYPO3CMSBackendAttributeAsController;\nuse TYPO3CMSBackendTemplateModuleTemplateFactory;\nuse TYPO3CMSCoreDatabaseConnectionPool;\n\n#[AsController]\nfinal readonly class RedirectOverviewController\n{\n    public function __construct(\n        private ModuleTemplateFactory $moduleTemplateFactory,\n        private ConnectionPool $connectionPool,\n    ) {}\n\n    public function handleRequest(ServerRequestInterface $request): ResponseInterface\n    {\n        $queryBuilder = $this-&gt;connectionPool-&gt;getQueryBuilderForTable('sys_redirect');\n        $redirects = $queryBuilder\n            -&gt;select('source_host', 'source_path', 'target', 'target_statuscode', 'hitcount')\n            -&gt;from('sys_redirect')\n            -&gt;orderBy('source_host')\n            -&gt;addOrderBy('source_path')\n            -&gt;executeQuery()\n            -&gt;fetchAllAssociative();\n\n        $view = $this-&gt;moduleTemplateFactory-&gt;create($request);\n        $view-&gt;setTitle('Redirect overview');\n        $view-&gt;assign('redirects', $redirects);\n\n        return $view-&gt;renderResponse('RedirectOverview\/Index');\n    }\n}<\/code><\/pre>\n<p>Things I like here. <code>#[AsController]<\/code> tags the class so the backend can create it with its dependencies; it came in v13.0 and replaced the v12 <code>#[Controller]<\/code> attribute (<code>Feature-102631<\/code>). The query builder applies the default restrictions, so deleted and disabled redirects are not listed. And <code>ModuleTemplateFactory::create()<\/code> gives you the full backend frame: doc header, flash messages, the right CSS. The core&#8217;s own About module is built the same way, if you want a reference to read.<\/p>\n<p>The controller needs DI, so the extension must have a <code>Configuration\/Services.yaml<\/code> with <code>autowire: true<\/code>, <code>autoconfigure: true<\/code> and the <code>Classes\/<\/code> folder as resource. Most sitepackages have it already.<\/p>\n<h2>Step 3: the template<\/h2>\n<p>For non-Extbase modules, the view looks for templates in <code>Resources\/Private\/Templates<\/code> of the extension that registered the route, and the layout <code>Module<\/code> comes from EXT:backend. So the file is <code>Resources\/Private\/Templates\/RedirectOverview\/Index.html<\/code>:<\/p>\n<pre><code class=\"language-html\">&lt;html xmlns:f=\"http:\/\/typo3.org\/ns\/TYPO3\/CMS\/Fluid\/ViewHelpers\" data-namespace-typo3-fluid=\"true\"&gt;\n\n&lt;f:layout name=\"Module\" \/&gt;\n\n&lt;f:section name=\"Content\"&gt;\n    &lt;h1&gt;Redirect overview&lt;\/h1&gt;\n    &lt;f:if condition=\"{redirects}\"&gt;\n        &lt;f:then&gt;\n            &lt;div class=\"table-fit\"&gt;\n                &lt;table class=\"table table-striped table-hover\"&gt;\n                    &lt;thead&gt;\n                        &lt;tr&gt;\n                            &lt;th&gt;Host&lt;\/th&gt;\n                            &lt;th&gt;Old path&lt;\/th&gt;\n                            &lt;th&gt;Target&lt;\/th&gt;\n                            &lt;th&gt;Status&lt;\/th&gt;\n                            &lt;th&gt;Hits&lt;\/th&gt;\n                        &lt;\/tr&gt;\n                    &lt;\/thead&gt;\n                    &lt;tbody&gt;\n                        &lt;f:for each=\"{redirects}\" as=\"redirect\"&gt;\n                            &lt;tr&gt;\n                                &lt;td&gt;{redirect.source_host}&lt;\/td&gt;\n                                &lt;td&gt;&lt;code&gt;{redirect.source_path}&lt;\/code&gt;&lt;\/td&gt;\n                                &lt;td&gt;{redirect.target}&lt;\/td&gt;\n                                &lt;td&gt;{redirect.target_statuscode}&lt;\/td&gt;\n                                &lt;td&gt;{redirect.hitcount}&lt;\/td&gt;\n                            &lt;\/tr&gt;\n                        &lt;\/f:for&gt;\n                    &lt;\/tbody&gt;\n                &lt;\/table&gt;\n            &lt;\/div&gt;\n        &lt;\/f:then&gt;\n        &lt;f:else&gt;\n            &lt;f:be.infobox title=\"No redirects\" state=\"-1\"&gt;There are no active redirects.&lt;\/f:be.infobox&gt;\n        &lt;\/f:else&gt;\n    &lt;\/f:if&gt;\n&lt;\/f:section&gt;\n&lt;\/html&gt;<\/code><\/pre>\n<p>Note the plain <code>.html<\/code> name. v14 also accepts <code>Index.fluid.html<\/code> and prefers it, but v13 does not know that extension. For a module that must run on both, stay with <code>.html<\/code>.<\/p>\n<h2>Step 4: access for editors<\/h2>\n<p>Admins see the module right away after a cache flush. Editors do not, and that is correct. In the backend, open the editor&#8217;s backend user group, go to the tab with module permissions and tick &#8220;Redirect overview&#8221;. The permission lands in <code>be_groups.groupMods<\/code>. If you deploy groups as code, add the module identifier <code>sitepackage_redirects<\/code> there.<\/p>\n<p>One more thing to think about: the module shows the whole <code>sys_redirect<\/code> table, across all sites. The table definition of EXT:redirects ignores web mounts on purpose, and my query does too. For a multi-site installation where editors should only see their own site, filter by <code>source_host<\/code> using the sites the editor can access. Do not hand out data just because the module was easy to build.<\/p>\n<h2>Check it<\/h2>\n<pre><code class=\"language-bash\">ddev exec vendor\/bin\/typo3 cache:flush<\/code><\/pre>\n<p>Then log in once as admin and once as a test editor. The admin should see &#8220;Redirect overview&#8221; below the Web (v13) or Content (v14) main module, the editor only after the group got the permission. If opening the module gives an error that the controller cannot be created, the attribute was not picked up. In my experience that is almost always a missing or wrong <code>Services.yaml<\/code>, or a cache that was not flushed after adding it.<\/p>\n<p>That is all. No <code>ext_tables.php<\/code>, no magic strings for the module key, and a controller you can unit test. If this post saved you an evening, I will be really happy.<\/p>\n<h2>Further reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.typo3.org\/m\/typo3\/reference-coreapi\/main\/en-us\/ExtensionArchitecture\/HowTo\/BackendModule\/Index.html\">TYPO3 documentation: Backend modules<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/m\/typo3\/reference-coreapi\/main\/en-us\/ExtensionArchitecture\/HowTo\/BackendModule\/ModuleConfiguration.html\">Backend module configuration reference<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/permalink\/changelog:feature-107628-1729026000\">Feature: #107628 \u2013 Improved backend module naming and structure<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/permalink\/changelog:feature-109365-1774538611\">Feature: #109365 \u2013 Module access gates<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Module registration in ext_tables.php is gone. A small backend module for TYPO3 v13 and v14 with Modules.php, #[AsController], ModuleTemplateFactory and editor access.<\/p>\n","protected":false},"author":1,"featured_media":83,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[30,32,33,24],"class_list":["post-82","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-typo3","tag-extension-development","tag-php-8","tag-typo3-v13","tag-typo3-v14"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Backend modules in TYPO3 v13 and v14 with Modules.php<\/title>\n<meta name=\"description\" content=\"Build a backend module for TYPO3 v13 and v14: Configuration\/Backend\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Backend modules in TYPO3 v13 and v14 with Modules.php\" \/>\n<meta property=\"og:description\" content=\"Build a backend module for TYPO3 v13 and v14: Configuration\/Backend\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Hoja Mustaffa Abdul Latheef \u2013 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-23T15:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:site\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/\"},\"author\":{\"name\":\"Hoja Mustaffa Abdul Latheef\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"headline\":\"Backend modules in TYPO3 v13\\\/v14 with Modules.php\",\"datePublished\":\"2026-10-23T15:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/\"},\"wordCount\":670,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg\",\"keywords\":[\"Extension development\",\"PHP 8\",\"TYPO3 v13\",\"TYPO3 v14\"],\"articleSection\":[\"TYPO3\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/\",\"name\":\"Backend modules in TYPO3 v13 and v14 with Modules.php\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg\",\"datePublished\":\"2026-10-23T15:00:00+00:00\",\"description\":\"Build a backend module for TYPO3 v13 and v14: Configuration\\\/Backend\\\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg\",\"contentUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg\",\"width\":1200,\"height\":630,\"caption\":\"A backend module, the modern way\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/backend-modules-in-typo3-v13-v14-with-modules-php\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Backend modules in TYPO3 v13\\\/v14 with Modules.php\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\",\"name\":\"Hoja Mustaffa Abdul Latheef \u2013 Blog\",\"description\":\"Notes from daily work with TYPO3, PHP and AI-assisted engineering\",\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hoja.in\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\",\"name\":\"Hoja Mustaffa Abdul Latheef\",\"logo\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/image\\\/\"},\"description\":\"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.\",\"sameAs\":[\"https:\\\/\\\/hoja.in\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Backend modules in TYPO3 v13 and v14 with Modules.php","description":"Build a backend module for TYPO3 v13 and v14: Configuration\/Backend\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/","og_locale":"en_US","og_type":"article","og_title":"Backend modules in TYPO3 v13 and v14 with Modules.php","og_description":"Build a backend module for TYPO3 v13 and v14: Configuration\/Backend\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.","og_url":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/","og_site_name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","article_published_time":"2026-10-23T15:00:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg","type":"image\/jpeg"}],"author":"Hoja Mustaffa Abdul Latheef","twitter_card":"summary_large_image","twitter_creator":"@hojaonline","twitter_site":"@hojaonline","twitter_misc":{"Written by":"Hoja Mustaffa Abdul Latheef","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#article","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/"},"author":{"name":"Hoja Mustaffa Abdul Latheef","@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"headline":"Backend modules in TYPO3 v13\/v14 with Modules.php","datePublished":"2026-10-23T15:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/"},"wordCount":670,"commentCount":0,"publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"image":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg","keywords":["Extension development","PHP 8","TYPO3 v13","TYPO3 v14"],"articleSection":["TYPO3"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/","url":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/","name":"Backend modules in TYPO3 v13 and v14 with Modules.php","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#primaryimage"},"image":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg","datePublished":"2026-10-23T15:00:00+00:00","description":"Build a backend module for TYPO3 v13 and v14: Configuration\/Backend\/Modules.php, #[AsController], ModuleTemplateFactory, Fluid template, editor access.","breadcrumb":{"@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#primaryimage","url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg","contentUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/backend-modules-in-typo3-v13-v14-with-modules-php.jpg","width":1200,"height":630,"caption":"A backend module, the modern way"},{"@type":"BreadcrumbList","@id":"https:\/\/hoja.in\/blog\/backend-modules-in-typo3-v13-v14-with-modules-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hoja.in\/blog\/"},{"@type":"ListItem","position":2,"name":"Backend modules in TYPO3 v13\/v14 with Modules.php"}]},{"@type":"WebSite","@id":"https:\/\/hoja.in\/blog\/#website","url":"https:\/\/hoja.in\/blog\/","name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","description":"Notes from daily work with TYPO3, PHP and AI-assisted engineering","publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hoja.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b","name":"Hoja Mustaffa Abdul Latheef","logo":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/image\/"},"description":"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.","sameAs":["https:\/\/hoja.in\/"]}]}},"_links":{"self":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/82","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/comments?post=82"}],"version-history":[{"count":1,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/82\/revisions"}],"predecessor-version":[{"id":164,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/82\/revisions\/164"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media\/83"}],"wp:attachment":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media?parent=82"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/categories?post=82"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/tags?post=82"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}