Common tasks in Extbase 

This page answers the "how do I…" questions that come up repeatedly when building or maintaining Extbase extensions. Each entry states the goal, gives a short direct answer, and links to the full explanation.

If you know what you want to achieve but are not sure which chapter covers it, start here.

Add a field to a third-party extension's model 

Goal: A third-party extension (for example georgringer/news ) has a model you want to extend with an extra database field — without forking the extension.

Short answer: You need three things working together: a TCA override that adds the column to the table, a class mapping override in your own extension that tells Extbase to use your subclass instead of the original, and your subclass extending the third-party model with the new property and getter/setter. No changes to the original extension required.

Query records with custom conditions 

Goal: findAll() and findBy() are not enough — you need records filtered by date, a relation, or a combination of conditions.

Short answer: Call $this->createQuery() in your repository method, build constraints with $query->matching(), and return $query->execute(). For anything the Extbase query API cannot express (aggregates, complex joins), drop down to ConnectionPool and raw DBAL.

Use a repository in a controller 

Goal: Make a repository available inside a controller action without using GeneralUtility::makeInstance().

Short answer: Declare the repository as a constructor parameter with protected readonly. TYPO3's DI container injects it automatically — no annotation, no factory call needed.

EXT:my_extension/Classes/Controller/BlogPostController.php
class BlogPostController extends ActionController
{
    public function __construct(
        protected readonly BlogPostRepository $blogPostRepository,
    ) {}
}
Copied!

Set a default sort order for all repository queries 

Goal: Every call to findAll() or findBy() on a repository should return records sorted by a specific property, without having to specify ordering in every call.

Short answer: Set the $defaultOrderings class property on your repository. It applies automatically to all queries from that repository.

EXT:my_extension/Classes/Domain/Repository/BlogPostRepository.php
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;

class BlogPostRepository extends Repository
{
    protected $defaultOrderings = [
        'publishDate' => QueryInterface::ORDER_ASCENDING,
    ];
}
Copied!

Load a relation only when needed 

Goal: A model has a related object or collection that is expensive to load and not always needed — for example, the comments on an event in a list view. You want to avoid loading them unless the template actually uses them.

Short answer: Add #[Lazy] to the relation property. Extbase defers the database query until the property is first accessed. For 1:1 relations, the getter must also handle the LazyLoadingProxy intermediate.

Use a native PHP enum as a model property 

Goal: A model property should hold one of a fixed set of values — for example a status or salutation — and you want to use a native PHP 8.1 backed enum (an enum with an underlying string or int value that can be stored in the database) rather than a plain string or integer.

Short answer: Declare the property with the enum type and a default case. Extbase's built-in EnumConverter handles the conversion between the stored backing value and the enum instance automatically. No extra configuration needed.

Add a computed property that is never stored in the database 

Goal: A model needs a property that holds a computed or temporary value — for example a formatted label derived from other properties — that should never be written to the database.

Short answer: Add #[Transient] to the property. Extbase skips it entirely during read and write operations.