From craft-workspace-webconsulting-skills
Guides TYPO3 v14 extension development and upgrades, covering version constraints, PHP 8.2+, controllers, ViewFactory, Fluid, events, backend modules, TCA, QueryBuilder, CLI commands, tests, and v14-only manual changes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/craft-workspace-webconsulting-skills:typo3-updateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Source: https://github.com/dirnbauer/webconsulting-skills
references/06-backend-module-registration-typo3-v14.mdreferences/07-tca-configuration-typo3-v14.mdreferences/08-database-operations-typo3-v14.mdreferences/09-cli-commands-typo3-v14.mdreferences/10-testing.mdreferences/11-upgrade-process.mdreferences/12-resources.mdreferences/13-v14-only-changes-manual-not-handled-by-rector.mdreferences/credits-and-attribution.mdreferences/full-guide.mdStrategy: This collection targets TYPO3 v14.x only. Patterns here assume a v14 codebase. For migrating from older majors, use
typo3-rector,typo3-extension-upgrade, Fractor, and the official upgrade guide first.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Do not reinvent what TYPO3 already provides. Always verify that the APIs and methods you use exist and are not deprecated in TYPO3 v14 by checking the official TYPO3 documentation.
| Version | Role for this collection |
|---|---|
| v14.x | Supported target — examples and constraints assume TYPO3 v14 |
| Older majors | Not targeted — complete a core upgrade, then use these patterns |
Best Practice: Declare TYPO3 v14-only constraints in composer.json and ext_emconf.php for new work.
<?php
// ext_emconf.php — TYPO3 v14
$EM_CONF[$_EXTKEY] = [
'title' => 'My Extension',
'version' => '2.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '14.0.0-14.99.99',
'php' => '8.2.0-8.5.99',
],
'conflicts' => [],
'suggests' => [],
],
];
// composer.json — TYPO3 v14
{
"name": "vendor/my-extension",
"type": "typo3-cms-extension",
"require": {
"php": "^8.2",
"typo3/cms-core": "^14.0"
},
"extra": {
"typo3/cms": {
"extension-key": "my_extension"
}
}
}
Content Blocks:
friendsoftypo3/content-blocksmay requiretypo3/cms-core^14.3 or higher (current 2.4.5 requires^14.3) — match the Packagist constraint before pinning^14.0only.
TYPO3 v14 requires PHP 8.2+. Use modern PHP features:
<?php
declare(strict_types=1);
namespace Vendor\Extension\Service;
// ✅ Constructor property promotion (PHP 8.0+)
final class MyService
{
public function __construct(
private readonly SomeDependency $dependency,
private readonly AnotherService $anotherService,
) {}
}
// ✅ Named arguments (PHP 8.0+)
$result = $this->doSomething(
name: 'value',
options: ['key' => 'value'],
);
// ✅ Match expressions (PHP 8.0+)
$type = match ($input) {
'a' => 'Type A',
'b' => 'Type B',
default => 'Unknown',
};
// ✅ Enums (PHP 8.1+)
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
}
<?php
declare(strict_types=1);
namespace Vendor\Extension\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use Vendor\Extension\Domain\Repository\ItemRepository;
final class ItemController extends ActionController
{
public function __construct(
private readonly ItemRepository $itemRepository,
) {}
// ✅ Must return ResponseInterface (required in TYPO3 v14)
public function listAction(): ResponseInterface
{
$items = $this->itemRepository->findAll();
$this->view->assign('items', $items);
return $this->htmlResponse();
}
public function showAction(int $item): ResponseInterface
{
$item = $this->itemRepository->findByUid($item);
$this->view->assign('item', $item);
return $this->htmlResponse();
}
// ✅ JSON response
public function apiAction(): ResponseInterface
{
$data = ['success' => true, 'items' => []];
return $this->jsonResponse(json_encode($data));
}
// ✅ Redirect
public function createAction(): ResponseInterface
{
// Process creation...
$this->addFlashMessage('Item created');
return $this->redirect('list');
}
}
<?php
declare(strict_types=1);
namespace Vendor\Extension\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
#[AsController]
final class BackendModuleController
{
public function __construct(
private readonly ModuleTemplateFactory $moduleTemplateFactory,
) {}
public function indexAction(ServerRequestInterface $request): ResponseInterface
{
$moduleTemplate = $this->moduleTemplateFactory->create($request);
$moduleTemplate->assign('items', []);
return $moduleTemplate->renderResponse('Backend/Index');
}
}
<?php
declare(strict_types=1);
namespace Vendor\Extension\Service;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;
final class RenderingService
{
public function __construct(
private readonly ViewFactoryInterface $viewFactory,
) {}
public function renderEmail(ServerRequestInterface $request, array $data): string
{
$viewFactoryData = new ViewFactoryData(
templateRootPaths: ['EXT:my_extension/Resources/Private/Templates/Email'],
partialRootPaths: ['EXT:my_extension/Resources/Private/Partials'],
layoutRootPaths: ['EXT:my_extension/Resources/Private/Layouts'],
request: $request,
);
$view = $this->viewFactory->create($viewFactoryData);
$view->assignMultiple($data);
return $view->render('Notification');
}
}
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:layout name="Default" />
<f:section name="Main">
<div class="content">
<!-- ✅ Auto-escaped output -->
<h1>{item.title}</h1>
<!-- ✅ Explicit HTML (use with caution) -->
<f:format.html>{item.bodytext}</f:format.html>
<!-- ✅ Conditional rendering -->
<f:if condition="{items}">
<f:then>
<f:for each="{items}" as="item">
<f:render partial="Item" arguments="{item: item}" />
</f:for>
</f:then>
<f:else>
<p>No items found.</p>
</f:else>
</f:if>
<!-- ✅ Link building -->
<f:link.action action="show" arguments="{item: item.uid}">
View Details
</f:link.action>
</div>
</f:section>
</html>
PSR-14 events are the standard in TYPO3 v14. Always prefer events over hooks.
<?php
declare(strict_types=1);
namespace Vendor\Extension\EventListener;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Frontend\Event\ModifyCacheLifetimeForPageEvent;
#[AsEventListener(identifier: 'vendor-extension/modify-cache-lifetime')]
final class ModifyCacheLifetimeListener
{
public function __invoke(ModifyCacheLifetimeForPageEvent $event): void
{
// Reduce cache lifetime for certain pages
if ($event->getPageId() === 123) {
$event->setCacheLifetime(300); // 5 minutes
}
}
}
| Event | Purpose |
|---|---|
ModifyPageLinkConfigurationEvent | Modify link building |
ModifyCacheLifetimeForPageEvent | Adjust page cache |
BeforeStdWrapFunctionsInitializedEvent | Modify stdWrap |
| (DataHandler) | Core ships no generic BeforeRecordOperationEvent / AfterRecordOperationEvent; use SC_OPTIONS DataHandler hooks or documented TYPO3\CMS\Core\DataHandling\Event\* only (list) |
# Configuration/Services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
Vendor\Extension\:
resource: '../Classes/*'
exclude:
- '../Classes/Domain/Model/*'
# Event listener (alternative to #[AsEventListener] attribute)
Vendor\Extension\EventListener\MyListener:
tags:
- name: event.listener
identifier: 'vendor-extension/my-listener'
Read the full guide when the task needs detailed examples, long templates, troubleshooting matrices, appendices, or sections not included above. Keep this file unloaded for narrow tasks so the skill follows progressive disclosure.
npx claudepluginhub dirnbauer/webconsulting-skillsApplies TYPO3 Rector upgrade patterns for PHP migrations toward v14, including configuration, dry runs, rule sets, and manual deprecation checks.
Builds TYPO3 v13+/v14+ Extbase frontend plugins from scratch, covering plugin registration, domain models, repositories, controllers, TCA, TypoScript, and dependency injection.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.