.

Layout Builder Anpassung. Erstellen eines benutzerdefinierten Layouts in Drupal

Layout Builder ermöglicht das schnelle Erstellen von Website-Layouts aus vorgefertigten Komponenten, die zu Abschnitten hinzugefügt werden. Standardmäßig bietet Drupal vier Arten von Abschnitten: einspaltig, zweispaltig, dreispaltig und vierspaltig. Diese Spalten haben ein vordefiniertes Verhalten, das die Redakteure nicht steuern können. Drupal bietet die Möglichkeit, eigene Abschnittstypen zu erstellen, sodass wir sie an unser Projekt anpassen können. Diesen Prozess zeigen wir Ihnen in unserem Artikel.

Wie erstellt man einen benutzerdefinierten Abschnitt im Layout Builder?

Der erste und wichtigste Schritt ist, die Ziele zu definieren und somit die Liste der Funktionen, die der Abschnitt bieten soll. Dann lohnt es sich, die Funktionen in kleine Aufgaben zu unterteilen, die in einem bestimmten Zeitrahmen erledigt werden können. Das Ziel unseres Abschnitts wird es sein, die Möglichkeit zu bieten, Klassen zum Hauptabschnitts-Wrapper und zu individuellen Regionen hinzuzufügen.

Als Basis verwenden wir die im Drupal Layout Builder-Modul verfügbare Vorlage, die in den mit der Modulintegration verfügbaren Abschnitten verwendet wird. Unsere Aufgabenliste sollte Folgendes umfassen:

  • Erstellen eines benutzerdefinierten Moduls,
  • die Definition eines Abschnitts in der Datei layouts.yml,
  • die Definition einer Vorlage für unsere Abschnitte und das Plugin, in dem wir die Logik des Hinzufügens unserer Klassen einbetten.

Erstellen eines neuen Moduls in Drupal

Wir müssen eine Standard-*.info.yml-Datei erstellen, wie in jedem Modul. Für eine detaillierte Beschreibung verweisen Sie bitte auf die Dokumentation auf Drupal.org.

# Dateiname: custom_layout_builder.info.yml

name: Custom Layout Builder sections
description: Funktionalität, die den Layout Builder erweitert
core_version_requirement: ^9
type: module
package: custom
dependencies:
  - drupal:layout_builder

Wir wissen, wofür das Modul gedacht ist, weil wir die erforderliche Funktionalität definiert haben. Daher sind wir bereits in diesem Stadium sicher, dass die Liste der Abhängigkeiten mindestens das Layout Builder-Modul umfassen sollte. Nach der Definition der info.yml-Datei ist es sinnvoll, den Cache zu leeren und zu überprüfen, ob das Modul in der Liste erscheint. Dazu rufen wir die Modulansicht auf und suchen das Modul nach Titel oder Maschinennamen. Wir sollten unser Modul mit einer Liste der erforderlichen Abhängigkeiten sehen.

Informationen über Ihr neues Drupal-Modul mit einer Liste von Abhängigkeiten

 

Wie wir klar sehen können, auch wenn wir nur eine Abhängigkeit zum Layout Builder-Modul angegeben haben, ist die Liste etwas länger. Dies liegt daran, dass das Layout Builder-Modul seine eigene Abhängigkeitsliste hat, die von unserem Modul geerbt wird.

Ansicht einer Liste von Abhängigkeiten des Drupal Layout Builder-Moduls

 

In diesem Stadium ist es sinnvoll, die Dokumentation zu beginnen, um die mentale Gesundheit anderer Entwickler zu berücksichtigen (oder Ihre, wenn Sie nach ein paar Monaten zu diesem Code zurückkehren) und die Implementierung des Hooks hook_help() zu beginnen.

Ein Beispiel für die Implementierung von hook_help() im Code des neuen Drupal-Moduls

 

Es ist auch eine gute Idee, eine README.md-Datei zu erstellen und sie aktuell zu halten.

Registrierung eines Abschnitts mit *.layouts.yml

Um einen neuen Abschnitt zu registrieren, ist der einfachste Weg, die Datei *.layouts.yml hinzuzufügen (wobei * der Maschinenname unseres Moduls ist). Die Datei sollte im Hauptordner des Moduls hinzugefügt werden, wo wir die *.info.yml-Datei hinzugefügt haben.

Lassen Sie uns mit der Definition eines Abschnitts beginnen:

# Dateiname: custom_layout_builder.layouts.yml

layout_custom_one_column:                 # Hauptschlüssel des Abschnitts
  label: '[CUSTOM] Eine Spalte'          # Titel des Abschnitts
  category: 'Benutzerdefinierte Layouts'
  path: layouts/custom_onecol_section     # Relativer Pfad von der Vorlage
  template: layout--custom-onecol-section # Name der Vorlage
  default_region: first
  icon_map:
    - [first]
  regions:                                # Tabelle der Regionen
    first:                                # Maschinenname der Region
      label: Erste                        # Titel der Region

Nach der Konfiguration sollten wir, beim Hinzufügen von Abschnitten, in der Lage sein, unseren neu definierten Abschnitt zu sehen.

Hier sieht man, wie der frisch definierte Abschnitt im neuen Drupal-Modul aussieht

 

Definition der Abschnitts-Vorlage

Um einen Abschnitt hinzufügen zu können, müssen wir noch die Vorlage hinzufügen, deren Name und Pfad wir angegeben haben. In unserem Fall müssen wir den Ordner layouts/custom_onecol_section erstellen, in den die Datei layout--custom-onecol-section.html.twig gelegt werden muss.

Standardmäßig hat die Vorlage Zugriff auf vier Variablen: content, attributes, region_attributes, und settings. Wenn wir keine Blöcke im Abschnitt platzieren, wird die Variable content nach der Umwandlung in einen booleschen Wert false zurückgeben. Wir können dieses Verhalten nutzen, um die Anzeige leerer HTML-Tags zu vermeiden. In der Content-Variable finden wir die Schlüssel, die den definierten Regionen entsprechen, und in diesen Regionen befinden sich die Blöcke, die wir hinzugefügt haben. In der content-Variable werden wir nur den first-Schlüssel finden, weil das der einzige ist, den wir definiert haben. Das Verhalten von content.first bei der Konvertierung in einen booleschen Wert ist analog zum Verhalten der content-Variable. Wir werden dies nutzen, um leere Tags nicht anzuzeigen.

# Dateiname:  layout--custom-onecol-section.html.twig

{#
/**
 * @file
 * Standard-Implementierung für einen benutzerdefinierten Layout-Onecol-Abschnitt.
 *
 * Verfügbare Variablen:
 * - content: Der Inhalt für dieses Layout.
 * - attributes: HTML-Attribute für das Layout <div>.
 * - region_attributes: HTML-Attribute für die Region <div>.
 * - settings: Ein Array von konfigurierten Einstellungen für das Layout.
 *
 * @ingroup themeable
 */
#}
{% if content %}
  <div{{ attributes }}>

    {% if content.first %}
      <div {{ region_attributes.first }}>
        {{ content.first }}
      </div>
    {% endif %}

  </div>
{% endif %}

Nach dem Hinzufügen der Vorlage sollten wir in der Lage sein, unseren Abschnitt einfach hinzuzufügen:

Definition des Layout-Plugins

Aus der Sicht des Endbenutzers haben wir bisher nichts getan, da der Inhaltsredakteur nur den neuen Abschnittstitel mit dem großen [CUSTOM]-Präfix sehen wird. Das liegt daran, dass der von uns hinzugefügte Abschnitt identisch mit dem Standardabschnitt ist, der mit dem Layout Builder-Modul bereitgestellt wird (mit einer kleinen Ausnahme: unsere Implementierung fügt keine Klassen hinzu). Um das Verhalten zu ändern, müssen wir ein neues Layout-Plugin implementieren.

Rahmen der Basisklasse

Die Klasse sollte sich im Ordner src/Plugin/Layout befinden. Sie wird generisch genug sein, um für eine beliebige Anzahl von Regionen verwendet zu werden. Die Klasse Drupal\Core\Layout\LayoutDefault enthält viele Basismethoden und implementiert die benötigten Schnittstellen. Um das Rad nicht neu zu erfinden, können wir es in unserer Klasse erweitern.

# Dateiname: CustomLayoutClassBase.php

<?php

namespace Drupal\custom_layout_builder\Plugin\Layout;

use Drupal\Core\Layout\LayoutDefault;

/**
 * Basisklasse unserer benutzerdefinierten Layouts mit konfigurierbaren HTML-Klassen.
 *
 * @internal
 *   Plugin-Klassen sind intern.
 */
class CustomLayoutClassBase extends LayoutDefault {

}

Hinzufügen von Konfigurationsoptionen zu einer Basisklasse

Eine der Anforderungen ist die Möglichkeit, eine Klasse für das Tag auszuwählen, das die Regionen des Abschnitts umschließt. Dazu müssen wir zunächst die Methode defaultConfiguration überschreiben und eine neue Konfigurationsoption hinzufügen.

/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {
  $configuration = parent::defaultConfiguration();

  return $configuration + [
    'wrapper_classes' => '',
  ];
}

Dann sollten wir die Möglichkeit hinzufügen, einen Wert für diese Konfigurationsoption anzugeben. Dies können wir durch das Überschreiben der Methoden buildConfigurationForm und submitConfigurationForm erreichen.

/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $form['wrapper_classes'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Zusätzliche Wrapper-Klassen'),
    '#default_value' => $this->configuration['wrapper_classes'],
    '#description' => $this->t('Zusätzliche Wrapper-Klassen. Geben Sie so viele ein, wie Sie möchten, aber denken Sie daran, sie durch ein einzelnes Leerzeichen zu trennen.'),
  ];

  return parent::buildConfigurationForm($form, $form_state);
}

/**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  parent::submitConfigurationForm($form, $form_state);

  $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes');
}

Wenn es nötig ist, die Formularvalidierung hinzuzufügen, kann dies durch das Überschreiben der Methode validateConfigurationForm erfolgen. Wir empfehlen die Implementierung der Validierung für dieses Feld, da Klassen den Standards der Drupal-Community entsprechen sollten. Die Methode Html::getClass() kann in diesem Fall nützlich sein.

Verwendung von Konfigurationen zum Erstellen von Abschnitten

Das Render-Array wird in der build-Methode erstellt, und genau das werden wir jetzt überschreiben. Wenn Sie sich an den Inhalt der Vorlage erinnern, die wir hinzugefügt haben, wissen Sie wahrscheinlich bereits, dass wir Klassen zum attributes-Objekt hinzufügen.

/**
 * {@inheritdoc}
 */
public function build(array $regions): array {
  $build = parent::build($regions);
  $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']);
  $build['#attributes']['class'] = [...$wrapper_classes];

  return $build;
}

Verwendung der Basis zur Erstellung eines Layout-Plugins

Unsere Klasse ist bereit, jetzt ist es Zeit, sie in einem Abschnitt zu verwenden. Dazu gehen wir zurück zur Datei *.layouts.yml, um ein neues Plugin zu deklarieren. Dies geschieht durch Angabe des vollständigen Namespace der Klasse unter dem class-Schlüssel.

# Dateiname: custom_layout_builder.layouts.yml

layout_custom_one_column:
  label: '[CUSTOM] Eine Spalte'
  category: 'Benutzerdefinierte Layouts'
  path: layouts/custom_onecol_section
  template: layout--custom-onecol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomOneColLayout'
  default_region: first
  icon_map:
    - [first]
  regions:
    first:
      label: Erste

Nach den oben genannten Änderungen werden Sie feststellen, dass das Abschnittsformular ein neues Feld hat und dass die in diesem Feld eingegebenen Klassen an der richtigen Stelle im HTML stehen.

Hier sieht man ein neu hinzugefügtes Feld im Formular in Drupal

Der HTML-Schnipsel mit den Klassen, sichtbar während der Erstellung eines Layout-Plugins

Hinzufügen der Option zum Auswählen von Klassen für Regionen

Wir können bereits eine Klassenliste für das Umhüllungselement in unserem Abschnitt definieren. Es ist Zeit, darüber nachzudenken, wie man die Logik erstellt, die für das Hinzufügen von Klassen zu einzelnen Bereichen unseres Layouts verantwortlich ist. Wir sollten die Erweiterbarkeit unserer Basisklasse in Betracht ziehen. Deshalb empfehlen wir, die Logik zur Bestimmung und zum Zugriff auf Regionen auf der Grundlage der Methode getRegionNames() der Klasse LayoutDefinition zu etablieren.

1. Zuerst fügen wir für jede Region ein Feld zu unserem Formular hinzu:

/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $form['wrapper_classes'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Zusätzliche Wrapper-Klassen'),
    '#default_value' => $this->configuration['wrapper_classes'],
    '#description' => $this->t('Zusätzliche Wrapper-Klassen. Geben Sie so viele ein, wie Sie möchten, aber denken Sie daran, sie durch ein einzelnes Leerzeichen zu trennen.'),
  ];

  foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
    $form['region_classes'][$region_name] = [
      '#type' => 'textfield',
      '#title' => $this->t('Zusätzliche Klassen für Region @region_name', [
        '@region_name' => $region_name,
      ]),
      '#default_value' => $this->configuration['region_classes'][$region_name],
      '#description' => $this->t('Zusätzliche Klassen für den Wrapper der Region @region_name. Geben Sie so viele ein, wie Sie möchten, aber denken Sie daran, sie durch ein einzelnes Leerzeichen zu trennen.', [
        '@region_name' => $region_name,
      ]),
    ];
  }

  return parent::buildConfigurationForm($form, $form_state);
}

2. Wir verwenden eine ähnliche Schleife, um den Wert zu schreiben:

/**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  parent::submitConfigurationForm($form, $form_state);

  $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes');

  foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
    $this->configuration['region_classes'][$region_name] = $form_state->getValue(['region_classes', 
$region_name], '');
      }
}

3. Der letzte Schritt besteht darin, die Methode build zu überschreiben und unsere Klassen in das entsprechende Attributes-Klassenobjekt einzubetten.

/**
 * {@inheritdoc}
 */
public function build(array $regions): array {
  $build = parent::build($regions);
  $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']);
  $build['#attributes']['class'] = [...$wrapper_classes];

  foreach (array_keys($regions) as $region_name) {
    $region_classes = explode(' ', (string) $this->configuration['region_classes'][$region_name]);
    $build[$region_name]['#attributes']['class'] = [...$region_classes];
  }

  return $build;
}

Nach unseren neuesten Änderungen sollten wir ein neues Feld Zusätzliche Klassen für die erste Region sehen, in dem wir eine Liste von Klassen angeben können, die wir verwenden möchten.

Ein Abschnitt-Bearbeitungsformular in Drupal, in dem wir Klassen und unser eigenes Administrationslabel hinzufügen können

Beachten Sie, dass die Region nur erscheint, wenn sie nicht leer ist. Deshalb haben wir einen Testblock hinzugefügt, der den Titel des Knotens enthält. Lassen Sie uns sehen, ob die Klassen im HTML sichtbar sind.

HTML-Code-Schnipsel mit den Abschnitt-Tags und Attributen für das Layout Plugin

Erstellen verschiedener Varianten von Abschnitten

Der Code wurde so generisch geschrieben, dass das Hinzufügen eines Abschnitts mit einer unterschiedlichen Anzahl von Regionen nur die Definition der Region und Vorlage von uns erfordert. Lassen Sie uns dann einen neuen Abschnitt mit zwei Regionen hinzufügen.

Zuerst fügen wir die Definition hinzu:

# Dateiname: custom_layout_builder.layouts.yml

layout_custom_one_column:
  label: '[CUSTOM] Eine Spalte'
  category: 'Benutzerdefinierte Layouts'
  path: layouts/custom_onecol_section
  template: layout--custom-onecol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase'
  default_region: first
  icon_map:
    - [first]
  regions:
    first:
      label: Erste
layout_custom_two_columns:
  label: '[CUSTOM] Zwei Spalten'
  category: 'Benutzerdefinierte Layouts'
  path: layouts/custom_twocol_section
  template: layout--custom-twocol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase'
  default_region: first
  icon_map:
    - [first, second]
  regions:
    first:
      label: Erste
    second:
      label: Zweite

Und dann bereiten wir die Vorlage vor:

# Dateiname: layout--custom-twocol-section.html.twig

{#
/**
 * @file
 * Standard-Implementierung für einen benutzerdefinierten Layout-Onecol-Abschnitt.
 *
 * Verfügbare Variablen:
 * - content: Der Inhalt für dieses Layout.
 * - attributes: HTML-Attribute für das Layout <div>.
 * - region_attributes: HTML-Attribute für die Region <div>.
 * - settings: Ein Array von konfigurierten Einstellungen für das Layout.
 *
 * @ingroup themeable
 */
#}
{% if content %}
  <div{{ attributes }}>

    {% if content.first %}
      <div {{ region_attributes.first }}>
        {{ content.first }}
      </div>
    {% endif %}

    {% if content.second %}
      <div {{ region_attributes.second }}>
        {{ content.second }}
      </div>
    {% endif %}

  </div>
{% endif %}

Der Abschnitt sollte jetzt verfügbar sein.

Eine Liste der verfügbaren Abschnitte für ein benutzerdefiniertes Layout im Layout Builder

 

Das Konfigurationsformular sollte sich automatisch an die Anzahl der Regionen anpassen.

Ansicht des Abschnitt-Konfigurationsformulars mit den Feldern für Klassen und Administrationslabel

Nach der Konfiguration des Formulars und dem Hinzufügen von Testdaten können wir das Ergebnis unserer Operation im HTML sehen.

Der HTML-Schnipsel, der die Ergebnisse von Operationen im Zusammenhang mit dem Abschnitt enthält

 

Das in diesem Tutorial erstellte Modul ist auf unserem GitHub-Konto verfügbar.

Layout Builder-Anpassung - Zusammenfassung

Layout Builder ist ein großartiges Tool, dessen API völlige Freiheit erlaubt. Wie immer bei Drupal, wenn Sie es träumen können, können Sie es bauen. Das in diesem Artikel gezeigte Beispiel ist nur ein kleiner Teil dessen, was erreichbar ist. Falls Sie an der breiteren Nutzung der Layout Builder API interessiert sind, lohnt es sich, über das Bootstrap Layout Builder-Modul zu lesen.

Brauchen Sie benutzerdefinierte Einstellungen in Ihrem System? Schauen Sie nach, wie wir Ihnen im Rahmen unserer Dienstleistungen im Bereich der Drupal-Entwicklung helfen können.

As part of Drupal support, we maintain existing websites and expand them with new functionalities