Controller

The controller controls the flow of data between the view and the data repository containing the model.

A controller can contain one or more actions. Each of them is a method which ends on the name "Action" and returns an object of type \Psr\Http\Message\ResponseInterface.

In the following action a tea object should be displayed in the view:

Class TTN\Tea\Controller\TeaController
use Psr\Http\Message\ResponseInterface;
use TTN\Tea\Domain\Model\Product\Tea;

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

This action would be displayed if an URL like the following would be requested: https://www.example.org/myfrontendplugin?tx_tea[action]=show&tx_tea[controller]=tea&tx_tea[tea]=42&chash=whatever.

So where does the model Tea $tea come from? The only reference we had to the actual tea to be displayed was the ID 42. In most cases, the parent class \TYPO3\CMS\Extbase\Mvc\Controller\ActionController will take care of matching parameters to objects or models. In more advanced scenarios it is necessary to influence the parameter matching. But in our scenario it is sufficient to know that this happens automatically in the controller.

The following action expects no parameters. It fetches all available tea objects from the repository and hands them over to the view:

Class TTN\Tea\Controller\TeaController
use Psr\Http\Message\ResponseInterface;
use TTN\Tea\Domain\Repository\Product\TeaRepository;

class TeaController extends ActionController
{
    private TeaRepository $teaRepository;

    public function injectTeaRepository(TeaRepository $teaRepository): void
    {
        $this->teaRepository = $teaRepository;
    }

    public function indexAction(): ResponseInterface
    {
        $this->view->assign('teas', $this->teaRepository->findAll());
        return $this->htmlResponse();
    }
}
Copied!

The controller has to access the TeaRepository to find all available tea objects. We use Dependency Injection to make the repository available to the controller: The method injectTeaRepository() will be called automatically with an initialized TeaRepository when the TeaController is created.

Both action methods return a call to the method $this->htmlResponse(). This method is implemented in the parent class ActionController and is a shorthand method to create a response from the response factory and attach the rendered content. Let us have a look at what happens in this method:

Class TYPO3\CMS\Extbase\Mvc\Controller\ActionController
use Psr\Http\Message\ResponseInterface;

abstract class ActionController implements ControllerInterface
{
    protected function htmlResponse(string $html = null): ResponseInterface
    {
        return $this->responseFactory->createResponse()
            ->withHeader('Content-Type', 'text/html; charset=utf-8')
            ->withBody($this->streamFactory->createStream((string)($html ?? $this->view->render())));
    }
}
Copied!

You can also use this code directly in your controller if you need to return a different HTTP header. If a different rendering from the standard view is necessary you can just pass the rendered HTML content to this method. There is also a shorthand method for returning JSON called jsonResponse().

This basic example requires no actions that are forwarding or redirecting. Read more about those concepts here: Forward to a different controller.