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 a conference listing. The browser sends an HTTP GET request to a URL like /conferences?tx_myextension_conferencelist[action]=show&tx_myextension_conferencelist[conference]=42. The controller receives that request, asks the model layer (the repository) for the Conference 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 should never query the database directly — it asks a repository for objects.

View
Fluid templates reside 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, applies business logic as needed and 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

    TYPO3 receives the browser request as a PSR-7 server request object. PSR-7 is a PHP-FIG standard that defines a common interface for HTTP messages — requests and responses — so that frameworks and libraries can interoperate without depending on each other's implementations. Extbase wraps that PSR-7 request in its own \TYPO3\CMS\Extbase\Mvc\Request object, which 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 — the component responsible for constructing objects and automatically providing their dependencies, so your controller receives everything it needs without wiring it up manually. See Dependency injection for details.

  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.

How Extbase MVC differs from other frameworks 

Developers coming from Symfony or Laravel may expect the framework to dispatch the entire URL to a controller. Extbase does not work this way.

In TYPO3, the page router handles the URL first and resolves it to a page in the page tree. Only after a page is determined does TYPO3 render its content elements — and only at that point, when a content element of the plugin's type is encountered, does Extbase take over.

This has two practical consequences:

  • Multiple plugins can live on one page. Each plugin content element is rendered independently. Two or more Extbase plugins, which may belong to different extensions, can comfortably live on the same page and each runs their own request dispatch cycle. URL arguments are namespaced per plugin (for example tx_myextension_conferencelist[action]=show) to avoid collisions.
  • Extbase does not control the page URL. Clean URLs require a route enhancer configured in the site's config.yaml. Without one, plugin arguments appear as query parameters. See Routing for Extbase plugins for the full configuration.

If you are used to a framework where every route maps to exactly one controller, it helps to think of an Extbase plugin as a self-contained sub-application embedded inside a TYPO3 page — the page router gets you to the page, then Extbase dispatches within that boundary.

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/ConferenceController.php
namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\Conference;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
    public function showAction(Conference $event): ResponseInterface
    {
        $this->view->assign('event', $event);
        return $this->htmlResponse();
    }
}
Copied!

When the URL contains event=42, Extbase loads the Conference 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 is executed 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 HTML response with status code 200 (OK)
  • $this->jsonResponse() — returns a JSON response, also with status code 200 (OK); use with \TYPO3\CMS\Extbase\Mvc\View\JsonView to control which properties are serialised

For redirects and forwards:

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