logo

Extra Block Types (EBT) - Neue Erfahrung im Layout Builder❗

Extra Block Types (EBT) - gestylte, anpassbare Blocktypen: Diashows, Registerkarten, Karten, Akkordeons und viele andere. Eingebaute Einstellungen für Hintergrund, DOM Box, Javascript Plugins. Erleben Sie die Zukunft der Layouterstellung schon heute.

Demo EBT-Module EBT-Module herunterladen

❗Extra Absatztypen (EPT) - Erfahrung mit neuen Absätzen

Extra Paragraph Types (EPT) - analoger, auf Absätzen basierender Satz von Modulen.

Demo EPT-Module EPT-Module herunterladen

Scroll

Definition und Verwendung von Content Entity Field Definitionen

18/06/2025, by Ivan

Content Entities müssen alle ihre Felder explizit definieren, indem sie Definitionen für die Entity-Klasse bereitstellen. Die Felddefinitionen basieren auf der Typed Data API (siehe Wie Entities diese implementieren).

Felddefinitionen

Entity-Typen definieren ihre Basisfelder in einer statischen Methode der Entity-Klasse. Basisfelder sind keine konfigurierbaren Felder, sondern Felder, die in diesem Entity-Typ immer existieren, z. B. der Knotentitel oder Erstellungs- und Änderungsdaten. Der Entity Manager ergänzt diese Felder um konfigurierbare und nicht konfigurierbare Felder, die von anderen Modulen bereitgestellt werden, indem er hook_entity_field_info() und hook_entity_field_info_alter() aufruft. So werden auch Felder hinzugefügt, die über das Field UI konfiguriert wurden (diese Hooks existieren laut API nicht mehr).

Felddefinitionen sind einfache Objekte, die das FieldDefinitionInterface implementieren, während Basisfelder meist mit der Klasse BaseFieldDefinition erstellt werden. Konfigurierbare Felder implementieren das Interface direkt mit den entsprechenden Konfigurationsobjekten (so genannten Field und FieldInstance).
Felddefinitionen sind außerdem der Ort, an dem Validierungsbeschränkungen für Feldelemente oder Feld-Propertys definiert werden können. Es können alle Feldtyp-Plugin-Implementierungen genutzt werden (dieses Interface und die Klasse existieren nicht mehr).

Felder sind derzeit immer Listen von Feldelementen, das bedeutet, dass die als Typ definierte Klasse FieldItem in der Klasse FieldItemList eingeschlossen wird, die eine Liste dieser Feldelemente darstellt.

Alle Felder (einschließlich Basisfelder) können zudem Widgets und Formatter zur Anzeige und Bearbeitung besitzen.

Basisfelder

Nachfolgend ein verkürztes Beispiel für die Felddefinitionen des Entity-Typs Node.

use Drupal\Core\Field\BaseFieldDefinition;

class Node implements NodeInterface {

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions($entity_type) {
    // Die Knotennummer ist ein Integer, verwendet die IntegerItem-Klasse.
    $fields['nid'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Node ID'))
      ->setDescription(t('Die Knotennummer.'))
      ->setReadOnly(TRUE);

    // Das UUID-Feld verwendet den Typ uuid_field, der automatisch eine neue UUID generiert.
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('Die UUID des Knotens.'))
      ->setReadOnly(TRUE);

    // Der Sprachcode ist als language_field definiert, der einen gültigen Standard-Sprachcode für neue Entities setzt.
    $fields['langcode'] = BaseFieldDefinition::create('language')
      ->setLabel(t('Sprachcode'))
      ->setDescription(t('Der Sprachcode des Knotens.'));

    // Der Titel ist StringItem, der Standardwert ist ein leerer String, mit einer Property-Constraint, dass der Wert max. 255 Zeichen lang sein darf.
    $fields['title'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Titel'))
      ->setDescription(t('Der Titel dieses Knotens, immer als reiner Text behandelt.'))
      ->setRequired(TRUE)
      ->setTranslatable(TRUE)
      ->setSettings(array(
        'default_value' => '',
        'max_length' => 255,
      ));

    // Die uid ist eine Entity Reference auf den Benutzertyp, so kann auf die Benutzer-ID mit $node->uid->target_id
    // und auf das Benutzer-Entity mit $node->uid->entity zugegriffen werden. NodeInterface definiert auch getAuthor() und getAuthorId(). (@todo: owner vs. revisionAuthor prüfen)
    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Benutzer-ID'))
      ->setDescription(t('Die Benutzer-ID des Knotenerstellers.'))
      ->setSettings(array(
        'target_type' => 'user',
        'default_value' => 0,
      ));

    // Der Typ changed aktualisiert automatisch den Zeitstempel bei jeder Speicherung.
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Geändert'))
      ->setDescription(t('Zeitpunkt der letzten Bearbeitung des Knotens.'));
    return $fields;
  }
}

Mehrwertige Felder

Um die maximale Anzahl von Elementen für ein Feld anzugeben, rufen Sie die Methode setCardinality() auf.
Beispiel für ein Feld mit maximal drei Elementen:

->setCardinality(3);

Für ein Feld mit unbegrenzten Werten rufen Sie auf:

->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);

Beispiel für ein mehrwertiges Feld, das eine Referenz auf „User“-Entities enthält:

$fields['my_field'] = BaseFieldDefinition::create('entity_reference')
  ->setLabel(t('Bezeichnung des Feldes'))
  ->setDescription(t('Beschreibung des Feldes.'))
  ->setRevisionable(TRUE)
  ->setSetting('target_type', 'user')
  ->setSetting('handler', 'default')
  ->setTranslatable(TRUE)
  ->setDisplayOptions('view', [
    'label' => 'hidden',
    'type' => 'author',
    'weight' => 0,
  ])
  ->setDisplayOptions('form', [
    'type' => 'entity_reference_autocomplete',
    'weight' => 5,
    'settings' => [
      'match_operator' => 'CONTAINS',
      'size' => '60',
      'autocomplete_type' => 'tags',
      'placeholder' => '',
    ],
  ])
  ->setDisplayConfigurable('form', TRUE)
  ->setDisplayConfigurable('view', TRUE)
  ->setRequired(TRUE)
  ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);

Eine Entity kann auch Felder bereitstellen, die nur für ein bestimmtes Bundle existieren oder von einem Bundle verändert werden. Zum Beispiel kann der Knotentitel für jedes Bundle ein anderes Label haben. Um eine Änderung im Bundle zu gewährleisten, muss die Basisfelddefinition geklont werden, bevor sie geändert wird, da sonst die Basisfelddefinition geändert wird und alle Bundles beeinflusst.

use Drupal\node\Entity\NodeType;

  /**
   * {@inheritdoc}
   */
  public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
    $node_type = NodeType::load($bundle);
    $fields = array();
    if (isset($node_type->title_label)) {
      $fields['title'] = clone $base_field_definitions['title'];
      $fields['title']->setLabel($node_type->title_label);
    }
    return $fields;
  }

Feldtypen

Drupal Core bietet eine Liste von Feldtypen, die für Basisfelder verwendet werden können. Module können zusätzliche Feldtypen bereitstellen, die ebenfalls genutzt werden können.

  • string: einfacher String
  • boolean: logischer Wert, gespeichert als Integer
  • integer: Ganzzahl mit Einstellungen für Min- und Max-Werte (ähnlich für decimal und float)
  • decimal: Dezimalzahl mit konfigurierbarer Präzision und Skalierung
  • float: Gleitkommazahl
  • language: enthält Sprachcode und Sprache als berechnete Property
  • timestamp: Unix-Zeitstempel als Integer gespeichert
  • created: Zeitstempel mit aktuellem Zeitpunkt als Standardwert
  • changed: Zeitstempel, der bei Speicherung automatisch aktualisiert wird
  • datetime: Datum als ISO 8601 String
  • uri: enthält URI. Das Link-Modul stellt zudem einen Link-Feldtyp bereit, der Titel und interne/externe URIs oder Routen unterstützt.
  • uuid: UUID-Feld, das standardmäßig eine neue UUID generiert
  • email: E-Mail mit Validierung, Widgets und Formatierern
  • entity_reference: Referenz auf ein Entity über target_id und eine berechnete Entity-Property. Das entity_reference-Modul stellt Widgets und Formatter bereit, wenn aktiviert.
  • map: kann beliebig viele benutzerdefinierte Properties als serialisierten String speichern

Konfigurierbare Felder

Zusätzliche Felder können in hook_entity_base_field_info() und hook_entity_bundle_field_info() registriert werden. Die folgenden Beispiele fügen Basis- und Bundle-Felder hinzu.

use Drupal\Core\Field\BaseFieldDefinition;

/**
 * Implements hook_entity_base_field_info().
 */
function path_entity_base_field_info(EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'taxonomy_term' || $entity_type->id() === 'node') {
    $fields['path'] = BaseFieldDefinition::create('path')
      ->setLabel(t('Der Pfad-Alias'))
      ->setComputed(TRUE);

    return $fields;
  }
}

/**
 * Implements hook_entity_bundle_field_info().
 */
function field_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  if ($entity_type->isFieldable()) {
    // Konfigurierbare Felder, die immer an ein bestimmtes Bundle gebunden sind,
    // werden „nach Bundle“ hinzugefügt.
    return Field::fieldInfo()->getBundleInstances($entity_type->id(), $bundle);
  }
}

Entsprechende alter-Hooks existieren ebenfalls für diese.

Feldspeicherung

Wenn Ihr Feld keine speziellen Anforderungen hat, kümmert sich die Entity Field API um die Datenbankspeicherung und passt die Datenbankschemata entsprechend an. Dies ist der Standard für Felder, die nicht als berechnete Felder markiert sind (setComputed(TRUE)) oder die explizit ihre eigene Feldspeicherung angeben (setCustomStorage(TRUE)).

Angenommen, Sie wollen allen Node-Entities ein neues Basisfeld hinzufügen, das ein einfaches Boolean enthält, um zu kennzeichnen, ob der Inhalt hervorgehoben ist.

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

/**
 * Implements hook_entity_base_field_info().
 */
function MYMODULE_entity_base_field_info(EntityTypeInterface $entity_type) {
  $fields = array();

  // Füge ein "Highlight"-Basisfeld zu allen Knotentypen hinzu.
  if ($entity_type->id() === 'node') {
    $fields['highlight'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Highlight'))
      ->setDescription(t('Ob der Knoten hervorgehoben ist.'))
      ->setRevisionable(TRUE)
      ->setTranslatable(TRUE)
      ->setDisplayOptions('form', array(
        'type' => 'boolean_checkbox',
        'settings' => array(
          'display_label' => TRUE,
        ),
      ))
      ->setDisplayConfigurable('form', TRUE);
  }

  return $fields;
}

Ich habe das schon oft probiert, und der Besuch von update.php fügt die Spalte nicht zur Datenbank hinzu, aber das Ausführen von

  \Drupal::entityTypeManager()->clearCachedDefinitions();
  \Drupal::service('entity.definition_update_manager')->applyUpdates();

kann die Spalte in der Datenbank erstellen. Hinweis: Dadurch werden auch alle anderen anstehenden Updates für Felddefinitionen ausgeführt.

Update: Der obige Code funktioniert nicht mehr mit Drupal 8.7

Siehe ein Beispiel in diesem Changelog-Eintrag

Installation einer neuen Feldspeicherdefinition

function example_update_8701() {
  $field_storage_definition = BaseFieldDefinition::create('boolean')
    ->setLabel(t('Revision translation affected'))
    ->setDescription(t('Zeigt an, ob die letzte Bearbeitung einer Übersetzung zur aktuellen Revision gehört.'))
    ->setReadOnly(TRUE)
    ->setRevisionable(TRUE)
    ->setTranslatable(TRUE);

  \Drupal::entityDefinitionUpdateManager()
    ->installFieldStorageDefinition('revision_translation_affected', 'block_content', 'block_content', $field_storage_definition);
}

Wenn Ihr Custom-Modul ein neues Feld hinzufügt, wird es beim Aktivieren automatisch hinzugefügt und beim Deinstallieren entfernt.

Wenn Ihr Modul bereits installiert ist und Sie hook_update_N schreiben müssen, um Felddefinitionen zu aktualisieren, können Sie so vorgehen:

/**
 * Füge das Highlight-Feld zu allen Nodes hinzu.
 */
function MYMODULE_update_8001() {
  $entity_type = \Drupal::service('entity_type.manager')->getDefinition('node');
  \Drupal::service('entity.definition_update_manager')->updateEntityType($entity_type);
}

oder

/**
 * Füge das Feld 'revision_translation_affected' zu 'node'-Entities hinzu.
 */
function node_update_8001() {
  // Installiert die Definition, wie sie in
  // \Drupal\node\Entity\Node::baseFieldDefinitions()
  // zum Zeitpunkt der Erstellung dieser Update-Funktion definiert war.
  // Wenn die Definition später geändert wird, muss das entsprechende Modul
  // eine Update-Funktion schreiben, die
  // \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition()
  // mit der neuen Definition aufruft.
  $storage_definition = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Revision translation affected'))
      ->setDescription(t('Zeigt an, ob die letzte Bearbeitung einer Übersetzung zur aktuellen Revision gehört.'))
      ->setReadOnly(TRUE)
      ->setRevisionable(TRUE)
      ->setTranslatable(TRUE);

  \Drupal::entityDefinitionUpdateManager()
    ->installFieldStorageDefinition('revision_translation_affected', 'node', 'node', $storage_definition);
}

Siehe https://www.drupal.org/node/2554097 für weitere Informationen und Beispiele.

Arbeiten mit Felddefinitionen

Hinweis: Da Objekte komplexe Daten sind, müssen sie dem ComplexDataInterface folgen. Aus Sicht der Typed Data API sind alle darin enthaltenen typisierten Datenelemente Properties. Diese Namensbeschränkung/-zuweisung könnte gelockert werden.

// Prüft, ob eine Entity ein bestimmtes Feld hat.
$entity->hasField('field_tags');

// Gibt ein Array mit den benannten Keys aller Felder und deren Definitionen zurück, z. B. das Feld „image“.
$field_definitions = $entity->getFieldDefinitions();

// Gibt ein Array mit benannten Keys aller Feldeigenschaften und deren Definitionen des Feldes „image“ zurück, z. B. „file_id“ und „alt“.
$property_definitions = $entity->image->getFieldDefinition()->getPropertyDefinitions();

// Gibt nur die Definition der Property „alt“ zurück.
$alt_definition = $entity->image->getFieldDefinition()->getPropertyDefinition('alt');

// Felddefinitionen können auch vom Entity Manager angefragt werden,
// die folgende Methode gibt alle Felder zurück, die für alle Bundles verfügbar sind.
\Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type);

// Die folgende Methode gibt Felder zurück, die für ein bestimmtes Bundle verfügbar sind.
\Drupal::service('entity_field.manager')->getFieldDefinitions($entity_type, $bundle);

Widgets und Formatter für Basisfelder

Basisfelder können Widgets und Formatter angeben, die sie verwenden sollen, genau wie konfigurierbare Felder. Widget, Formatter und nötige Parameter werden in der FieldDefinition-Klasse wie folgt gesetzt:

use Drupal\Core\Field\BaseFieldDefinition;

// ...

    $fields['title'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Titel'))
      ->setDescription(t('Der Titel dieses Knotens, immer als reiner Text behandelt.'))
      ->setRequired(TRUE)
      ->setTranslatable(TRUE)
      ->setSettings(array(
        'default_value' => '',
        'max_length' => 255,
      ))
      ->setDisplayOptions('view', array(
        'label' => 'hidden',
        'type' => 'string',
        'weight' => -5,
      ))
      ->setDisplayOptions('form', array(
        'type' => 'string',
        'weight' => -5,
      ))
      ->setDisplayConfigurable('form', TRUE);

Hierbei wird der Formatter „string“ und das Widget gesetzt sowie das Gewicht für den Knotentitel konfiguriert. setDisplayConfigurable() macht das Feld im UI für die Verwaltung der Anzeige-/Formularanzeige sichtbar, sodass Reihenfolge und Label-Anzeige verändert werden können. Aktuell erlaubt der Core nicht, Widgets oder deren Einstellungen im UI zu ändern.

Um ein Feld standardmäßig ausgeblendet zu definieren, kann im Array, das an setDisplayOptions() übergeben wird, der Region-Key auf „hidden“ gesetzt werden.

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.