Enable Contrib Recipes Run:
composer config extra.symfony.allow-contrib true
Add to composer.json:
"extra": {
"contao-component-dir": "assets"
}
Install the Bundle
composer require contao/core-bundle:4.8.* php-http/guzzle6-adapter:^1.1
Configure Symfony
Add to config/bundles.php:
return [
// ...
Contao\CoreBundle\ContaoCoreBundle::class => ['all' => true],
];
Run Database Setup
php bin/console contao:install
Follow prompts to configure database and admin credentials.
First Use Case: Basic Page Rendering Create a controller to render a Contao page:
use Contao\CoreBundle\Controller\FrontendController;
class MyController extends AbstractController
{
public function showPage(FrontendController $frontendController, string $pageId)
{
return $frontendController->renderPage($pageId);
}
}
Page Management
Contao\CoreBundle\Framework\ContaoFramework to interact with Contao’s core.$framework = $this->get('contao.framework');
$framework->initialize();
$page = $framework->getAdapter('PageModel')->findByPk($pageId);
Content Elements
# config/services.yaml
services:
App\ContentElement\MyCustomElement:
tags: ['contao.content_element']
Themes and Templates
vendor/contao/core-bundle/src/Resources/contao/templates/ to assets/contao/templates/.{# assets/contao/templates/ce_my_custom.html5 #}
<div class="my-custom-element">
{{ content }}
</div>
Backend Integration
use Contao\CoreBundle\DataContainer\PaletteManipulator;
class MyBackendModule extends AbstractModule
{
public function getSubPalette($dc, $strTable)
{
return PaletteManipulator::create()
->addField('my_field', 'input_fields', PaletteManipulator::POSITION_APPEND)
->getPalette();
}
}
Register in config/services.yaml:
services:
App\BackendModule\MyBackendModule:
tags: ['contao.backend_module']
Routing
use Contao\CoreBundle\Routing\ScopeMatcher;
$scopeMatcher = $this->get('contao.routing.scope_matcher');
if ($scopeMatcher->getScope('frontend')) {
// Handle frontend request
}
Contao\CoreBundle\Form\FormBuilder.contao.initialize, contao.save_page) for custom logic:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Contao\CoreBundle\Event\InitializeEvent;
class MyEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
InitializeEvent::NAME => 'onInitialize',
];
}
public function onInitialize(InitializeEvent $event)
{
// Custom logic here
}
}
{# assets/contao/css/my_styles.css #}
.my-class { color: red; }
Enqueue in a template:
{{ contao_assets('css/my_styles.css') }}
Component Directory
contao-component-dir in composer.json will break asset compilation.composer config extra.contao-component-dir assets and clear cache.Framework Initialization
ContaoFramework before using models:
$framework = $this->get('contao.framework');
$framework->initialize(); // Critical!
$model = $framework->getAdapter('PageModel');
initialize() causes NullReferenceException.Template Overrides
assets/contao/templates/ requires clearing the cache:
php bin/console cache:clear
contao:debug:templates to verify overrides:
php bin/console contao:debug:templates
Backend Module Permissions
tl_backend_user table).Contao\CoreBundle\Security\Authorization\ContaoPermission for granular checks.Database Migrations
ALTER TABLE queries.Contao\CoreBundle\Migration\MigrationGenerator for safe schema updates.Enable Debug Mode
Set CONTAO_DEBUG=1 in .env for detailed error logs and template debugging:
CONTAO_DEBUG=1
APP_ENV=dev
Log Contao Events Enable event dispatching logs:
# config/packages/dev/monolog.yaml
handlers:
contao:
type: stream
path: "%kernel.logs_dir%/contao.log"
level: debug
channels: ["contao"]
Dump Models Use Contao’s debug tools:
$framework->getAdapter('PageModel')->findAll()->dump(); // Laravel-style dump
Custom Models
Extend Contao’s models via Contao\CoreBundle\Model\ModelInterface:
class MyCustomModel extends AbstractModel
{
protected static $strTable = 'tl_my_custom';
}
Register in config/services.yaml:
services:
App\Model\MyCustomModel:
tags: ['contao.model']
Hooks Use Contao’s hook system for non-event-based extensions:
$hook = new \Contao\CoreBundle\Framework\ContaoFramework();
$hook->initialize();
$hook->import('BackendUser', 'User');
if (isset($GLOBALS['TL_HOOKS']['generatePage'])) {
$GLOBALS['TL_HOOKS']['generatePage'][] = ['MyClass', 'customPageLogic'];
}
DCA (Data Container Attributes) Modify data containers dynamically:
$dc = new \Contao\CoreBundle\DataContainer\DataContainer('tl_page');
$dc->loadData();
$dc->activeRecord->customField = 'value';
$dc->save();
CLI Commands Extend Contao’s CLI with custom commands:
use Symfony\Component\Console\Command\Command;
use Contao\CoreBundle\Command\ContaoCommand;
class MyContaoCommand extends ContaoCommand
{
protected function configure()
{
$this->setName('contao:my-task');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->initializeContaoFramework();
// Custom logic here
}
}
Register in config/services.yaml:
services:
App\Command\MyContaoCommand:
tags: ['console.command']
How can I help you explore Laravel packages today?