How to register custom layout with annotations in Drupal 8 & 9
The layout builder lets users change the way their content is presented. There was some projects in Drupal 7 which were similar to layout builder, but it was never as effective, or as user friendly like the Layout API. The layout, when set up, can affect a content type globally, or user can change layout per node.
The layout builder can be used in two ways. You can use it to create a layout for each content type on your site and you can also use it to create a layout for each individual piece of content.
Read this tuto If you want to read how to create custom layout in Drupal 8 & 9.
After you create custom layout it's possible to use a custom 'class' for a layout! This can be useful if, for example, you want your layout to have custom settings so that users can change the way the layout is rendered.
Here's an example layout class which provides a settings form to add classes to custom layout:
<?php
namespace Drupal\mymodule\Plugin\Layout;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Layout\LayoutDefault;
use Drupal\Core\Plugin\PluginFormInterface;
class MyLayoutClass extends LayoutDefault implements PluginFormInterface
{
/**
* {@inheritdoc}
*/
public function defaultConfiguration()
{
return parent::defaultConfiguration() + [
'layout_classes' => 'layout',
'region_classes' => 'region',
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state)
{
$configuration = $this->getConfiguration();
$form['layout_classes'] = [
'#type' => 'textfield',
'#title' => $this->t('Layout classes'),
'#default_value' => $configuration['layout_classes'],
];
$form['region_classes'] = [
'#type' => 'textfield',
'#title' => $this->t('Region classes'),
'#default_value' => $configuration['region_classes'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state)
{
// any additional form validation that is required
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state)
{
$this->configuration['layout_classes'] = $form_state->getValue('layout_classes');
$this->configuration['region_classes'] = $form_state->getValue('region_classes');
}
}
Then your *.layouts.yml could look like:
custom_layout_2:
label: Custom Layout 2
category: Custom
class: '\Drupal\mymodule\Plugin\Layout\MyLayoutClass'
path: layouts/custom-layout-2
template: custom-layout-2
library: mymodule/custom-layout-2-library
regions:
main:
label: Main content
And in your Twig template, you now have access to the {{ settings.layout_classes }}
and {{ settings.region_classes }}
variables. So, your layouts/custom-layout-2/custom-layout-2
.html.twig
could look like:
{#
/**
* @file
* Default theme implementation to display a one plus four grid layout.
*
* Available variables:
* - content: The content for this layout.
* - attributes: HTML attributes for the layout <div>.
*
* @ingroup themeable
*/
#}
<div class="custom-layout-2 {{ settings.layout_classes }}">
<div class="main-region {{ settings.region_classes }} ">
{{ content.main }}
</div>
</div>
The form settings is like this:
and after you submit the form your classes are added to twig file like this:
How to register custom layout with annotations in Drupal 8 & 9
If you have a one-off layout that uses a custom class, it can be easier register it in PHP with annotations.
To do this, you put a @Layout
annotation above the layout class using the same keys as in the *.layouts.yml. Here's a simple example:
<?php
namespace Drupal\mymodule\Plugin\Layout;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Layout\LayoutDefault;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Custom Layout.
*
* @Layout(
* id = "custom_layout_2",
* label = @Translation("Custom Layout 2"),
* category = @Translation("Custom"),
* path = "layouts/custom-layout-2",
* template = "custom-layout-2",
* library = "mymodule/custom-layout-2-library",
* regions = {
* "main" = {
* "label" = @Translation("Main content"),
* }
* }
* )
*/
class MyLayoutClass extends LayoutDefault implements PluginFormInterface
{
/**
* {@inheritdoc}
*/
public function defaultConfiguration()
{
return parent::defaultConfiguration() + [
'layout_classes' => 'layout',
'region_classes' => 'region',
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state)
{
$configuration = $this->getConfiguration();
$form['layout_classes'] = [
'#type' => 'textfield',
'#title' => $this->t('Layout classes'),
'#default_value' => $configuration['layout_classes'],
];
$form['region_classes'] = [
'#type' => 'textfield',
'#title' => $this->t('Region classes'),
'#default_value' => $configuration['region_classes'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state)
{
// any additional form validation that is required
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state)
{
$this->configuration['layout_classes'] = $form_state->getValue('layout_classes');
$this->configuration['region_classes'] = $form_state->getValue('region_classes');
}
}
Each layout definition must have the following keys:
- label
- The human-readable name of the layout.
- category
- The human-readable category to which the layout belongs.
- regions
- Array of regions in the layout. The keys are the regions' machine names and the
values are sub-arrays containing the following elements:label
(required): The human-readable name of the region.
- theme_hook
- If specified, the theme hook which will be used to render this layout. It's
expected that the module or theme which registers the layout will also register this
theme hook. If you use this key, you cannot usetemplate
. - template
- If specified, the template to use to render the layout, relative to the given
path
, without the .html.twig extension. If given, the template will be
automagically registered with the theme system. If you use this key, you cannot usetheme_hook
.
Each layout definition can also have the following optional keys:
- default_region
- Machine-readable name of the default region.
- icon_map
- YML structure to describe the icon used in the layout builder interface
- description
- Optional description of the layout.
- path
- Path (relative to the module or theme) to resources like icon or template.
- library
- The asset library to load for this layout. If given, it's expected that the module
or theme which registers the layout has also registered the library in its
*.libraries.yml file. If you use this key, you cannot usecss
.