MVC pattern and request flow in Extbase 

Extbase structures extensions around the Model-View-Controller (MVC) pattern. MVC separates three concerns that have a tendency to become entangled in less structured code:

  • Model — what your data looks like and how it is persisted
  • View — how the data is presented to the user
  • Controller — what happens when an HTTP request arrives and which data the response should contain

To make this concrete: a visitor clicks "Read more" on an event listing. The browser sends an HTTP GET request to a URL like /events?tx_myextension_eventlist[action]=show&tx_myextension_eventlist[event]=42. The controller receives that request, asks the model layer (the repository) for the Event with UID 42, hands the object to the view, and the view renders it as HTML. Each layer does one job and one job only.

Keeping these three things separate means you can change how data is stored without touching the templates, and change the output format without touching the business logic. It also means that any TYPO3 developer who knows Extbase can immediately orient themselves in an unfamiliar extension.

The three Extbase layers 

Model

Domain objects — PHP classes that represent the data your extension works with. An event, a product, a blog post. Models extend \TYPO3\CMS\Extbase\DomainObject\AbstractEntity and are stored in Classes/Domain/Model/. Their properties map to database columns. Models know nothing about HTTP, templates, or how they are displayed.

Repositories sit alongside models and are the only entry point to the database. A controller never queries the database directly — it asks a repository for objects.

View
Fluid templates in Resources/Private/Templates/. The view receives variables from the controller and renders them as HTML (or JSON, or any other format). The view knows nothing about where data came from or what triggered the request.
Controller
The coordinator. It receives a request, asks repositories for the data it needs, hands that data to the view, and returns a response. Controllers live in Classes/Controller/ and extend \TYPO3\CMS\Extbase\Mvc\Controller\ActionController .

The strict separation is intentional. When something goes wrong, you know exactly which layer to look at.

How a request flows through Extbase 

When TYPO3 renders a page containing an Extbase plugin, the following sequence happens:

  1. TYPO3 hands off to Extbase

    The plugin content element is rendered by the TYPO3 content object renderer. Control passes to \TYPO3\CMS\Extbase\Core\Bootstrap , which reads the plugin configuration to determine which extension and plugin is involved.

  2. An Extbase request object is built

    The PSR-7 server request is wrapped in an Extbase \TYPO3\CMS\Extbase\Mvc\Request object. This object carries the controller name, action name, and any arguments extracted from the URL or POST data — for example, the UID of a record to display.

  3. The dispatcher resolves the controller

    \TYPO3\CMS\Extbase\Mvc\Dispatcher looks up which controller class corresponds to the requested controller name and instantiates it via the DI container.

  4. The controller action runs

    The dispatcher calls processRequest() on the controller. The controller resolves the action method name (for example listAction or showAction), maps incoming arguments to typed PHP parameters via property mapping, runs validation, and then calls the action method.

  5. The action builds the response

    Inside the action, the controller assigns variables to the view and returns a ResponseInterface. For a standard HTML response this means calling $this->htmlResponse(), which renders the matching Fluid template and wraps the output in a PSR-7 response.

  6. TYPO3 renders the response into the page

    The rendered HTML is returned to the content object renderer and inserted into the page at the position of the plugin content element.

Controller actions in Extbase 

Each public method in a controller whose name ends in Action is a potential action. The action name in the URL is the method name without the Action suffix, lowercased: listAction() is addressed as action=list.

Actions can declare typed parameters. Extbase's property mapping resolves them automatically from the request:

EXT:my_extension/Classes/Controller/EventController.php
use MyVendor\MyExtension\Domain\Model\Event;
use Psr\Http\Message\ResponseInterface;

public function showAction(Event $event): ResponseInterface
{
    $this->view->assign('event', $event);
    return $this->htmlResponse();
}
Copied!

When the URL contains event=42, Extbase loads the Event object with UID 42 from the repository and passes it directly to the action. You never write a repository lookup for this — the framework handles it.

Primitive types work the same way: a parameter typed int receives an integer, string a string. Validation runs before the action is called; if it fails, errorAction() handles the error rather than your action running with invalid data.

Extbase action responses 

Actions must return a \Psr\Http\Message\ResponseInterface . The two most common helpers on \TYPO3\CMS\Extbase\Mvc\Controller\ActionController are:

  • $this->htmlResponse() — renders the Fluid template matching the current action and returns a 200 HTML response
  • $this->jsonResponse() — returns a 200 JSON response; use with \TYPO3\CMS\Extbase\Mvc\View\JsonView to control which properties are serialised

For redirects and forwards:

  • $this->redirect('list') — sends a 303 redirect to another action
  • return new \TYPO3\CMS\Extbase\Http\ForwardResponse('list') — passes control to another action within the same request, without a redirect