From typo3-skills
Builds TYPO3 v13+/v14+ Extbase frontend plugins from scratch, covering plugin registration, domain models, repositories, controllers, TCA, TypoScript, and dependency injection.
How this skill is triggered — by the user, by Claude, or both
Slash command
/typo3-skills:typo3-extbase-pluginThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Complete workflow for building Extbase frontend plugins in a TYPO3 v13+/v14+ site package.
Complete workflow for building Extbase frontend plugins in a TYPO3 v13+/v14+ site package.
Every Extbase plugin touches these files. Work through them in this order to avoid forward-reference errors:
| # | File | Purpose |
|---|---|---|
| 1 | ext_tables.sql | DB schema for custom tables |
| 2 | Configuration/TCA/tx_<ext>_domain_model_<model>.php | Backend form config per model |
| 3 | Classes/Domain/Model/<Model>.php | PHP domain model |
| 4 | Classes/Domain/Repository/<Model>Repository.php | Repository class |
| 5 | Configuration/Extbase/Persistence/Classes.php | Persistence mapping (only for non-standard table names) |
| 6 | Classes/Controller/<Name>Controller.php | Controller with actions |
| 7 | ext_localconf.php | configurePlugin() — wire controllers + actions |
| 8 | Configuration/TCA/Overrides/30_tt_content_<plugin>.php | registerPlugin() — make it insertable |
| 9 | Configuration/Services.yaml | DI: mark controller public: true |
| 10 | Configuration/Sets/<set>/TypoScript/plugin.typoscript | View paths + settings |
| 11 | Resources/Private/Templates/<Controller>/<Action>.html | Fluid templates |
ext_tables.sql)Define columns TYPO3 does not auto-create. Convention: tx_<extkey>_domain_model_<modelname>.
CREATE TABLE tx_mysitepackage_domain_model_example (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
PRIMARY KEY (uid),
title varchar(255) DEFAULT '' NOT NULL,
description text,
-- relation counters (int, not the actual FK)
images int(11) unsigned DEFAULT '0' NOT NULL,
categories int(11) unsigned DEFAULT '0' NOT NULL,
-- fe_user ownership: store uid, not object
fe_user int(11) unsigned DEFAULT '0' NOT NULL
);
[!IMPORTANT] For
ObjectStoragerelations (images, categories, inline children), the DB column stores a count, not a foreign key. TYPO3 manages the MM table or foreign_field internally.
See references/tca-patterns.md for complete field-type examples including type => 'category', type => 'country', type => 'file', type => 'inline', and type => 'select' with foreign_table => 'fe_users'.
See references/domain-model-patterns.md for property types, ObjectStorage relations, sys_category integration, and the Category shim pattern.
<?php
declare(strict_types=1);
namespace Vendor\MySitePackage\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
class ExampleRepository extends Repository
{
}
Override ENTITY_CLASSNAME only when the repository must return a mapped model class (e.g., for sys_category):
class CategoryRepository extends Repository
{
public const ENTITY_CLASSNAME = \Vendor\MySitePackage\Domain\Model\Category::class;
}
Configuration/Extbase/Persistence/Classes.php)Only needed when a model maps to a table whose name does not follow the tx_<ext>_domain_model_<name> convention:
<?php
declare(strict_types=1);
return [
// Map to core sys_category table
\Vendor\MySitePackage\Domain\Model\Category::class => [
'tableName' => 'sys_category',
],
// Map fe_users to a custom model
\Vendor\MySitePackage\Domain\Model\FrontendUser::class => [
'tableName' => 'fe_users',
],
// Property name → DB column name mapping
\Vendor\MySitePackage\Domain\Model\Blog::class => [
'tableName' => 'tx_blog_domain_model_blog',
'properties' => [
'categories' => ['fieldName' => 'category'],
],
],
];
See references/controller-patterns.md for the full CRUD pattern, frontend user access control via Context, CountryProvider integration, and initializeAction patterns for DateTimeConverter.
ext_localconf.php)<?php
declare(strict_types=1);
defined('TYPO3') or die();
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'MySitePackage', // Extension name (UpperCamelCase, no vendor)
'ExampleList', // Plugin name (unique within extension)
[
\Vendor\MySitePackage\Controller\ExampleController::class => 'list, show',
],
// Non-cacheable actions (forms, writes, user-specific)
[
\Vendor\MySitePackage\Controller\ExampleController::class => 'show',
]
);
Rules:
create, update, delete) or shows user-specific content must be non-cacheableConfiguration/TCA/Overrides/30_tt_content_<plugin>.php)<?php
declare(strict_types=1);
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
'MySitePackage',
'ExampleList',
'Example List' // Label shown in backend dropdown
);
Configuration/Services.yaml)services:
_defaults:
autowire: true
autoconfigure: true
public: false
Vendor\MySitePackage\:
resource: '../Classes/*'
Vendor\MySitePackage\Controller\ExampleController:
public: true
Every controller must be registered as public: true.
plugin.tx_mysitepackage {
view {
templateRootPaths {
10 = EXT:my_site_package/Resources/Private/Templates/
}
partialRootPaths {
10 = EXT:my_site_package/Resources/Private/Partials/
}
layoutRootPaths {
10 = EXT:my_site_package/Resources/Private/Layouts/
}
}
settings {
# Custom plugin settings accessible via {settings.key} in Fluid
storagePid = 5
}
}
Template path convention: Resources/Private/Templates/<ControllerName>/<ActionName>.html
Example for list action: Resources/Private/Templates/Example/List.html
| Pitfall | Symptom | Fix |
|---|---|---|
Missing public: true in Services.yaml | Controller not found / 404 | Add controller entry to Services.yaml |
| Persistence mapping missing for sys_category | InvalidClassException | Create Category shim + Classes.php mapping |
| Cacheable action shows stale user data | Wrong user's data displayed | Move action to non-cacheable list |
| ObjectStorage not initialized | "Call to member function on null" | Initialize in __construct() + initializeObject() |
| DB column missing for relation | Count always 0 | Add int(11) unsigned DEFAULT '0' column in ext_tables.sql |
configurePlugin vs registerPlugin confused | Plugin not insertable in backend | configurePlugin = ext_localconf.php; registerPlugin = TCA/Overrides |
npx claudepluginhub starraider/typo3-skillsCreates and configures TYPO3 v14 FlexForms for Extbase plugins and plain content elements. Use for adding plugin settings, registering FlexForms, or fixing legacy TCEforms issues.
Simplifies and refines TYPO3 extension code (PHP, Fluid, TCA, YAML) for v14 best practices, replacing deprecated patterns with core APIs. Run after implementing a feature or before merging a PR.
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.