Feldtypen, Feld-Widgets und Feld-Formatter
Überblick
Drupal 8 wird mit einer großen Bibliothek von Basisklassen geliefert, die es Ihnen ermöglichen, mit Ihren eigenen Inhalten zu arbeiten. Wenn es um Inhaltseinheiten geht, wollen Sie Felder verwenden. Es ist wichtig, Felder zu verstehen, da Ihre Entitäten dort ihre Daten speichern.
FieldTypes (Feldtypen)
Grundlegende Feldtypen:
- boolean
- changed
- comment
- created
- datetime
- daterange
- decimal
- entity_reference
- file
- float
- image
- integer
- language
- link
- list_float
- list_integer
- list_string
- map
- password
- path
- string
- string_long
- telephone
- text
- text_long
- text_with_summary
- timestamp
- uri
- uuid
Benutzerdefinierte Feldtypen
Wann immer Sie Daten auf eine Weise darstellen wollen, die Drupal nicht anbietet, möchten Sie vielleicht einen neuen Feldtyp für Ihre Daten erstellen.
Angenommen, Sie haben ein Inhaltselement, das vertrauliche Daten enthält. Der Ersteller dieses Inhalts möchte bestimmten Benutzern Zugriff auf das Element mittels eines Passwortes geben, das für jeden Benutzer unterschiedlich ist. Wenn wir in Datenbanktabellen sprechen, wollen Sie etwas wie das hier erstellen:
| entity_id | uid | password | ----------------------------------- | 1 | 1 | 'helloworld' | | 1 | 2 | 'goodbye' |
Wie Sie sehen, hat unsere Entität mit der ID 1 zwei verschiedene Passwörter für zwei verschiedene Benutzer. Wie können wir das in Drupal umsetzen, ohne manuell eine Tabelle zu erstellen? Wir erstellen einen neuen Feldtyp.
Da Drupal die Feldlogik als Plugin implementiert, gibt es immer eine Basisklasse, von der Sie erben können, damit es in Drupal funktioniert. Für einen neuen Feldtyp wollen Sie die folgende Ordnerstruktur in Ihrem Modul erstellen:
modules/custom/MODULENAME/src/Plugin/Field/FieldType
Das ist ein ziemlich langer Pfad und etwas umständlich, aber Drupal erleichtert die Arbeit mit all den verschiedenen Funktionen, die in Ihren Modulen koexistieren können.
Für unser Beispiel erstellen wir die Datei EntityUserAccessField.php
namespace Drupal\MODULENAME\Plugin\Field\FieldType; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\TypedData\DataDefinition; /** * @FieldType( * id = "entity_user_access", * label = @Translation("Entity User Access"), * description = @Translation("This field stores a reference to a user and a password for this user on the entity."), * ) */ class EntityUserAccessField extends FieldItemBase { /** * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { //ToDo: Implement this. } /** * {@inheritdoc} */ public static function schema(FieldStorageDefinitionInterface $field_definition) { //ToDo: Implement this. } }
Wie Sie sehen, ähnelt der Feldtyp stark einer Inhaltseinheit. Tatsächlich gibt es keinen großen Unterschied zwischen beiden, aber das ist ein Thema für einen anderen Beitrag ;)
Zuerst haben wir eine Annotation für unseren Feldtyp:
- @FieldType: ruft die FieldType-Annotation-Klasse aus der Drupal-Bibliothek auf
- id: ist der Maschinenname unseres Feldtyps, damit wir ihn wiederverwenden können. Achten Sie darauf, keine vordefinierten Namen aus PHP etc. zu überschreiben.
- label: ist die für Benutzer lesbare Übersetzung des Maschinennamens.
- description: wenn das Label nicht ausreicht, können Sie auch eine Beschreibung für den Feldtyp hinzufügen.
Zweitens erweitert unsere Klasse FieldItemBase, was uns zwingt, zwei Methoden zu implementieren, damit wir diesen Feldtyp korrekt verwenden können:
- propertyDefinitions(): Diese Methode ähnelt der baseFieldDefinition eines Inhaltselements (ist aber nicht dasselbe!). Wir können die Daten definieren, die in Entitätsformularen erscheinen, in denen dieser Feldtyp verwendet wird.
- schema(): Bei Entitäten ist diese Methode veraltet, aber wir haben sie noch bei Feldern. Diese Methode definiert, wie die Felddaten in der Datenbank gespeichert werden. Das kann von den Eigenschaften abweichen.
Da nicht ganz klar ist, was in diese Methoden gehört, fügen wir ihnen Beispielcode hinzu.
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['uid'] = DataDefinition::create('integer') ->setLabel(t('User ID Reference')) ->setDescription(t('The ID of the referenced user.')) ->setSetting('unsigned', TRUE); $properties['password'] = DataDefinition::create('string') ->setLabel(t('Password')) ->setDescription(t('A password saved in plain text. That is not safe dude!')); $properties['created'] = DataDefinition::create('timestamp') ->setLabel(t('Created Time')) ->setDescription(t('The time that the entry was created')); // ToDo: Add more Properties. return $properties; }
Es ist auch möglich, die Benutzer-ID mit DataReferenceDefinition zu speichern, das könnte hier in Zukunft betrachtet werden.
public static function schema(FieldStorageDefinitionInterface $field_definition) { $columns = array( 'uid' => array( 'description' => 'The ID of the referenced user.', 'type' => 'int', 'unsigned' => TRUE, ), 'password' => array( 'description' => 'A plain text password.', 'type' => 'varchar', 'length' => 255, ), 'created' => array( 'description' => 'A timestamp of when this entry has been created.', 'type' => 'int', ), // ToDo: Add more columns. ); $schema = array( 'columns' => $columns, 'indexes' => array(), 'foreign keys' => array(), ); return $schema; }
schema() ist notwendig, damit Drupal weiß, wie es unsere Daten speichert. Die Schema-Spalten müssen eine Teilmenge der in propertyDefinitions() definierten Eigenschaften sein.
Jetzt haben wir einen völlig neuen Feldtyp. Er enthält keine Logik, wie Eingaben verarbeitet werden, aber er kann in jedem Inhaltselement als Feld verwendet werden. Falls gewünscht, kann er auch als baseField für eine Entität genutzt werden:
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { // Einige Felder oben. $fields['entity_user_access'] = BaseFieldDefinition::create('entity_user_access') ->setLabel(t('Entity User Access')) ->setDescription(t('Specify passwords for any user that want to see this entity.')) ->setCardinality(-1); // Erlaubt mehr als einen Eintrag // Noch mehr Felder unten. return $fields; }
- BaseFieldDefinition::create(): Verwenden Sie den Maschinenname des Feldtyps in create()
- setCardinality(-1): Die Kardinalität bestimmt, wie viele Feldwerte eine Entität besitzen kann. Beispielsweise bedeutet 2, dass nur zwei Benutzer Zugriff auf das Objekt haben. -1 bedeutet unendlich viele Benutzer.
FieldWidget (Feld-Widget)
Wenn Sie benutzerdefinierte Daten haben, benötigen Sie möglicherweise eine benutzerdefinierte Darstellung dieser Daten. Widgets werden verwendet, um darzustellen, wie ein Benutzer diese Daten in Formulare eingeben kann. Zum Beispiel:
- Wenn ein ganzzahliger Wert eingegeben werden soll, der Benutzer aber nur ein Kontrollkästchen anklicken kann
- Wenn Sie Autovervollständigung für Ihre Daten möchten
- Wenn die Passworteingabe über eine spezielle grafische Oberfläche erfolgt
usw.
Das Feld-Widget in Drupal finden Sie unter
modules/custom/MODULENAME/src/Plugin/Field/FieldWidget
auch ein sehr langer Pfad. Nun sollte klar sein, warum Drupal diesen Stil verwendet, um PHP-Dateien zu trennen – so verliert man nicht den Überblick, welche Datei wo hingehört.
Wir erstellen das Feld-Widget in EntityUserAccessWidget.php
namespace Drupal\MODULENAME\Plugin\Field\FieldWidget; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; /** * Plugin-Implementierung des Widgets 'entity_user_access_w'. * * @FieldWidget( * id = "entity_user_access_w", * label = @Translation("Entity User Access - Widget"), * description = @Translation("Entity User Access - Widget"), * field_types = { * "entity_user_access", * }, * multiple_values = TRUE, * ) */ class EntityUserAccessWidget extends WidgetBase { /** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { // ToDo: Implementieren. } }
Ist Ihnen schon aufgefallen? Drupal 8 verwendet diesen Code-Stil immer wieder, wenn Sie Funktionen implementieren wollen. Es gibt eine Annotation und eine Basisklasse, von der Sie erben. Ja, Drupal benutzt das!
- @FieldWidget: definiert die Annotation-Klasse
- id: Maschinenname des Widgets
- field_types: Array von Maschinen-Namen der Feldtypen, die dieses Widget verwenden können
- multiple_values: Standardmäßig FALSE. Wenn TRUE, können Sie in einem Entitätsformular mehr als einen Wert eingeben.
Wenn Sie dieses Widget nun mit Ihrem Feldtyp verwenden wollen, müssen Sie die Feldtyp-Annotation wie folgt anpassen:
/** * @FieldType( * id = "entity_user_access", * label = @Translation("Entity User Access"), * description = @Translation("This field stores a reference to a user and a password for this user on the entity."), * default_widget = "entity_user_access_w", * ) */
Fertig! Nein, warten Sie, noch passiert nichts, denn wir müssen formElement() in unserem Widget implementieren.
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element['userlist'] = array( '#type' => 'select', '#title' => t('User'), '#description' => t('Select group members from the list.'), '#options' => array( 0 => t('Anonymous'), 1 => t('Admin'), 2 => t('foobar'), // Das sollte besser implementiert werden! ), ); $element['passwordlist'] = array( '#type' => 'password', '#title' => t('Password'), '#description' => t('Select a password for the user'), ); // Standardwerte für alle oben genannten Felder setzen $childs = Element::children($element); foreach ($childs as $child) { $element[$child]['#default_value'] = isset($items[$delta]->{$child}) ? $items[$delta]->{$child} : NULL; } return $element; }
Wenn Sie das Formular mit diesem Widget jetzt öffnen, sehen Sie mindestens zwei Eingabefelder. Eins ist eine Benutzer-Auswahl, das andere ein Passwortfeld. Wenn Sie eine Daten-Speicherung implementieren wollen, müssen Sie Validierungsmethoden in diesem Widget oder im Entitätsformular implementieren. Siehe Drupal 8 Form API für weitere Informationen.
Bis hierhin haben Sie den Großteil der Arbeit an einem benutzerdefinierten Feld erledigt. Wenn Sie nicht verstehen, was passiert, probieren Sie einfach den Code aus oder sehen Sie sich Kernmodule an, um tiefer in das Thema einzutauchen.
FieldFormatters (Feld-Formatter)
Was noch fehlt, ist die Darstellung der Daten im sogenannten View-Modus der Entität – übrigens ist ein Widget der Formularmodus. Das passiert meist, wenn Sie die Entität über yourdrupalpage.com/myentity/1/view aufrufen.
Da es hier wenig zu erklären gibt, kommen wir direkt zum Code. Erstellen Sie unter modules/custom/MODULENAME/src/Plugin/Field/FieldFormatter die Datei EntityUserAccessFormatter.php
namespace Drupal\MODULENAME\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FormatterBase; /** * Plugin-Implementierung des Formatters 'entity_user_access_f'. * * @FieldFormatter( * id = "entity_user_access_f", * label = @Translation("Entity User Access - Formatter"), * description = @Translation("Entity User Access - Formatter"), * field_types = { * "entity_user_access", * } * ) */ class EntityUserAccessFormatter extends FormatterBase { /** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { $elements[$delta] = array( 'uid' => array( '#markup' => \Drupal\user\Entity\User::load($item->uid)->getUsername(), ), // Weitere Inhalte hinzufügen ); } return $elements; } }
Dieses Beispiel hat eine sehr ähnliche Annotation wie das Widget, daher müssen wir nicht viel dazu sagen. viewElements() zeigt einfach den Benutzernamen des gespeicherten Benutzer-IDs unseres Feldtyps an. Die Zugriffskontrolle sollte in der Entität selbst implementiert werden. So zeigt diese Implementierung alle Benutzernamen, die ein Passwort für die Entität haben.
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.