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_.
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.
On this page
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\and are stored inCMS\ Extbase\ Domain Object\ Abstract Entity Classes/. Their properties map to database columns. Models know nothing about HTTP, templates, or how they are displayed.Domain/ Model/ 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/. 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.Private/ Templates/ - 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/and extendController/ \TYPO3\.CMS\ Extbase\ Mvc\ Controller\ Action Controller
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:
-
TYPO3 hands off to Extbase
The plugin content element is rendered by the TYPO3 content object renderer. Control passes to
\TYPO3\, which reads the plugin configuration to determine which extension and plugin is involved.CMS\ Extbase\ Core\ Bootstrap -
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\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.CMS\ Extbase\ Mvc\ Request -
The dispatcher resolves the controller
\TYPO3\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.CMS\ Extbase\ Mvc\ Dispatcher -
The controller action runs
The dispatcher calls
processon the controller. The controller resolves the action method name (for exampleRequest () listorAction show), maps incoming arguments to typed PHP parameters via property mapping, runs validation, and then calls the action method.Action -
The action builds the response
Inside the action, the controller assigns variables to the view and returns a
Response. For a standard HTML response this means callingInterface $this->html, which renders the matching Fluid template and wraps the output in a PSR-7 response.Response () -
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.
Note
If an action calls
$this->redirect or returns a
\TYPO3\, the dispatcher loops and
processes the new target action before returning a final response. This is
how multi-step flows (for example: form → validate → confirm) work within a
single page request.
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_) to avoid collisions.myextension_ conferencelist [action]=show - Extbase does not control the page URL. Clean URLs require a route
enhancer configured in the site's
config.. Without one, plugin arguments appear as query parameters. See Routing for Extbase plugins for the full configuration.yaml
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:
list is addressed as
action=list.
Actions can declare typed parameters. Extbase's property mapping resolves them automatically from the request:
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();
}
}
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,
error handles the error rather than your action
running with invalid data.
Extbase action responses
Actions must return a
\Psr\. The two most
common helpers on
\TYPO3\ are:
$this->html— renders the Fluid template matching the current action and returns a HTML response with status code 200 (OK)Response () $this->json— returns a JSON response, also with status code 200 (OK); use withResponse () \TYPO3\to control which properties are serialisedCMS\ Extbase\ Mvc\ View\ Json View
For redirects and forwards:
$this->redirect— creates a redirect via request with 303 status code (SEE OTHER) to another action('list') return new \— passes control to another action within the same request, without a redirectTYPO3\ CMS\ Extbase\ Http\ Forward Response ('list')
See also
ActionController: actions, arguments and responses — the full controller reference, including argument handling, error actions, and backend controllers.
Extbase domain model — how models and repositories work.
View layer in Extbase — Fluid templates and response types.