Front End Modules
Front end modules in Contao are used for more complex functionality, which are typically used on more than one page or even in page layouts. They are used to generate dynamic content, like news lists, displaying the detailed content of news or navigation items.
These modules are implemented as so called fragment controllers which Contao then renders into the main content, using their defined renderer. See the caching documentation for more information.
Creating a front end module is very similar to creating content elements.
Definition
To create a new front end module, the following things must be defined and implemented:
Fragment Controller
The actual implementation of the front end module is done via a class that extends fromAbstractFrontendModuleControllerof the Contao core.Service Tag
To identify the controller as a Contao front end module, the service must be tagged with the service tagcontao.frontend_module- and the tag will hold the following additional information:Type
The type of a front end module is a specific string which is used to identify the element’s template (if not defined) and DCA palette. If omitted the type will be automatically inferred by converting the class name of the controller from pascal case to snake case and removing a possibleControllerpostfix.Category
All front end modules are categorised within the type dropdown of the front end module’s palette. The default category ismiscellaneouswhen using PHP attributes to tag the service.Template
If not specified, the Twig template name follows is the type and prepends it with thefrontend_module/path, i.e.frontend_module/<type>.html.twig.
Example
Consider this fairly simple example of a front end module:
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\Exception\RedirectResponseException;
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
use Contao\ModuleModel;
use Contao\PageModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
#[AsFrontendModule]
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
if ($request->isMethod(Request::METHOD_POST)) {
if (null !== ($redirectPage = PageModel::findById($model->jumpTo))) {
throw new RedirectResponseException($redirectPage->getAbsoluteUrl());
}
}
$template->action = $request->getUri();
return $template->getResponse();
}
}In this example the service tag was implemented via PHP attributes. The controller itself processes the
request and checks, if it was a POST request. In that case, the redirect page is loaded via Contao’s model functionality
and a RedirectResponseException is thrown to redirect to that page.
In order to be able to set the options for our front end module in the back end, we also need to define a palette
in the tl_module DCA configuration. The palette key is based on the type of the front end module. Since we did not
specify a type in our example it defaults to example_module as explained above.
// contao/dca/tl_module.php
$GLOBALS['TL_DCA']['tl_module']['palettes']['example_module'] = '
{title_legend},name,headline,type;
{redirect_legend},jumpTo;
';This very simple palette enables us to select a redirect page using the pre-existing jumpTo field.
Using the naming convention for templates also mentioned above, the final template name for this front end module will
be frontend_module/example_module for Twig templates:
{# templates/frontend_module/example_module.html.twig #}
{% extends "@Contao/frontend_module/_base.html.twig" %}
{% block content %}
<form action="{{ action }}" method="POST">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<button type="submit">Submit</button>
</form>
{% endblock %}A “fragment template” instance of this template will automatically be generated and passed to the controller’s
getResponse() method. The controller then returns the rendered Twig template as a response.
Registration
As mentioned previously a front end module is registered by registering a controller as a service and tagging it with the
contao.frontend_module service tag. The service tag supports the following options:
| Option | Type | Description |
|---|---|---|
| name | string | Must be contao.frontend_module. |
| type | string | Optional: The type mentioned in Type can be customized. |
| category | string | Defines in which option group this front end module will be placed in the module type selector. |
| template | string | Optional: Override the generated template name. |
| renderer | string | Optional: The renderer can be changed to inline or esi. Defaults to forward. See Caching Fragments for more details. |
| method | string | Optional: Which method should be invoked on the controller. |
Applying the service tag can either be done via PHP attributes, annotations or via the YAML configuration.
A front end module can be registered using the AsFrontendModule PHP attribute.
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
#[AsFrontendModule(category: 'miscellaneous')]
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
return $template->getResponse();
}
}The above example only defines the mandatory category attribute. If you wish you can also define the other options of the service tag:
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
#[AsFrontendModule(
type: 'example',
category: 'miscellaneous',
template: 'frontend_module/example',
renderer: 'forward',
method: '__invoke',
priority: 100,
)]
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
return $template->getResponse();
}
}However, it is recommended to only define what you need and otherwise leave the defaults.
A front end module can be registered using the FrontendModule annotation. The annotation can be used on the class of the front end module,
if the class is invokable (has an __invoke method) or extends from the AbstractFrontendModuleController. Otherwise the annotation can be
used on the method that will deliver the response.
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\ServiceAnnotation\FrontendModule;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @FrontendModule(category="miscellaneous")
*/
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
return $template->getResponse();
}
}The above example only defines the mandatory category attribute. If you wish you can also define the other options of the service tag:
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\ServiceAnnotation\FrontendModule;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @FrontendModule("example", category="miscellaneous", template="mod_example", renderer="forward", method="__invoke")
*/
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
return $template->getResponse();
}
}A front end module can be registered using the contao.frontend_module service tag.
# config/services.yaml
services:
App\Controller\FrontendModule\ExampleModuleController:
tags:
-
name: contao.frontend_module
category: miscellaneous// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
return $template->getResponse();
}
}The above example only defines the mandatory category attribute. If you wish you can also define the other options of the service tag:
# config/services.yaml
services:
App\Controller\FrontendModule\ExampleModuleController:
tags:
-
name: contao.frontend_module
category: miscellaneous
template: mod_example
renderer: forward
method: __invokeTranslations
In order to have a nice label in the back end, we also need to add a translation for our front end module - otherwise it will only be named example_module. The translation needs to be set as follows:
# translation/contao_modules.en.yaml
FMD:
example_module:
- My front end module
- A front end module for testing purposesPage Model
If your fragment extends from AbstractFrontendModuleController (or just AbstractFragmentController)
you can use $this->getPageModel() in order to receive the \Contao\PageModel
object of the currently rendered page of Contao’s site structure.
// src/Controller/FrontendModule/ExampleModuleController.php
namespace App\Controller\FrontendModule;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\Exception\RedirectResponseException;
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
use Contao\ModuleModel;
use Contao\PageModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
#[AsFrontendModule(category: 'miscellaneous')]
class ExampleModuleController extends AbstractFrontendModuleController
{
protected function getResponse(Template $template, ModuleModel $model, Request $request): Response
{
$page = $this->getPageModel();
// Get some information about the current page
$template->rootTitle = $page->rootPageTitle ?: $page->rootTitle;
return $template->getResponse();
}
}