typo3-update
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTYPO3 Dual-Version Development: v13 & v14
TYPO3 双版本开发:v13 & v14
Strategy: Write code that works on both TYPO3 v13 and v14, with v14 as the preferred target. All patterns in this skill are designed for dual-version compatibility.
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 your target TYPO3 version (v13 or v14) by checking the official TYPO3 documentation.
策略: 编写可同时运行在TYPO3 v13和v14上的代码,优先以v14为目标版本。 本指南中的所有模式均为双版本兼容设计。
优先使用TYPO3原生API: 编写自定义实现之前,请始终优先使用TYPO3内置API、核心功能和既定规范,不要重复实现TYPO3已经提供的能力。请通过查阅TYPO3官方文档,确认你所使用的API和方法在目标版本(v13或v14)中存在且未被弃用。
1. Version Strategy
1. 版本策略
Target: TYPO3 v13 and v14
目标版本:TYPO3 v13 和 v14
| Version | Status | PHP | Support Until |
|---|---|---|---|
| v12.4 LTS | Maintenance | 8.1-8.3 | April 2026 |
| v13.4 LTS | Active LTS | 8.2-8.4 | ~2028 |
| v14.x | Latest | 8.2-8.4 | ~2029 |
Best Practice: Target both v13 and v14 for maximum compatibility. New projects should prefer v14.
| 版本 | 状态 | PHP版本 | 支持截止日期 |
|---|---|---|---|
| v12.4 LTS | 维护期 | 8.1-8.3 | 2026年4月 |
| v13.4 LTS | 活跃LTS版本 | 8.2-8.4 | ~2028年 |
| v14.x | 最新版本 | 8.2-8.4 | ~2029年 |
最佳实践: 同时适配v13和v14以获得最大兼容性,新项目优先选择v14。
Version Constraints
版本约束
php
<?php
// ext_emconf.php - Dual version support
$EM_CONF[$_EXTKEY] = [
'title' => 'My Extension',
'version' => '2.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '13.0.0-14.99.99',
'php' => '8.2.0-8.4.99',
],
'conflicts' => [],
'suggests' => [],
],
];json
// composer.json - Dual version support
{
"name": "vendor/my-extension",
"type": "typo3-cms-extension",
"require": {
"php": "^8.2",
"typo3/cms-core": "^13.0 || ^14.0"
},
"extra": {
"typo3/cms": {
"extension-key": "my_extension"
}
}
}php
<?php
// ext_emconf.php - 双版本支持配置
$EM_CONF[$_EXTKEY] = [
'title' => 'My Extension',
'version' => '2.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '13.0.0-14.99.99',
'php' => '8.2.0-8.4.99',
],
'conflicts' => [],
'suggests' => [],
],
];json
// composer.json - 双版本支持配置
{
"name": "vendor/my-extension",
"type": "typo3-cms-extension",
"require": {
"php": "^8.2",
"typo3/cms-core": "^13.0 || ^14.0"
},
"extra": {
"typo3/cms": {
"extension-key": "my_extension"
}
}
}2. PHP Requirements
2. PHP要求
Minimum PHP Version: 8.2
最低PHP版本:8.2
Both v13 and v14 require PHP 8.2+. Use modern PHP features:
php
<?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';
}v13和v14均要求PHP 8.2及以上版本,建议使用现代PHP特性:
php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Service;
// ✅ 构造函数属性提升(PHP 8.0+)
final class MyService
{
public function __construct(
private readonly SomeDependency $dependency,
private readonly AnotherService $anotherService,
) {}
}
// ✅ 命名参数(PHP 8.0+)
$result = $this->doSomething(
name: 'value',
options: ['key' => 'value'],
);
// ✅ Match表达式(PHP 8.0+)
$type = match ($input) {
'a' => 'Type A',
'b' => 'Type B',
default => 'Unknown',
};
// ✅ 枚举(PHP 8.1+)
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
}3. Controller Patterns (v13/v14 Compatible)
3. 控制器模式(兼容v13/v14)
Extbase Action Controller
Extbase 动作控制器
php
<?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 since v13)
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
<?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,
) {}
// ✅ 必须返回ResponseInterface(v13起强制要求)
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响应
public function apiAction(): ResponseInterface
{
$data = ['success' => true, 'items' => []];
return $this->jsonResponse(json_encode($data));
}
// ✅ 重定向
public function createAction(): ResponseInterface
{
// 处理创建逻辑...
$this->addFlashMessage('Item created');
return $this->redirect('list');
}
}Backend Module Controller
后台模块控制器
php
<?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
<?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');
}
}4. View & Templating (v13/v14 Compatible)
4. 视图与模板(兼容v13/v14)
ViewFactory (Preferred Pattern)
ViewFactory(推荐模式)
php
<?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');
}
}php
<?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');
}
}Fluid Template Best Practices
Fluid 模板最佳实践
html
<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>html
<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">
<!-- ✅ 自动转义输出 -->
<h1>{item.title}</h1>
<!-- ✅ 显式输出HTML(谨慎使用) -->
<f:format.html>{item.bodytext}</f:format.html>
<!-- ✅ 条件渲染 -->
<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>
<!-- ✅ 链接生成 -->
<f:link.action action="show" arguments="{item: item.uid}">
查看详情
</f:link.action>
</div>
</f:section>
</html>5. Event System (v13/v14 Compatible)
5. 事件系统(兼容v13/v14)
PSR-14 Event Listeners
PSR-14 事件监听器
PSR-14 events are the standard in both v13 and v14. Always prefer events over hooks.
php
<?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
}
}
}PSR-14事件是v13和v14通用的标准,优先使用事件而非钩子。
php
<?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
{
// 缩短特定页面的缓存有效期
if ($event->getPageId() === 123) {
$event->setCacheLifetime(300); // 5分钟
}
}
}Common Events (v13/v14)
常用事件(v13/v14通用)
| Event | Purpose |
|---|---|
| Before DataHandler operations |
| After DataHandler operations |
| Modify link building |
| Adjust page cache |
| Modify stdWrap |
| 事件名称 | 用途 |
|---|---|
| DataHandler操作执行前触发 |
| DataHandler操作执行后触发 |
| 修改链接生成配置 |
| 调整页面缓存有效期 |
| 修改stdWrap配置 |
Services.yaml Registration
Services.yaml 注册方式
yaml
undefinedyaml
undefinedConfiguration/Services.yaml
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'
undefinedservices:
_defaults:
autowire: true
autoconfigure: true
public: false
Vendor\Extension:
resource: '../Classes/'
exclude:
- '../Classes/Domain/Model/'
事件监听器(#[AsEventListener]注解的替代方案)
Vendor\Extension\EventListener\MyListener:
tags:
- name: event.listener
identifier: 'vendor-extension/my-listener'
undefined6. Backend Module Registration (v13/v14)
6. 后台模块注册(v13/v14通用)
Configuration/Backend/Modules.php
Configuration/Backend/Modules.php
php
<?php
// Configuration/Backend/Modules.php
return [
'web_myextension' => [
'parent' => 'web',
'position' => ['after' => 'web_info'],
'access' => 'user,group',
'iconIdentifier' => 'myextension-module',
'path' => '/module/web/myextension',
'labels' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_mod.xlf',
'extensionName' => 'MyExtension',
'controllerActions' => [
\Vendor\MyExtension\Controller\BackendController::class => [
'index',
'list',
'show',
],
],
],
];php
<?php
// Configuration/Backend/Modules.php
return [
'web_myextension' => [
'parent' => 'web',
'position' => ['after' => 'web_info'],
'access' => 'user,group',
'iconIdentifier' => 'myextension-module',
'path' => '/module/web/myextension',
'labels' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_mod.xlf',
'extensionName' => 'MyExtension',
'controllerActions' => [
\Vendor\MyExtension\Controller\BackendController::class => [
'index',
'list',
'show',
],
],
],
];Icon Registration
图标注册
php
<?php
// Configuration/Icons.php
return [
'myextension-module' => [
'provider' => \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class,
'source' => 'EXT:my_extension/Resources/Public/Icons/module.svg',
],
];php
<?php
// Configuration/Icons.php
return [
'myextension-module' => [
'provider' => \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class,
'source' => 'EXT:my_extension/Resources/Public/Icons/module.svg',
],
];7. TCA Configuration (v13/v14 Compatible)
7. TCA配置(兼容v13/v14)
Static TCA Only
仅使用静态TCA
In v14, runtime TCA modifications are forbidden. Always use static files:
php
<?php
// Configuration/TCA/tx_myext_domain_model_item.php
return [
'ctrl' => [
'title' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'delete' => 'deleted',
'enablecolumns' => [
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
],
'searchFields' => 'title,description',
'iconIdentifier' => 'myextension-item',
],
'palettes' => [
'visibility' => [
'showitem' => 'hidden',
],
'access' => [
'showitem' => 'starttime, endtime',
],
],
'types' => [
'1' => [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
title, description,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;visibility,
--palette--;;access,
',
],
],
'columns' => [
'title' => [
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item.title',
'config' => [
'type' => 'input',
'size' => 50,
'max' => 255,
'required' => true,
],
],
'description' => [
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item.description',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 5,
'enableRichtext' => true,
],
],
],
];v14中禁止运行时修改TCA,请始终使用静态文件配置:
php
<?php
// Configuration/TCA/tx_myext_domain_model_item.php
return [
'ctrl' => [
'title' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'delete' => 'deleted',
'enablecolumns' => [
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
],
'searchFields' => 'title,description',
'iconIdentifier' => 'myextension-item',
],
'palettes' => [
'visibility' => [
'showitem' => 'hidden',
],
'access' => [
'showitem' => 'starttime, endtime',
],
],
'types' => [
'1' => [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
title, description,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;visibility,
--palette--;;access,
',
],
],
'columns' => [
'title' => [
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item.title',
'config' => [
'type' => 'input',
'size' => 50,
'max' => 255,
'required' => true,
],
],
'description' => [
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_item.description',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 5,
'enableRichtext' => true,
],
],
],
];Auto-Created Columns from ctrl (v13.3+)
ctrl字段自动生成(v13.3+)
Since TYPO3 v13.3, column definitions for fields referenced in are auto-created by the Core. You no longer need explicit entries for:
ctrl'columns'- fields:
enablecolumns/hidden,disabled,starttime,endtimefe_group - Language fields: ,
sys_language_uid,l10n_parentl10n_diffsource - ,
editlock(if set asdescription)descriptionColumn
Convention fields don't need -- all fields referenced by properties (, , , , , , , , , , ) are added to the database automatically. Defining them in may cause problems.
ext_tables.sqlctrldeletedhiddenstarttimeendtimefe_groupsortingtstampcrdatesys_language_uidl10n_parentl10n_diffsourceext_tables.sqlFields that never need definitions:
columns- -- handled by DataHandler, not shown in forms
deleted - ,
tstamp,crdate-- managed by DataHandler, never displayedt3_origuid - -- managed by DataHandler, should NOT be in
sortingcolumns
Still required:
- The properties themselves (
ctrl,tstamp,crdate,delete, etc.)enablecolumns - References in / palettes (auto-created columns must still be added to types manually)
showitem - Use dedicated palettes: for
visibility,hiddenforaccessstarttime, endtime - Your own custom column definitions
Override auto-created columns in if needed:
Configuration/TCA/Overrides/php
<?php
// Configuration/TCA/Overrides/pages.php
// New pages are disabled by default
$GLOBALS['TCA']['pages']['columns']['disabled']['config']['default'] = 1;Important: Auto-creation only works forproperties in basectrlfiles, NOT inConfiguration/TCA/.Configuration/TCA/Overrides/
从TYPO3 v13.3开始,中引用的字段的列定义会由核心自动生成,你无需为以下字段显式编写配置:
ctrl'columns'- 字段:
enablecolumns/hidden、disabled、starttime、endtimefe_group - 多语言字段:、
sys_language_uid、l10n_parentl10n_diffsource - 、
editlock(如果设置为description)descriptionColumn
约定字段不需要在中定义——所有属性引用的字段(、、、、、、、、、、)都会自动添加到数据库,在中定义这些字段反而可能引发问题。
ext_tables.sqlctrldeletedhiddenstarttimeendtimefe_groupsortingtstampcrdatesys_language_uidl10n_parentl10n_diffsourceext_tables.sql完全不需要定义的字段:
columns- ——由DataHandler处理,不在表单中展示
deleted - 、
tstamp、crdate——由DataHandler管理,永远不会展示t3_origuid - ——由DataHandler管理,不应该出现在
sorting中columns
仍需手动配置的内容:
- 属性本身(
ctrl、tstamp、crdate、delete等)enablecolumns - /调色板中的引用(自动生成的列仍需手动添加到types配置中)
showitem - 使用专用调色板:用于
visibility,hidden用于accessstarttime, endtime - 自定义字段的列定义
如果需要,可在中覆盖自动生成的列:
Configuration/TCA/Overrides/php
<?php
// Configuration/TCA/Overrides/pages.php
// 新建页面默认禁用
$GLOBALS['TCA']['pages']['columns']['disabled']['config']['default'] = 1;重要: 自动生成仅对基础文件中的Configuration/TCA/属性生效,ctrl中的配置不支持自动生成。Configuration/TCA/Overrides/
TCA Overrides
TCA 覆盖配置
php
<?php
// Configuration/TCA/Overrides/tt_content.php
defined('TYPO3') or die();
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:ctype.title',
'value' => 'myextension_element',
'icon' => 'content-text',
'group' => 'default',
]
);
$GLOBALS['TCA']['tt_content']['types']['myextension_element'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
header,
bodytext,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
',
];php
<?php
// Configuration/TCA/Overrides/tt_content.php
defined('TYPO3') or die();
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:ctype.title',
'value' => 'myextension_element',
'icon' => 'content-text',
'group' => 'default',
]
);
$GLOBALS['TCA']['tt_content']['types']['myextension_element'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
header,
bodytext,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
',
];8. Database Operations (v13/v14 Compatible)
8. 数据库操作(兼容v13/v14)
QueryBuilder
QueryBuilder
php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
final class CustomRepository
{
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function findByStatus(string $status): array
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_myext_items');
return $queryBuilder
->select('*')
->from('tx_myext_items')
->where(
$queryBuilder->expr()->eq(
'status',
$queryBuilder->createNamedParameter($status)
)
)
->orderBy('title', 'ASC')
->executeQuery()
->fetchAllAssociative();
}
public function countByPid(int $pid): int
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_myext_items');
return (int)$queryBuilder
->count('uid')
->from('tx_myext_items')
->where(
$queryBuilder->expr()->eq(
'pid',
$queryBuilder->createNamedParameter($pid, Connection::PARAM_INT)
)
)
->executeQuery()
->fetchOne();
}
}php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
final class CustomRepository
{
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function findByStatus(string $status): array
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_myext_items');
return $queryBuilder
->select('*')
->from('tx_myext_items')
->where(
$queryBuilder->expr()->eq(
'status',
$queryBuilder->createNamedParameter($status)
)
)
->orderBy('title', 'ASC')
->executeQuery()
->fetchAllAssociative();
}
public function countByPid(int $pid): int
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_myext_items');
return (int)$queryBuilder
->count('uid')
->from('tx_myext_items')
->where(
$queryBuilder->expr()->eq(
'pid',
$queryBuilder->createNamedParameter($pid, Connection::PARAM_INT)
)
)
->executeQuery()
->fetchOne();
}
}Extbase Repository
Extbase 仓库
php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
final class ItemRepository extends Repository
{
protected $defaultOrderings = [
'sorting' => QueryInterface::ORDER_ASCENDING,
];
public function findPublished(): array
{
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals('hidden', false),
$query->lessThanOrEqual('starttime', time()),
$query->logicalOr(
$query->equals('endtime', 0),
$query->greaterThan('endtime', time())
)
)
);
return $query->execute()->toArray();
}
}php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
final class ItemRepository extends Repository
{
protected $defaultOrderings = [
'sorting' => QueryInterface::ORDER_ASCENDING,
];
public function findPublished(): array
{
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals('hidden', false),
$query->lessThanOrEqual('starttime', time()),
$query->logicalOr(
$query->equals('endtime', 0),
$query->greaterThan('endtime', time())
)
)
);
return $query->execute()->toArray();
}
}9. CLI Commands (v13/v14 Compatible)
9. CLI命令(兼容v13/v14)
php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Core\Bootstrap;
#[AsCommand(
name: 'myext:process',
description: 'Process items in the extension',
)]
final class ProcessCommand extends Command
{
protected function configure(): void
{
$this
->addArgument('type', InputArgument::REQUIRED, 'The type to process')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Force processing');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
// Initialize backend for DataHandler operations
Bootstrap::initializeBackendAuthentication();
$type = $input->getArgument('type');
$force = $input->getOption('force');
$io->title('Processing: ' . $type);
// Your logic here...
$io->success('Processing completed successfully');
return Command::SUCCESS;
}
}php
<?php
declare(strict_types=1);
namespace Vendor\Extension\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Core\Bootstrap;
#[AsCommand(
name: 'myext:process',
description: '处理扩展中的条目',
)]
final class ProcessCommand extends Command
{
protected function configure(): void
{
$this
->addArgument('type', InputArgument::REQUIRED, '要处理的类型')
->addOption('force', 'f', InputOption::VALUE_NONE, '强制处理');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
// 为DataHandler操作初始化后台权限
Bootstrap::initializeBackendAuthentication();
$type = $input->getArgument('type');
$force = $input->getOption('force');
$io->title('处理中:' . $type);
// 你的业务逻辑...
$io->success('处理完成');
return Command::SUCCESS;
}
}Command Registration
命令注册
yaml
undefinedyaml
undefinedConfiguration/Services.yaml
Configuration/Services.yaml
services:
Vendor\Extension\Command\ProcessCommand:
tags:
- name: console.command
undefinedservices:
Vendor\Extension\Command\ProcessCommand:
tags:
- name: console.command
undefined10. Testing for Dual-Version Compatibility
10. 双版本兼容性测试
PHPUnit Setup
PHPUnit 配置
xml
<!-- phpunit.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsBootstrap.php"
colors="true">
<testsuites>
<testsuite name="Unit">
<directory>Tests/Unit</directory>
</testsuite>
<testsuite name="Functional">
<directory>Tests/Functional</directory>
</testsuite>
</testsuites>
</phpunit>xml
<!-- phpunit.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsBootstrap.php"
colors="true">
<testsuites>
<testsuite name="单元测试">
<directory>Tests/Unit</directory>
</testsuite>
<testsuite name="功能测试">
<directory>Tests/Functional</directory>
</testsuite>
</testsuites>
</phpunit>Test Both Versions in CI
CI中测试两个版本
yaml
undefinedyaml
undefined.github/workflows/ci.yaml
.github/workflows/ci.yaml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
typo3: ['^13.0', '^14.0']
php: ['8.2', '8.3']
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: intl, pdo_mysql
- name: Install dependencies
run: |
composer require typo3/cms-core:${{ matrix.typo3 }} --no-update
composer install --prefer-dist --no-progress
- name: Run tests
run: vendor/bin/phpunitundefinedname: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
typo3: ['^13.0', '^14.0']
php: ['8.2', '8.3']
steps:
- uses: actions/checkout@v4
- name: 配置PHP环境
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: intl, pdo_mysql
- name: 安装依赖
run: |
composer require typo3/cms-core:${{ matrix.typo3 }} --no-update
composer install --prefer-dist --no-progress
- name: 运行测试
run: vendor/bin/phpunitundefined11. Upgrade Process
11. 升级流程
From v12 to v13/v14
从v12升级到v13/v14
bash
undefinedbash
undefined1. Create backup
1. 创建备份
ddev snapshot --name=before-upgrade
ddev snapshot --name=before-upgrade
2. Update composer constraints
2. 更新composer约束
ddev composer require "typo3/cms-core:^13.0 || ^14.0" --no-update
ddev composer update "typo3/*" --with-all-dependencies
ddev composer require "typo3/cms-core:^13.0 || ^14.0" --no-update
ddev composer update "typo3/*" --with-all-dependencies
3. Run upgrade wizards
3. 运行升级向导
ddev typo3 upgrade:list
ddev typo3 upgrade:run
ddev typo3 upgrade:list
ddev typo3 upgrade:run
4. Clear caches
4. 清除缓存
ddev typo3 cache:flush
ddev typo3 cache:flush
5. Update database schema
5. 更新数据库结构
ddev typo3 database:updateschema
ddev typo3 database:updateschema
6. Test thoroughly
6. 全面测试
undefinedundefined12. Resources
12. 参考资源
- v13 Documentation: https://docs.typo3.org/m/typo3/reference-coreapi/13.4/en-us/
- v14 Documentation: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/
- v13 Changelog: https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog-13/Index.html
- v14 Changelog: https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog-14/Index.html
- TYPO3 Rector: https://github.com/sabbelasichon/typo3-rector
- v13 官方文档: https://docs.typo3.org/m/typo3/reference-coreapi/13.4/en-us/
- v14 官方文档: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/
- v13 更新日志: https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog-13/Index.html
- v14 更新日志: https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog-14/Index.html
- TYPO3 Rector: https://github.com/sabbelasichon/typo3-rector
Credits & Attribution
致谢
Thanks to Netresearch DTT GmbH for their contributions to the TYPO3 community.
感谢Netresearch DTT GmbH为TYPO3社区做出的贡献。