TYPO3

Enterprise CMS from Germany. High-functionality system with strengths in complex multi-language and multi-site management.

CMSOpen SourceEnterprisePHPMySQLMultilingual
License
GPL v2
Language
PHP
Pricing
完全無料(自己ホスト)
Official Site
Visit Official Site

CMS

TYPO3

Overview

TYPO3 is an enterprise-level open-source Content Management System (CMS) from Germany. Founded in 1998, it has gained particularly high popularity in Europe. With strengths in complex multilingual and multi-site management, it is widely adopted by large corporate websites, government agencies, and universities. Running on PHP 8.2+ and databases like MySQL, it provides advanced security and flexibility.

Details

TYPO3 is designed as a high-performance CMS that meets enterprise-level requirements. It excels particularly in multilingual support and complex content management, offering comprehensive features necessary for large-scale site operations including workspace management, versioning, and advanced caching capabilities.

Key Features:

  • Advanced Multilingual Support: Unlimited language support with translation workflows
  • Workspace Management: Separation of staging and live environments
  • Flexible Content Elements: Highly customizable content element combinations
  • Multi-site Management: Manage multiple sites from a single installation
  • Versioning: Content history management and restoration capabilities
  • Advanced Caching System: Performance optimization
  • Enterprise Security: Multi-factor authentication, IP restrictions, detailed access control
  • TYPO3 v13 (Latest 2024): Improved image rendering, RTL UI support, content blocks feature

Pros and Cons

Pros

  • Powerful multilingual and multi-regional capabilities
  • Enterprise-level security
  • Advanced workflow management
  • Flexible content structure
  • Excellent scalability
  • Long-Term Support (LTS) releases
  • Active development community (especially in Europe)
  • Architecture optimized for large-scale sites

Cons

  • Very steep learning curve
  • Complex configuration and customization
  • Limited English resources (especially for Asian markets)
  • Overkill features for small sites
  • Time-consuming initial setup
  • Requires specialized knowledge
  • Smaller community (especially in Asia)

Reference Pages

Code Examples

1. Hello World (Basic Setup)

Installation via Composer (TYPO3 v13)

# Create project directory
mkdir my-typo3-project && cd my-typo3-project

# Install TYPO3
composer create-project typo3/cms-base-distribution:^13

# Database setup (CLI setup)
./vendor/bin/typo3 setup \
  --driver=mysqli \
  --host=localhost \
  --port=3306 \
  --dbname=typo3_db \
  --username=db_user \
  --password=db_password \
  --admin-username=admin \
  --admin-password=password123 \
  [email protected] \
  --project-name="My TYPO3 Project"

# Configure web server (set public/ as document root)
# Access backend at http://localhost/typo3

2. TypoScript Page Template

setup.typoscript (Basic Page Configuration)

# Define page object
page = PAGE
page {
    # Meta tags
    meta {
        viewport = width=device-width, initial-scale=1.0
        description = TYPO3 Enterprise CMS Site
        keywords = TYPO3, CMS, Enterprise
    }
    
    # Include CSS
    includeCSS {
        styles = EXT:my_sitepackage/Resources/Public/Css/styles.css
        bootstrap = https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css
        bootstrap.external = 1
    }
    
    # Include JavaScript
    includeJSFooter {
        scripts = EXT:my_sitepackage/Resources/Public/Js/scripts.js
        bootstrap = https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js
        bootstrap.external = 1
    }
    
    # Fluid template configuration
    10 = FLUIDTEMPLATE
    10 {
        templateName = Default
        templateRootPaths {
            10 = EXT:my_sitepackage/Resources/Private/Templates/Page/
        }
        partialRootPaths {
            10 = EXT:my_sitepackage/Resources/Private/Partials/Page/
        }
        layoutRootPaths {
            10 = EXT:my_sitepackage/Resources/Private/Layouts/Page/
        }
        
        # Data processors
        dataProcessing {
            # Generate menu
            10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
            10 {
                levels = 2
                includeSpacer = 1
                as = mainNavigation
            }
            
            # Language menu
            20 = TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor
            20 {
                languages = auto
                as = languageMenu
            }
        }
        
        # Variables
        variables {
            currentYear = TEXT
            currentYear.data = date:Y
            
            siteName = TEXT
            siteName.value = My TYPO3 Site
        }
    }
}

3. Fluid Template Development

Default.html (Page Template)

<f:layout name="Default" />

<f:section name="Main">
    <header class="site-header">
        <nav class="navbar navbar-expand-lg">
            <div class="container">
                <a class="navbar-brand" href="/">
                    {siteName}
                </a>
                
                <!-- Main navigation -->
                <f:if condition="{mainNavigation}">
                    <ul class="navbar-nav">
                        <f:for each="{mainNavigation}" as="menuItem">
                            <li class="nav-item {f:if(condition: menuItem.active, then: 'active')}">
                                <a class="nav-link" href="{menuItem.link}" 
                                   {f:if(condition: menuItem.target, then: 'target="{menuItem.target}"')}>
                                    {menuItem.title}
                                </a>
                                
                                <!-- Submenu -->
                                <f:if condition="{menuItem.children}">
                                    <ul class="dropdown-menu">
                                        <f:for each="{menuItem.children}" as="subItem">
                                            <li>
                                                <a class="dropdown-item" href="{subItem.link}">
                                                    {subItem.title}
                                                </a>
                                            </li>
                                        </f:for>
                                    </ul>
                                </f:if>
                            </li>
                        </f:for>
                    </ul>
                </f:if>
                
                <!-- Language switcher -->
                <f:if condition="{languageMenu}">
                    <div class="language-menu">
                        <f:for each="{languageMenu}" as="language">
                            <a href="{language.link}" 
                               class="lang-link {f:if(condition: language.active, then: 'active')}">
                                {language.languageIsoCode}
                            </a>
                        </f:for>
                    </div>
                </f:if>
            </div>
        </nav>
    </header>
    
    <main class="site-main">
        <div class="container">
            <!-- Content area -->
            <f:cObject typoscriptObjectPath="lib.dynamicContent" data="{colPos: '0'}" />
        </div>
    </main>
    
    <footer class="site-footer">
        <div class="container">
            <p>&copy; {currentYear} {siteName}. All rights reserved.</p>
        </div>
    </footer>
</f:section>

4. Extension Development

ext_emconf.php (Extension Configuration)

<?php
$EM_CONF[$_EXTKEY] = [
    'title' => 'My Custom Extension',
    'description' => 'TYPO3 extension providing custom functionality',
    'category' => 'plugin',
    'author' => 'Your Name',
    'author_email' => '[email protected]',
    'state' => 'stable',
    'version' => '1.0.0',
    'constraints' => [
        'depends' => [
            'typo3' => '12.4.0-13.4.99',
            'php' => '8.2.0-8.3.99'
        ],
    ],
];

Classes/Controller/MyController.php (Controller)

<?php
namespace Vendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use Vendor\MyExtension\Domain\Repository\ProductRepository;

class MyController extends ActionController
{
    protected ProductRepository $productRepository;
    
    public function injectProductRepository(ProductRepository $productRepository): void
    {
        $this->productRepository = $productRepository;
    }
    
    /**
     * List products
     */
    public function listAction(int $currentPage = 1): ResponseInterface
    {
        // Get products
        $products = $this->productRepository->findAll();
        
        // Pagination settings
        $itemsPerPage = $this->settings['itemsPerPage'] ?? 10;
        $paginator = GeneralUtility::makeInstance(
            QueryResultPaginator::class,
            $products,
            $currentPage,
            $itemsPerPage
        );
        
        // Assign variables to view
        $this->view->assignMultiple([
            'products' => $paginator->getPaginatedItems(),
            'paginator' => $paginator,
            'pages' => range(1, $paginator->getNumberOfPages()),
            'currentPage' => $currentPage
        ]);
        
        return $this->htmlResponse();
    }
    
    /**
     * Show product detail
     */
    public function showAction(int $product): ResponseInterface
    {
        $productObject = $this->productRepository->findByUid($product);
        
        if (!$productObject) {
            throw new \TYPO3\CMS\Core\Error\Http\PageNotFoundException(
                'Product not found',
                1234567890
            );
        }
        
        $this->view->assign('product', $productObject);
        
        return $this->htmlResponse();
    }
    
    /**
     * Search functionality
     */
    public function searchAction(string $searchTerm = ''): ResponseInterface
    {
        $products = [];
        
        if ($searchTerm !== '') {
            $products = $this->productRepository->findBySearchTerm($searchTerm);
        }
        
        $this->view->assignMultiple([
            'products' => $products,
            'searchTerm' => $searchTerm
        ]);
        
        return $this->htmlResponse();
    }
}

5. Custom Content Elements

Configuration/TCA/Overrides/tt_content.php (Register Content Element)

<?php
defined('TYPO3') or die();

// Register custom content element
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
    'tt_content',
    'CType',
    [
        'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:content_element.product_teaser',
        'product_teaser',
        'content-products'
    ],
    'textmedia',
    'after'
);

// Add fields
$tempColumns = [
    'product_limit' => [
        'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:product_limit',
        'config' => [
            'type' => 'input',
            'size' => 5,
            'eval' => 'int',
            'default' => 3
        ]
    ],
    'product_category' => [
        'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:product_category',
        'config' => [
            'type' => 'select',
            'renderType' => 'selectSingle',
            'foreign_table' => 'tx_myextension_domain_model_category',
            'foreign_table_where' => 'AND {#tx_myextension_domain_model_category}.{#pid} = ###PAGE_TSCONFIG_ID###',
            'items' => [
                ['LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:all_categories', 0]
            ],
            'default' => 0
        ]
    ]
];

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
    'tt_content',
    $tempColumns
);

// Configure palette
$GLOBALS['TCA']['tt_content']['palettes']['product_settings'] = [
    'showitem' => 'product_limit, product_category'
];

// Configure type
$GLOBALS['TCA']['tt_content']['types']['product_teaser'] = [
    'showitem' => '
        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
            --palette--;;general,
            --palette--;;headers,
        --div--;LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:product_settings,
            --palette--;;product_settings,
        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:appearance,
            --palette--;;frames,
            --palette--;;appearanceLinks,
        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
            --palette--;;language,
        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
            --palette--;;hidden,
            --palette--;;access
    '
];

6. Database Operations and Repository

Classes/Domain/Repository/ProductRepository.php (Repository Class)

<?php
namespace Vendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;

class ProductRepository extends Repository
{
    /**
     * Set default ordering
     */
    protected $defaultOrderings = [
        'sorting' => QueryInterface::ORDER_ASCENDING,
        'title' => QueryInterface::ORDER_ASCENDING
    ];
    
    /**
     * Initialize repository
     */
    public function initializeObject(): void
    {
        // Disable storage PID restriction if needed
        $querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
        $querySettings->setRespectStoragePage(false);
        $this->setDefaultQuerySettings($querySettings);
    }
    
    /**
     * Find products by category
     */
    public function findByCategory(int $categoryUid, int $limit = 0): array
    {
        $query = $this->createQuery();
        
        $constraints = [];
        $constraints[] = $query->contains('categories', $categoryUid);
        
        $query->matching($query->logicalAnd(...$constraints));
        
        if ($limit > 0) {
            $query->setLimit($limit);
        }
        
        return $query->execute()->toArray();
    }
    
    /**
     * Search by keyword
     */
    public function findBySearchTerm(string $searchTerm): array
    {
        $query = $this->createQuery();
        
        $constraints = [];
        $constraints[] = $query->like('title', '%' . $searchTerm . '%');
        $constraints[] = $query->like('description', '%' . $searchTerm . '%');
        $constraints[] = $query->like('teaser', '%' . $searchTerm . '%');
        
        $query->matching($query->logicalOr(...$constraints));
        
        return $query->execute()->toArray();
    }
    
    /**
     * Get featured products
     */
    public function findFeatured(int $limit = 5): array
    {
        $query = $this->createQuery();
        
        $query->matching(
            $query->equals('featured', true)
        );
        
        $query->setOrderings([
            'featuredSorting' => QueryInterface::ORDER_ASCENDING,
            'title' => QueryInterface::ORDER_ASCENDING
        ]);
        
        $query->setLimit($limit);
        
        return $query->execute()->toArray();
    }
    
    /**
     * Get related products
     */
    public function findRelated($product, int $limit = 4): array
    {
        $query = $this->createQuery();
        
        $constraints = [];
        
        // Search products in same categories
        foreach ($product->getCategories() as $category) {
            $constraints[] = $query->contains('categories', $category);
        }
        
        // Exclude current product
        $constraints[] = $query->logicalNot(
            $query->equals('uid', $product->getUid())
        );
        
        if (count($constraints) > 1) {
            $query->matching($query->logicalAnd(...$constraints));
        }
        
        $query->setLimit($limit);
        
        return $query->execute()->toArray();
    }
}