There are two main cases when developing within Contao: either you want to customize your project-specific web application, or you want to create a re-usable extension - either for your own purposes or for others. In either case, the principles are the same. However, when creating an extension for Contao, there are some differences in structure and procedure. This articles covers the former case and also assumes the usage of the Contao Managed Edition. Another article explains how to create a reusable extension.
The purpose of this article is to show the directory structure of Contao and explain what goes where - and what to do initially for certain customization tasks.
After a fresh install of Contao, your project will have a certain initial file &
directory structure (which is similar to the structure of a pure Symfony project
using the symfony/skeleton
for example).
File/Directory | Explanation |
---|---|
assets/ | JavaScript and CSS assets of the Contao framework and third parties. |
config/ | Application configuration files. |
files/ | Public or protected files manged by Contao’s file manager. |
public/ | Public entry points; contains symlinks to other public resources. Prior to Contao version 4.13, the folder was called web/ |
system/ | Legacy folder for Contao 3 compatibility. |
templates/ | Customized Contao & Twig templates. |
var/ | Transient files like the application cache and log files. |
vendor/ | Composer’s vendor folder containing all dependencies (including Contao). |
composer.json | composer.json of your project defining your dependencies and autoloading. |
When customizing your web application, the following files and folders will usually be of interest. Some of those will need to be created manually:
File/Directory | Explanation |
---|---|
config/ | Application configuration. |
contao/ | Contao configuration and translations. |
src/ | Your own PHP code: controllers, event listeners for hooks and other services. |
templates/ | Templates for your own modules and elements, or customized existing templates. |
translations/ | Symfony translations. since 5.3 Can also be used for Contao translations. |
composer.json | Add dependencies, customize autoloading if required. |
Contao 4.4 still uses the Symfony 3 directory structure. The config/
folder
will be in app/config/
and the contao/
folder will be in app/Resources/contao/
instead.
The configuration of a Symfony application is done within the config/
directory
through various YAML files. The Contao Managed Edition automatically loads the following
configurations, if present:
File | Explanation |
---|---|
.env | Defaults for environment variables or environment variables that are agnostic to the environment. This is typically comitted to your project’s repository and thus should not contain any sensitive data. |
.env.local | Loaded if .env exists. Defines or overrides environment variables for the current environment (e.g. database and SMTP credentials). This should be added to your .gitignore as it typically contains sensitive data. |
config/config.yaml | Configuration of any bundle/package/extension. |
config/config_dev.yaml | Configuration for the dev environment. |
config/config_prod.yaml | Configuration for the prod environment. |
config/parameters.yaml | Parameters like database and SMTP server credentials.1 |
config/routes.yaml | Definition of application specific routes.2 |
config/services.yaml | since 4.9 Definition of services.3 |
Contao versions prior to 4.9 only support the *.yml
file extension.
1 Contao still supports the legacy way of defining parameters in a Symfony
application through the parameters.yaml
. However it is best-practice to use the
.env
files instead. See also Symfony’s documentation
for more information about the .env*
files.
2 Contao versions 4.6, 4.7 and 4.8 only support the routing.yml
file. Starting with Contao 4.9 all 4 variants
(routes.yaml
, routing.yaml
, routes.yml
and routing.yml
) are supported. Prior to Contao 4.6 you will need to implement an
App\ContaoManager\Plugin
that implements the
RoutingPluginInterface
.
3 While Contao versions prior to 4.9 do not load a config/services.yml
automatically,
you can still import it in your config/config.yml
via
imports:
- { resource: services.yml }
Contao has its own configuration files in the form of PHP arrays, as well as translation
files in the form of either PHP arrays or in an XLIFF format. These files are generally
defined within the contao/
folder of your project’s root directory (or app/Resources/contao/
in Contao 4.4).
File/Directory | Explanation |
---|---|
contao/config/config.php | Registering modules, content elements, models, hooks, crons, etc. |
contao/dca/ | Data Container Array customizations and definitions. |
contao/languages/ | Contao translations - contains sub directories for each language. |
contao/languages/de/ | German translations. |
contao/languages/en/ | English translations (also serves as the fallback). |
contao/languages/…/ | etc. |
Have a look at the DCA documentation on how to handle DCA files and the translation documentation
on how to handle translation files. The content of the config.php
depends on the
use-case and its documentation is covered in the applicable topics of the framework documentation.
Any custom class that is needed for the web application - like a Model, a Content Element
or a class for a Hook for example - needs to be loaded. Typically
the code for customizing the web application for Contao will be put in the \App
namespace. The default composer.json
of the recent Contao Managed Edition versions
already contains the appropriate autoloading directive:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
This is actually not necessary since Contao 4.9 as the Contao Managed Edition automatically adds the necessary
autoloading for the App\
namespace. However you can still add your own autoloading directives of course.
During development it is advisable to not optimize the Autoloader by Composer, otherwise
new classes that you created will not be available immediately. If you used the
-o
/--optimize-autoloader
option previously, or the Contao Manager (which optimizes
the Autoloader by default), execute composer install
or composer dump-autoload
without the parameter again. Then in the production environment you should use the
parameter again for performance reasons.
Any of these classes can also be registered as Symfony services, which is necessary if you want to use dependency injection and service tagging. Registering Contao hooks, content elements, front end modules, cron jobs and Data Container callbacks is done via service tags.
For the application development it is recommended to use autowiring and auto-configuration, which makes it easy to create new services and tag them automatically (through annotations or class interfaces) - and thus enables quick development of Contao content elements, hooks, callbacks etc.
# config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: ../src
If you want to implement regular controllers and define their routes via PHP attributes, you will also need to register
these routes via the automatically loaded config/routes.yaml
:
# config/routes.yaml
app.controller:
resource: ../src/Controller
type: attribute
Once this is configured, hooks, callbacks, content elements and front end modules for example can be created without having to configure them in separate files by using annotations for service tagging.
All this is not needed, if you only need to change or extend the Data Container Array definition of a table, or want to change a translation for example.
Starting with Contao 4.9 (Managed Edition), any class within the App\
namespace
within src/
will be automatically registered as a service, with autowiring and
autoconfiguration enabled. Controllers as services will work as well. However, any class extending from a class from the legacy Contao
framework will not be automatically registered as a service (as well as any class that would cause an error during compilation). You can
still provide your own services.yaml
in order to adjust the service registration to your
needs. Keep in mind that you still need to provide your own routes.yaml
in order
to register your routes.